eliemichel / OpenMfxForBlender

A branch of Blender featuring an OpenMfx modifier
Other
178 stars 19 forks source link

How to create wireframe mesh effects? #27

Closed EleotleCram closed 4 years ago

EleotleCram commented 4 years ago

I have one mesh effect that I want to create that adds a wireframe for sharp edges. For this I need to be able to define edges, no faces. But I could not find an edge array in the API.

Am I overlooking something, or is this not yet part of the API?

eliemichel commented 4 years ago

Hi @EleotleCram indeed there is no explicit notion of edge, I still wonder if it is needed. To define edge only meshes, you can create polygons made of two vertices only. At least it is the intention, I haven't checked lately if it still works, let me know!

One reason to add support to explicit edges would be to be able to attach attributes to edges, but so far I haven't added this because it raises some technical issues and actually you can always attach data to vertices = face corners = loops in blender API. Houdini does not have explicit edge attributes iirc. But Blender has.

tkarabela commented 4 years ago

I was also experimenting with this and as far as I can tell:

The "Feature edges" effect in https://github.com/tkarabela/MfxVTK can be used to create OFX mesh with just edges and no faces.

I've tried patching the host code to generate proper edges on Blender side ( https://github.com/tkarabela/OpenMeshEffectForBlender/commit/6005e1dcad4c20928dfcb7fb3b359bc660f6434e ) - it works, though some more work is needed to support attributes as well. In the future, it should also work the other way, ie. Blender should translate edges to 2-vertex faces for OFX. It looks like this will introduce a slow path in before_mesh_get(), since we won't be able to reuse Blender's vertex/face buffers (we can still keep current fast path for polygonal inputs).

If it's OK, I can implement this and send a pull request?

eliemichel commented 4 years ago

Yes I consent to this PR, thank you for putting thinking in this! I am still balancing about whether to add proper edges to the Open Mesh Effect API or not, at least to distinguish edge attributes from other ones. Still being able to reuse Blender's buffer is a relevant point too, even though it is more implementation related and should hence be less influencial in design desisions.

I'll try to build your MfxVTK and point to it from Open Mesh Effect web page if you agree.

tkarabela commented 4 years ago

Thanks for linking to MfxVTK!

I've tried to summarize my thoughts on edges below. Sorry for the brain dump, like they say, "If I had more time I would have written a shorter letter" ^^; In summary, the main pain point I see is that in current spec, face count is really (face + loose edge) count, and we should declare/require both, to enable optimizations for common cases, regardless of how we want to handle edge attributes.


To implement current spec, both Blender host and MfxVTK have to do extra processing to handle 2-vertex faces correctly, even if the mesh doesn't have any - there is no way of knowing this in advance. I'm not familiar with Houdini API, but it wouldn't surprise me if there were some opportunities as well.

My understanding is that most of the time, OpenEffectMesh input/output will be "homogenous" in the sense that it will be either:

  1. polygonal mesh - proper faces, no loose edges, no isolated points
  2. edge wireframe - no faces, only loose (ie. faceless) edges, no isolated points
  3. point cloud - no faces, no loose edges, just isolated points

I wouldn't disallow other combinations, but these should be hitting fast paths in the implementation if possible. Here's what I came up with:

Proposed extension of OpenMeshEffect spec (version 1)

Example

Screenshot from 2020-10-13 20-29-18

Implications for use cases

  1. polygonal mesh - I don't have to care about edges, use host point, vertex and face buffers directly
  2. edge wireframe - I don't have to care about faces, use host point, vertex and edge (?) buffers directly
  3. point cloud - I don't have to care about edges or faces - this can already be optimized without the change

Extensibility in the future

Issues (proposal 1)

If we were to prioritize polygonal meshes, the proposal may well be simplified:

Proposed extension of OpenMeshEffect spec (version 2, simpler)

This would require whoever creates the OFX mesh to declare how many "loose" edges there will be, without changing the current layout. Optimizations are still possible based on kOfxMeshPropEdgeCount == 0 or kOfxMeshPropEdgeCount == kOfxMeshPropFaceCount (in common cases, there should be no need to separate faces from loose edges, as it will be homogenous).

Issues (proposal 2)

eliemichel commented 4 years ago

Thanks for this pretty clear and structured brain dump. :)

In a similar spirit to version 2, I was thinking about (2b) a "tag" (I mean, a boolean prop) to tell that a mesh is edge only or face only. I don't see a use cas for knowing the exact edge count in mixed cases. If needed one can just iterate through face counts, they would at some point anyways. So I find it less missleading to just give the tags. BTW such a bool prop should eventually arise for tagging effects that only deform meshes without altering the connectivity.

