Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit Mode: "OneByFaces" #695

Closed
Anyeos opened this issue Sep 3, 2024 · 12 comments
Closed

Emit Mode: "OneByFaces" #695

Anyeos opened this issue Sep 3, 2024 · 12 comments

Comments

@Anyeos
Copy link

Anyeos commented Sep 3, 2024

Is your feature request related to a problem? Please describe.
To have more control of the instantiated items. Actually I only have Faces and NoiseGraph to have better control but it is not enough, for example I can't set the position of the items for myself.

Describe the solution you'd like
The idea is to generate only one per face. But, the condition is that it is always centered. That is, that its position corresponds to the center of the face where it is generated (the Normal of the face).
Then an additional requirement will be that the NoiseGraph has one more output: UV (or XY) which would be an offset that would be added to the position of the item. That offset can be added based on the normal of the face, therefore the displacement of the item would be based on the face and not based on global_position (that's why I think it's a good idea to name it UV instead of XY).

Describe alternatives you've considered
Using Faces and NoiseGraph but limited to what Faces can only allows me to do.

Additional context
With that, I can mitigate the problem of overlap allowing me to "program" the location (using NoiseGraph).

And I think it wouldn't be difficult to implement because this would be using almost everything as is.

I'm interested in having this implemented because I think it will not only be useful to me but will allow video game developers to define their own position for instantiating the elements on the ground.

The rest of the values ​​such as scale, rotation, maximum and minimum height, etc. continue to be used as is. The only thing I would change is that instead of dispersing points, It would only generate one in the center of the face and have to add an offset.

The rest remains unchanged, only a new Emit Mode and the offset input/output would be added. If it is not used, it remains at 0 and therefore does not apply any offset. So it does not even break compatibility or cause problems with anything that exists.

The only thing I need is for it to generate one centered per face and allow me to apply an offset to it from the NoiseGraph. The rest of the characteristics and how it does it or otherwise I leave to your discretion, I only need those two things.

I would really appreciate it if you could implement this if it is not too complicated for you.

Note: I will try to implement it myself.
Note2: I already implemented it.

@Anyeos
Copy link
Author

Anyeos commented Sep 3, 2024

Here are something that I don't understand: If I only generates one by triangle, why there are more than one by triangle?
image

I am doing it whit this code:

for (int triangle_index = 0; triangle_index < triangle_count; ++triangle_index) {
	const uint32_t ii = triangle_index * 3;

	const int ia = indices[ii];
	const int ib = indices[ii + 1];
	const int ic = indices[ii + 2];

	const Vector3 &pa = vertices[ia];
	const Vector3 &pb = vertices[ib];
	const Vector3 &pc = vertices[ic];

	const Vector3 &na = normals[ia];
	const Vector3 &nb = normals[ib];
	const Vector3 &nc = normals[ic];

	const Vector3 triangle_center = math::get_triangle_center(pa, pb, pc);
	const Vector3 triangle_center_normal = math::get_triangle_center(na, nb, nc);
	vertex_cache.push_back(to_vec3f(triangle_center));
	normal_cache.push_back(to_vec3f(triangle_center_normal));
}

get_triangle_center is:

inline Vector3 get_triangle_center(Vector3 p0, Vector3 p1, Vector3 p2) {
	return Vector3((p0.x+p1.x+p2.x) / 3.0, (p0.y+p1.y+p2.y) / 3.0, (p0.z+p1.z+p2.z) / 3.0);
}

Why there are more items than triangles?
I will continue reviewing all the code but... may it be a bug? Because when I choose Vertices there are a lot of items generated from one vertices but not from others:
image

@Anyeos
Copy link
Author

Anyeos commented Sep 3, 2024

I discovered it is because transitions enabled. It generates some near zero size triangles. I need to detect that from generator and skip that kind of triangles.

Look at this now:
image

@Anyeos
Copy link
Author

Anyeos commented Sep 4, 2024

I did it:
image

image

How can I share the code? A fork?

It took me a lot of time because the outputs are not obvious. For some reason the only combination of outputs that worked is the showed on the screenshots: OutputWeight for influence, OutputType for X, and OutputSingleTexture for Z. I tried with CustomOutput but it never worked.

Edited: The outputs swapped when I loaded again the project. But it is a mean of fail and try. I successful achieved it to stay working. I still don't understand how the outputs array have the relationship with the noise graph in the code.

It too supports one output and becomes the actual behaviour. And you can use too the offset mode (3 outputs) in any EmitMode, it apply the offset too.

@Zylann
Copy link
Owner

Zylann commented Sep 8, 2024

The idea is to generate only one per face. But, the condition is that it is always centered. That is, that its position corresponds to the center of the face where it is generated (the Normal of the face).

Sounds easy enough to add.

Then an additional requirement will be that the NoiseGraph has one more output: UV (or XY) which would be an offset that would be added to the position of the item. That offset can be added based on the normal of the face, therefore the displacement of the item would be based on the face and not based on global_position (that's why I think it's a good idea to name it UV instead of XY).

