c-d-a / io_export_qmap

.map exporter for Blender
GNU General Public License v3.0
93 stars 21 forks source link

Export Planes as a Patch #19

Closed creed59 closed 8 months ago

creed59 commented 8 months ago

Would it be possible to write out each plane as a patch? I'm thinking of a way to reduce the number of brushes that are written out by this program. The program currently doesn't work very well with concave mesh. This is the format I'm looking for: // entity 0 { "classname" "worldspawn" // brush 0 { mesh { toolFlags splitGeo dontSplitLights; $default lightmap_gray 5 5 16 8 ( v -168 -120 8 t 0 -0 -10.5 7.5 v -168 8 8 t 0 -32768 -10.5 -0.5 v -168 136 8 t 0 -65536 -10.5 -8.5 v -168 264 8 t 0 -98304 -10.5 -16.5 v -168 392 8 t 0 -131072 -10.5 -24.5 ) ( v -74 -120 8 t 24064 -0 -4.625 7.5 v -74 8 8 t 24064 -32768 -4.625 -0.5 v -74 136 8 t 24064 -65536 -4.625 -8.5 v -74 264 8 t 24064 -98304 -4.625 -16.5 v -74 392 8 t 24064 -131072 -4.625 -24.5 ) ( v 20 -120 8 t 48128 -0 1.25 7.5 v 20 8 8 t 48128 -32768 1.25 -0.5 v 20 136 8 t 48128 -65536 1.25 -8.5 v 20 264 8 t 48128 -98304 1.25 -16.5 v 20 392 8 t 48128 -131072 1.25 -24.5 ) ( v 114 -120 8 t 72192 -0 7.125 7.5 v 114 8 8 t 72192 -32768 7.125 -0.5 v 114 136 8 t 72192 -65536 7.125 -8.5 v 114 264 8 t 72192 -98304 7.125 -16.5 v 114 392 8 t 72192 -131072 7.125 -24.5 ) ( v 208 -120 8 t 96256 -0 13 7.5 v 208 8 8 t 96256 -32768 13 -0.5 v 208 136 8 t 96256 -65536 13 -8.5 v 208 264 8 t 96256 -98304 13 -16.5 v 208 392 8 t 96256 -131072 13 -24.5 ) } } }

cod5_map

c-d-a commented 8 months ago

Added in https://github.com/c-d-a/io_export_qmap/commit/cb42f4c007c8d73bae9f88b5643016a53fe3c7e9

Exporting the map as patches can be a lot (200x) faster, and texture coordinates are much simpler and more accurate (so it can be useful for small props too).

But I'm not sure this would improve the in-game performance at all. If anything, collision may get slower. Editor performance might tank as well, though that depends on the editor. And I assume you'd still need brush geometry for sealing and visibility calculation.

c-d-a commented 8 months ago

Actually, I didn't pay attention to the format that you specified. I'm not familiar with it, the exporter still only supports patchDef.

I suspect your editor can read patchDef anyway, but I can't test it myself, at least not at the moment. Try it out, let me know.

creed59 commented 8 months ago

I think the format for my editor is patchDef2 just in a different layout. It's a modified version of Q3 Radiant original. The idea is to make a single patch from connected mesh, not individual faces. Like this: e

c-d-a commented 8 months ago

Impossible. A patch is an NxM grid, it can't represent arbitrary topology.

Best you can do is prepare the meshes prior to exporting them. Do limited dissolve or planar decimate, then triangulate n-gons, then run tris-to-quads with high angle limits. Or do some auto retopology. The above picture will still be three patches minimum though.

creed59 commented 8 months ago

I do not think it's impossible, if you can calculate the minimal size of a patch that would represent a mesh. For example, if you had an octagonal prism whose height (number of unique y coord verts) is 4 and width is 4 (x) and unique z's is 2 you could represent it with a 16x2 patchDef2. Or if you had the same shape except its curved with 5 unique z value vertices 9x 1y 5z = 45, you could represent the mesh with a 9x5 patch. Screenshot 2024-01-22 235733

I threw together some pseudocode for the idea, tell me what you think. patch

c-d-a commented 8 months ago

Well yes, cylinders and donuts can be easily unwrapped into rectangles, that's the best case scenario for patches.

But look at the topology in your own example.
C
Even this simple strip is impossible to represent as a patch. You can't have a missing face, not unless each quad is further subdivided into at least 3x3 quads.

Then consider something like an icosphere. The algorithm will somehow need to decide where to add edge splits and duplicate verts so that the mesh can be peeled and flattened with no overlaps and 100% quad-based topology.

And the only benefit of all that is being able to group as a patch instead of a func_group. Hardly worth it.

creed59 commented 8 months ago

So the above algorithm might work if you first remove any face subdivisions or make a quad-based/Sub-D mesh?

MoritzJT commented 8 months ago

The vertices in the above example probably stem from tjunc fixing - if you had compiled these with q3map_noTjunc or globally disabling tjunc fixing in bsp phase with -notjunc - you might have had it more easy to discern that, but really no good algo would be able to do that entirely automatically as not every type of meta surface spewed out by q3map2 could be represented by a patchMesh, even with some of the rows collapsed. While you can even start to craft patches that have disjunct UV coordinates like on a UV unwrap with the seams costing you an extra row / col that has to be collapsed in 3D as in never rendered, nothing can do that automatically for you without extensive guesswork and random tries or some type of heuristic search for the best solution at massive costs.