Reading your point about kOfxMeshAttribFaceCounts being a useless array of 2s, I was thinking the same occurs for fully triangular meshes. Maybe (2c) we could have a kOfxMeshPropConstantFaceCounts set to 2 or 3 when there are only edges or triangles, and -1 to mean "use kOfxMeshAttribFaceCounts".

NB: In Houdini, there is just no explicit notion of loose edge: https://www.sidefx.com/docs/houdini/model/attributes They are treated as open 2-polygons (dunno whether the closeness of polygons is actually stored but it is specified in addprim).

So, version 1 makes a lot of sense, but I am still wondering whether the cost in terms of minimalism and elegance of the API are worth the benefits once we have something like version 2/2b/2c. I am especially not totally happy with the idea of mixing different data in the same buffers (edge extremities and face corners).

I think we could introduce arbitrary edge attributes (kOfxMeshAttribEdge) without requiring to separate lose edges from faces. Just define that seeing a primitive of 2 would imply to offset by only 1 in the edge attribute buffer, even though for all larger amounts it'd mean offseting by the number of vertices in the polygon.

Overall I'd lean toward the following changes:

What do you think?

tkarabela commented 4 years ago

I like this, it fits nicely with the design so far while keeping the benefits of (1), (2) and bringing in more.

(2C) Add a kOfxMeshPropConstantFaceCounts property type to meshes to skip the use of the kOfxMeshAttribFaceCounts buffer when it is not -1.

Completely agree, this optimizes at least three things at once :) (better use of memory, signalling pure wireframe or triangular mesh). Perhaps I would name it in singular: kOfxMeshPropConstantFaceCount, not that it matters much.

(2B) Add a kOfxMeshPropNoEdge boolean property turned on by default, that one must turn off when building a mesh with loose edges.

Agree.

(2D) Add a kOfxMeshPropNoEdge (incorrect name?) boolean property turned off by default that one can turn on to speed up processing when the effect does not affect connectivity.

Yes, we need something like this as well, as you mentioned before. I see this as two related concepts:

With (A), we can bypass copying output to host as well as handle host-specific things like Blender treating deform modifiers specially. In terms of API, it looks kinda like kOfxMeshEffectActionIsIdentity to me (kOfxMeshEffectActionIsDeformation?), even specifying which input should be routed to output.

Thinking about the host not looking at connectivity output from effect, I realized that some effects may not need to look at their input connectivity either (like lattice deformation, which will just move points and keep connectivity; or like convex hull, which creates new connectivity from points; or extra inputs that are used just for their transform #35), hence the opportunity for (B2), which may be niche but it could be solved together with (B1), ie. how should an effect declare additional inputs it needs (like vertex weights).

A simple solution to (B1), (B2) that comes to mind - since the effect already has to have boilerplate to explicitly define kOfxMeshMainInput, kOfxMeshMainOutput in Describe, we could require it to also define kOfxMeshAttribPointPosition, kOfxMeshAttribVertexPoint, kOfxMeshAttribFaceCounts on the meshes. It's a bit verbose, but pretty simple - you will get at cooking time, what you asked for at describe time (if data is available). If you need something more or less, that is the place to do it. (If you don't ask for points/vertices/faces on output mesh, I would be inclined to assume will be none, and leave the special handling to kOfxMeshEffectActionIsDeformation.)

(2E) Introduce arbitrary edge attributes (kOfxMeshAttribEdge) with no such attribute by default

Agree, it would be good to put them into the spec now, so that the "where goes what data" story is complete, even if we don't have use for them now.


It looks like the issue "How to create wireframe mesh effects?" is a lot clearer now :) Wireframe is just 2-vertex faces, but you have to set kOfxMeshPropNoEdge = false to be able to include them in the output. If you want to have edge attributes, create an attribute with kOfxMeshAttribEdge attachment; faces have the same number of vertices and edges, except for 2-vertex faces that have just one edge. Finally, if all your faces have the same number of vertices, skip kOfxMeshAttribFaceCounts and just use kOfxMeshPropConstantFaceCounts. That is to say, (2B), (2C), (2E).

(2D) feels as a separate topic, maybe we can continue discussing it in a new ticket?

Having written this, I remembered what the use case for "knowing the exact edge count in mixed cases" is. It's so that the host knows how big a buffer to allocate for edge attributes, otherwise it has to overallocate up to number of vertices (~2x in the worst case). So I'm leaning slightly more towards (2), (2C), (2E) than (2B), (2C), (2E).

eliemichel commented 4 years ago

(Following up this whole discussion in a dedicated issue in the OpenMeshEffect repo, @tkarabela concluded the wireframe part just above)