I'm a bit reserved about the way to do this.

First, the normal of the face is not enough to determine two tangent axes. So I assume you mean X and Y of the spawned item after its orientation is randomly chosen?

Then about doing X and Y with graphs... I think it's too much.
The graph system doesn't have vectors, and no dedicated output type that is meant to represent X and Y coordinates. The issues you got trying to re-use existing output nodes might have caused you issues because they are treated a little bit differently due to their purpose. Doing that is not obvious. Custom outputs could be used, and would have to be named "X" and "Y" to be recognized somewhat by the generator. Though adding a dedicated output type might be more user friendly (note: there is already a graph property to control density, and it uses the SDF output I think).
But also, it sounds like you aren't actually going to use X and Y to output anything more than noise to randomly displace items a bit... using a graph for this is too much. I'm not sure what else you'd use this for, considering triangles themselves are a little random already. Also, it has the risk of moving items outside of their triangle and end up floating, or inside a wall.

Instead, why couldnt there be a "jitter" slider, which would be used by the new emit mode, which would randomly displace points towards triangle corners? 0 would not displace, 1 would fully displace. It's far simpler than having to setup a graph. And also a lot cheaper (using 3 FastNoise instances is much heavier than a simple seeded random)

I discovered it is because transitions enabled. It generates some near zero size triangles

Existing modes get around this by either avoiding edges or accounting for triangle area. But there is a way to avoid them at zero cost: they are located towards the end of the triangle list. There would need to be some way to pull that info from the meshing process eventually (like colliders do), or have it filtered away before it gets to the generator.

@Anyeos
Copy link
Author

Anyeos commented Sep 9, 2024

The idea is to generate only one per face. But, the condition is that it is always centered. That is, that its position corresponds to the center of the face where it is generated (the Normal of the face).

Sounds easy enough to add.

Then an additional requirement will be that the NoiseGraph has one more output: UV (or XY) which would be an offset that would be added to the position of the item. That offset can be added based on the normal of the face, therefore the displacement of the item would be based on the face and not based on global_position (that's why I think it's a good idea to name it UV instead of XY).

I'm a bit reserved about the way to do this.

First, the normal of the face is not enough to determine two tangent axes. So I assume you mean X and Y of the spawned item after its orientation is randomly chosen?

I already did it and it is the middle of the triangle in 3D space. So it will spawn in the middle of the triangle and that is "easy" to compute (it is a math addition and division by 3.0 because there are 3 coordinates). You can see it on above screenshots and I put the code that I am using for it (get_triangle_center).

Then about doing X and Y with graphs... I think it's too much. The graph system doesn't have vectors, and no dedicated output type that is meant to represent X and Y coordinates. The issues you got trying to re-use existing output nodes might have caused you issues because they are treated a little bit differently due to their purpose. Doing that is not obvious. Custom outputs could be used, and would have to be named "X" and "Y" to be recognized somewhat by the generator. Though adding a dedicated output type might be more user friendly (note: there is already a graph property to control density, and it uses the SDF output I think). But also, it sounds like you aren't actually going to use X and Y to output anything more than noise to randomly displace items a bit... using a graph for this is too much. I'm not sure what else you'd use this for, considering triangles themselves are a little random already. Also, it has the risk of moving items outside of their triangle and end up floating, or inside a wall.

Yes, triangles are some random but as you can see it on the screenshots if you don't choose optimize from the mesh generator then the triangles are more close to a pattern instead random. And I can take an advantage of it doing what I am trying to do here.

Instead, why couldnt there be a "jitter" slider, which would be used by the new emit mode, which would randomly displace points towards triangle corners? 0 would not displace, 1 would fully displace. It's far simpler than having to setup a graph. And also a lot cheaper (using 3 FastNoise instances is much heavier than a simple seeded random)

I agree it is not efficient use the Graph editor to set the offset with a noise source. But I want to use noise for that because I can replicate that prediction in another generator for other items generation. That is the purpose. I hope I can be more clear: The purpose is to have some predictive / replicable pseudo random to apply the an offset to the items. So I can choose exactly the same noise source but with a displacement in the input of it. And the output will be different but will follow the same noise pattern and that will avoid overlapping of two or more items generated with different generators. That is the only purpose and the Graph is the only way that I found can let me program a logic for that.

I can use a jitter and I am very interested, but I don't know how to replicate the above behaviour.

I discovered it is because transitions enabled. It generates some near zero size triangles

Existing modes get around this by either avoiding edges or accounting for triangle area. But there is a way to avoid them at zero cost: they are located towards the end of the triangle list. There would need to be some way to pull that info from the meshing process eventually (like colliders do), or have it filtered away before it gets to the generator.

Good to know. I am actually doing it checking the area, and if it is very little it skip that triangle. Some weird that I found is that there are no zero size area, it is always some number, so I checked against 0.1 instead of 0 because it didn't worked. So if that triangles are always at the end it will be more easy to skip they.