The only thing you could attempt would be to export every triangle as a patch, but there would be hardly any way to make them mergable - same issue as with the other automation you crave.

creed59 commented 8 months ago

Well yes, cylinders and donuts can be easily unwrapped into rectangles, that's the best case scenario for patches.

But look at the topology in your own example. C Even this simple strip is impossible to represent as a patch. You can't have a missing face, not unless each quad is further subdivided into at least 3x3 quads.

Then consider something like an icosphere. The algorithm will somehow need to decide where to add edge splits and duplicate verts so that the mesh can be peeled and flattened with no overlaps and 100% quad-based topology.

And the only benefit of all that is being able to group as a patch instead of a func_group. Hardly worth it.

Triangular re-mesh / triangulation would be ideal in any case, correct? The topology seems to work out. An icosphere has a vertex order of 6 (6 edges per vertice at the highest), so it should be representable as a single patch. The highest vertex order of a patch is 6. Also, a default Blender icosphere would require a minimum 45x45 patch since it has 15 unique x's and y's and 9 unique z's (15x15x9=2025). The idea is then to find an algorithm that does what I did in the first picture below and have the (X,Y,Z) coordinates assigned to each control point. The assigning of coordinates to dummy vertices can be the nearest coaxial vertice out of the non-dummy vertices, so the edges overlap and there wont be any dummy faces. Converting a mesh into a 2d array of floating point coordinate plane values: Untitled

creed59 commented 8 months ago

Two requirements would have to be met in order for this method of converting mesh groups to patchdefs to work. I'm just making this up, but I think I'm going somewhere with it.

  1. You can't have any mesh in a scene where any of the vertices connect with more than 8 edges, as it violates the topology of a patch.
  2. Every face in the scene must be a triangle. Let's say that you wanted to convert a 2d-pentagon-shaped mesh into a map as a single patch: sample The most number of times an edge is attached to a vert is 4 times and every face is a triangle, so the mesh is suitable for conversion. An icosphere has a max of 6 edges to one vert and every face is a triangle, so this would also work. In fact I'm pretty sure any shape of any complexity meeting those 2 rules can be a single patchdef. As for getting rid of the dummy control points I don't think that's possible. However, as mentioned before you can avoid having any faces or edges that could be created by them by defining their coordinates to be that of the nearest non-dummy coaxial control point. In the octagon example, the row 1 column 2 control point is coaxial by the z axis to the row 2 column 3 control point. The row 2 column 4 and row 1 column 3 control points are coaxial to the same control point by the x and y axis respectively. So the algorithm would have to find the closest point that is also coaxial by one or more axes for each dummy control point.

I'm not entirely convinced its unrealistic to represent mesh as a single patch. You can reproduce very complicated scenes into a quake editor, so you can do things like make your own version of your favorite stock maps of games without exceeding the limits of the compiler or having to manually brush out everything. Hypothetically if this method is to work the brush count would be astronomically less than the counterparts to the plugin. For a complex map with all sorts of brush types you could represent them all with only a few thousand brushes at the most instead of 100,000.

c-d-a commented 8 months ago

You're still basically starting from a patch and working backwards. Now, punch a hole through the middle of the grid, and suddenly you'll have a whole lot of headache.

https://github.com/c-d-a/io_export_qmap/assets/55441216/013a2ad1-16ce-41ff-8538-ac564be8374a.mp4

The vert count went from 16 to 88, even on an extremely basic example. Hopefully, this also illustrates that simply counting vertex valence isn't enough. There are other ways to do this (split corner quads, turning the circle into a strip), but they won't work for every possible mesh.

Then the icosphere. You're merging the triangles, in which case - yes, it's easy to turn it into a grid, e.g. using the default UV map as a guideline (just fill in the missing corners). That's 13x11 meaningful coordinates, or 25x21 with the extra control points. However, crucially, it will change shape and stop being a sphere. You wouldn't want to merge triangles across a 90-degree corner of a building, this is the same thing.

When I brought up the icosphere, I meant treating each of its triangles as a separate quad. I can actually come up with a couple of ways to peel it - like this, for instance, an Nx1 strip with leftovers re-attached at the end.

https://github.com/c-d-a/io_export_qmap/assets/55441216/b5a24749-2d13-40bc-b29f-2c75f344ec62

I'm not even sure how I'd approach writing code that would keep track of all the relevant things, but already I'd estimate that it would be comparable in length to the rest of the export script. Would probably have to traverse the mesh as a tree, keeping track of cardinal directions, and extending the previous branches to make room as needed.

Anyway, the bottom line is that I can retract the word "impossible", but it's extremely unlikely that I'd ever go about actually implementing this as an algorithm, and even less likely as a part of the exporter.

c-d-a commented 8 months ago

Re: compiler limits - are there any? I know Q1 and Q3 maps from years ago that use tens of thousands of brushes. 40k brushes doesn't seem like that big a deal to me. Surface count limits are a thing in some formats, but I'd imagine patches contribute to that as well.

creed59 commented 8 months ago

I don't know if there are actually any I just get a message every time I try to load ripped geometry as a map, the MAX_MAP_PLANES one. Just did a 5 second Google and got that this error is a result of too many brushes.