@Anyeos
Copy link
Author

Anyeos commented Sep 9, 2024

So I'm thinking of implementing the jitter but also an additional noise input (which if the user doesn't want it, he won't use it). But if he uses it, he'll be able to always apply the same noise (copying and pasting) to other generators with the offset changed (I already saw that the noise has an offset). That's exactly what I want: That the items don't overlap.

The jitter is going to establish the +- variation of the offset that will be applied to each item. If the user defined a noise, the noise will be used for the jitter, if the user didn't define it, what you mentioned is used.

And so I won't need the Graph for this purpose.

Let me explain:
Find a 3D position that will correspond to the center of a triangle. Then, an offset is applied based on the output of a noise that receives the coordinates obtained previously as input. That displaces the item based on "jitter" and noise. Then, in another generator, the same noise is placed (same parameters) but with an offset (in the noise itself). This means that now the output has a different value but following the same pattern because it is the same noise or has the same parameters (except its offset, of course). As a result, a different offset will be applied that will not tend to clash with the item of the previous generator.

You can tell me that it is difficult to guarantee that it does not clash (overlap), but if the noise is chosen carefully, the probability of overlapping can be considerably reduced. And that, for me, is enough reason to use this "technique".

Well, I hope I have not made any mistakes in the previous logic, if you see something that I have missed or want to contribute, I would appreciate it. My idea is to find a solution to the overlapping since I want to place trees very close to other things but that do not touch each other and I want that to be able to replicate with a low probability of overlapping.

see you.

@Zylann
Copy link
Owner

Zylann commented Sep 9, 2024

Overall I'm trying to limit complexity in what to add, because there are simply a million ways each thing could be done (many of which are complicated/unnecessary/inefficient/too specific/whatever), and I don't want to add too much ad-hoc stuff in there. Especially if some initially requested approach ends up not being used like #682 (so I decided to stop working in it for now).
Eventually I'd like to expose point generators and helpers to scripting so it wouldn't be necessary to constantly try to add special cases to the stock generator.

Find a 3D position that will correspond to the center of a triangle

Could use a noise that has a period roughly the size of triangles, or a simple hash (cheaper than 3D noise, which has to compute and interpolate 8 hashes in the case of Perlin), or a hash where coordinates are slightly quantized to minimize floating point issues. It might be that even the noise resource option is not even necessary (it's like using a dedicated kind of "noise" that is optimized for this specific use case; it doesnt have to be interpolated)

Assuming that is all, I can try implementing some of this in another branch at some point. Otherwise you could carry on with your own approach, eventually integrating it with a future scripting API?

@Zylann
Copy link
Owner

Zylann commented Sep 11, 2024

I added a OnePerTriangle mode in the instance_generator_one_per_tri branch, if you want to try

@Anyeos
Copy link
Author

Anyeos commented Sep 18, 2024

Hi, how are you? Don't worry. My idea in posting the feature request here is to have an archive of the request within the original project. If someone else comes up with a similar idea, then they can find that it was already thought of, discussed and evaluated. I think it's a good idea to discuss features or ideas about your project right here just because it's the main project and also because it's active.

On the other hand, my intention is to comply with the license that requires me to publish the source code. Of course, the license doesn't say that I have to publish the code in the original project, nor does it force the author (you) to accept such modifications. But I wanted to keep things simpler regarding where to discuss issues about the project.

But from now on I'm going to create a fork where I'm going to be applying my own modifications in order to comply with the license requirements. However, I'd like to keep the feature discussions here if you don't mind. And of course, the personal and particular details (as you mentioned) I'll keep in my own fork.

I hope I haven't bothered you because my intentions were precisely to comply with the license and contribute to improvements. I hope I have been able to explain myself well and I apologize for the inconvenience.

@Anyeos
Copy link
Author

Anyeos commented Sep 18, 2024

Sorry, I noted that the licence is the MIT one, I thought it was the GPL or something similar. Well, anyway keeping a fork I think it is not a bad idea because I can do my own tests.

@Anyeos
Copy link
Author

Anyeos commented Sep 18, 2024

I added a OnePerTriangle mode in the instance_generator_one_per_tri branch, if you want to try

Hello again, I've seen the code and it's very good. It's very good, I really congratulate you. I hope I don't sound too cloying or silly but I think that people who do a good job deserve at least a congratulations.

This already helps me a lot with this problem. I'm also thinking of some ideas for the other problem with the list (array) of items. Because although I don't want to use it now, it actually does help me. What happens is that I'm making a video game project where I'm testing your voxel engine so I haven't developed the game much yet. Anyway, it's practice for making another game later so I can test the voxel engine without problems.

Here a screenshot of the game test:
image

@Zylann
Copy link
Owner

Zylann commented Sep 28, 2024

OnePerTriangle was merged in 06e8f90

@Zylann Zylann closed this as completed Sep 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants