From d19c89224ab33da95d5412d8a76c810089912eed Mon Sep 17 00:00:00 2001 From: Garux Date: Thu, 6 Nov 2025 19:30:15 +0500 Subject: [PATCH] refactor mesh_t --- tools/quake3/q3map2/decals.cpp | 74 ++++------ tools/quake3/q3map2/fog.cpp | 162 ++++++++++------------ tools/quake3/q3map2/light_bounce.cpp | 37 ++--- tools/quake3/q3map2/light_trace.cpp | 55 +++----- tools/quake3/q3map2/light_ydnar.cpp | 77 +++++------ tools/quake3/q3map2/lightmaps_ydnar.cpp | 85 +++++------- tools/quake3/q3map2/map.cpp | 4 +- tools/quake3/q3map2/mesh.cpp | 171 ++++++++++-------------- tools/quake3/q3map2/patch.cpp | 16 +-- tools/quake3/q3map2/q3map2.h | 29 ++-- tools/quake3/q3map2/surface.cpp | 60 ++++----- tools/quake3/q3map2/surface_foliage.cpp | 44 +++--- tools/quake3/q3map2/surface_meta.cpp | 36 ++--- tools/quake3/q3map2/writebsp.cpp | 2 +- 14 files changed, 352 insertions(+), 500 deletions(-) diff --git a/tools/quake3/q3map2/decals.cpp b/tools/quake3/q3map2/decals.cpp index 4da38e90..ac8faf22 100644 --- a/tools/quake3/q3map2/decals.cpp +++ b/tools/quake3/q3map2/decals.cpp @@ -410,40 +410,34 @@ void ProcessDecals(){ /* create projectors */ if ( distance > 0.125f ) { /* tesselate the patch */ - const int iterations = IterationsForCurve( p.longestCurve, patchSubdivisions ); - mesh_t *subdivided = SubdivideMesh2( p.mesh, iterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); + mesh_t mesh = TessellatedMesh( p.mesh, IterationsForCurve( p.longestCurve, patchSubdivisions ) ); /* offset by projector origin */ - for ( bspDrawVert_t& vert : Span( mesh->verts, mesh->width * mesh->height ) ) + for ( bspDrawVert_t& vert : Span( mesh.verts, mesh.numVerts() ) ) vert.xyz += e.origin; /* iterate through the mesh quads */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* get drawverts */ const bspDrawVert_t *dv[ 4 ] = { - &mesh->verts[ pw[ r + 0 ] ], - &mesh->verts[ pw[ r + 1 ] ], - &mesh->verts[ pw[ r + 2 ] ], - &mesh->verts[ pw[ r + 3 ] ] + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] }; /* planar? (nuking this optimization as it doesn't work on non-rectangular quads) */ if ( 0 && PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) && @@ -465,7 +459,7 @@ void ProcessDecals(){ } /* clean up */ - free( mesh ); + mesh.freeVerts(); } } /* remove patches from entity */ @@ -611,52 +605,42 @@ static void ProjectDecalOntoPatch( decalProjector_t& dp, mapDrawSurface_t& ds ){ return; /* tesselate the patch */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = ds.verts.data(); - const int iterations = IterationsForCurve( ds.longestCurve, patchSubdivisions ); - mesh_t *subdivided = SubdivideMesh2( src, iterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, ds.verts.data() ), IterationsForCurve( ds.longestCurve, patchSubdivisions ) ); /* iterate through the mesh quads */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* generate decal for first triangle */ winding_t w{ - mesh->verts[ pw[ r + 0 ] ].xyz, - mesh->verts[ pw[ r + 1 ] ].xyz, - mesh->verts[ pw[ r + 2 ] ].xyz }; + mesh.verts[ pw[ r + 0 ] ].xyz, + mesh.verts[ pw[ r + 1 ] ].xyz, + mesh.verts[ pw[ r + 2 ] ].xyz }; ProjectDecalOntoWinding( dp, ds, w ); /* generate decal for second triangle */ winding_t w2{ - mesh->verts[ pw[ r + 0 ] ].xyz, - mesh->verts[ pw[ r + 2 ] ].xyz, - mesh->verts[ pw[ r + 3 ] ].xyz }; + mesh.verts[ pw[ r + 0 ] ].xyz, + mesh.verts[ pw[ r + 2 ] ].xyz, + mesh.verts[ pw[ r + 3 ] ].xyz }; ProjectDecalOntoWinding( dp, ds, w2 ); } } /* clean up */ - free( mesh ); + mesh.freeVerts(); } diff --git a/tools/quake3/q3map2/fog.cpp b/tools/quake3/q3map2/fog.cpp index 7f6f5108..7ad9834d 100644 --- a/tools/quake3/q3map2/fog.cpp +++ b/tools/quake3/q3map2/fog.cpp @@ -30,6 +30,7 @@ /* dependencies */ #include "q3map2.h" +#include @@ -43,13 +44,10 @@ static int numFogPatchFragments; converts a patch drawsurface to a mesh_t */ -static mesh_t *DrawSurfToMesh( const mapDrawSurface_t& ds ){ - mesh_t *m = safe_malloc( sizeof( *m ) ); - m->width = ds.patchWidth; - m->height = ds.patchHeight; - m->verts = safe_malloc( sizeof( m->verts[ 0 ] ) * m->width * m->height ); - memcpy( m->verts, ds.verts.data(), sizeof( m->verts[ 0 ] ) * m->width * m->height ); - +static mesh_t DrawSurfToMesh( const mapDrawSurface_t& ds ){ + const size_t size = sizeof( ds.verts[ 0 ] ) * ds.patchWidth * ds.patchHeight; + mesh_t m( ds.patchWidth, ds.patchHeight, safe_malloc( size ) ); + memcpy( m.verts, ds.verts.data(), size ); return m; } @@ -59,24 +57,24 @@ static mesh_t *DrawSurfToMesh( const mapDrawSurface_t& ds ){ SplitMeshByPlane() chops a mesh by a plane */ - -static void SplitMeshByPlane( mesh_t *in, const Plane3f& plane, mesh_t **front, mesh_t **back ){ +/// \returns either {front, back} or {front, {}} or {{}, back} +/// frees or reuses \param in +static std::pair, std::optional> SplitMeshByPlane( mesh_t& in, const Plane3f& plane ){ int w, h, split; float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE]; bspDrawVert_t *dv, *v1, *v2; int c_front, c_back, c_on; - mesh_t *f, *b; int i; float frac; int frontAprox, backAprox; for ( i = 0; i < 2; ++i ) { - dv = in->verts; + dv = in.verts; c_front = 0; c_back = 0; c_on = 0; - for ( h = 0; h < in->height; ++h ) { - for ( w = 0; w < in->width; ++w, ++dv ) { + for ( h = 0; h < in.height; ++h ) { + for ( w = 0; w < in.width; ++w, ++dv ) { d[h][w] = plane3_distance_to_point( plane, dv->xyz ); if ( d[h][w] > ON_EPSILON ) { c_front++; @@ -90,21 +88,16 @@ static void SplitMeshByPlane( mesh_t *in, const Plane3f& plane, mesh_t **front, } } - *front = nullptr; - *back = nullptr; - if ( !c_front ) { - *back = in; - return; + return { {}, in }; } if ( !c_back ) { - *front = in; - return; + return { in, {} }; } // find a split point split = -1; - for ( w = 0; w < in->width - 1; ++w ) { + for ( w = 0; w < in.width - 1; ++w ) { if ( ( d[0][w] < 0 ) != ( d[0][w + 1] < 0 ) ) { if ( split == -1 ) { split = w; @@ -116,30 +109,27 @@ static void SplitMeshByPlane( mesh_t *in, const Plane3f& plane, mesh_t **front, if ( split == -1 ) { if ( i == 1 ) { Sys_FPrintf( SYS_WRN | SYS_VRBflag, "No crossing points in patch\n" ); - *front = in; - return; + return { in, {} }; } - in = TransposeMesh( in ); + TransposeMesh( in ); InvertMesh( in ); continue; } // make sure the split point stays the same for all other rows - for ( h = 1; h < in->height; ++h ) { - for ( w = 0; w < in->width - 1; ++w ) { + for ( h = 1; h < in.height; ++h ) { + for ( w = 0; w < in.width - 1; ++w ) { if ( ( d[h][w] < 0 ) != ( d[h][w + 1] < 0 ) ) { if ( w != split ) { Sys_Printf( "multiple crossing points for patch -- can't clip\n" ); - *front = in; - return; + return { in, {} }; } } } if ( ( d[h][split] < 0 ) == ( d[h][split + 1] < 0 ) ) { Sys_Printf( "differing crossing points for patch -- can't clip\n" ); - *front = in; - return; + return { in, {} }; } } @@ -148,63 +138,49 @@ static void SplitMeshByPlane( mesh_t *in, const Plane3f& plane, mesh_t **front, // create two new meshes - f = safe_malloc( sizeof( *f ) ); - f->width = split + 2; - if ( !( f->width & 1 ) ) { - f->width++; + mesh_t f( split + 2, in.height, nullptr ), b( in.width - split, in.height, nullptr ); + if ( !( f.width & 1 ) ) { + f.width++; frontAprox = 1; } else { frontAprox = 0; } - if ( f->width > MAX_PATCH_SIZE ) { + if ( f.width > MAX_PATCH_SIZE ) { Error( "MAX_PATCH_SIZE after split" ); } - f->height = in->height; - f->verts = safe_malloc( sizeof( f->verts[0] ) * f->width * f->height ); + f.verts = safe_malloc( sizeof( f.verts[0] ) * f.numVerts() ); - b = safe_malloc( sizeof( *b ) ); - b->width = in->width - split; - if ( !( b->width & 1 ) ) { - b->width++; + if ( !( b.width & 1 ) ) { + b.width++; backAprox = 1; } else { backAprox = 0; } - if ( b->width > MAX_PATCH_SIZE ) { + if ( b.width > MAX_PATCH_SIZE ) { Error( "MAX_PATCH_SIZE after split" ); } - b->height = in->height; - b->verts = safe_malloc( sizeof( b->verts[0] ) * b->width * b->height ); - - if ( d[0][0] > 0 ) { - *front = f; - *back = b; - } - else { - *front = b; - *back = f; - } + b.verts = safe_malloc( sizeof( b.verts[0] ) * b.numVerts() ); // distribute the points - for ( w = 0; w < in->width; ++w ) { - for ( h = 0; h < in->height; ++h ) { + for ( w = 0; w < in.width; ++w ) { + for ( h = 0; h < in.height; ++h ) { if ( w <= split ) { - f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ]; + f.verts[ h * f.width + w ] = in.verts[ h * in.width + w ]; } else { - b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ]; + b.verts[ h * b.width + w - split + backAprox ] = in.verts[ h * in.width + w ]; } } } // clip the crossing line - for ( h = 0; h < in->height; ++h ) + for ( h = 0; h < in.height; ++h ) { - dv = &f->verts[ h * f->width + split + 1 ]; - v1 = &in->verts[ h * in->width + split ]; - v2 = &in->verts[ h * in->width + split + 1 ]; + dv = &f.verts[ h * f.width + split + 1 ]; + v1 = &in.verts[ h * in.width + split ]; + v2 = &in.verts[ h * in.width + split + 1 ]; frac = d[h][split] / ( d[h][split] - d[h][split + 1] ); @@ -215,24 +191,28 @@ static void SplitMeshByPlane( mesh_t *in, const Plane3f& plane, mesh_t **front, LerpDrawVertAmount( v1, v2, frac, dv ); if ( frontAprox ) { - f->verts[ h * f->width + split + 2 ] = *dv; + f.verts[ h * f.width + split + 2 ] = *dv; } - b->verts[ h * b->width ] = *dv; + b.verts[ h * b.width ] = *dv; if ( backAprox ) { - b->verts[ h * b->width + 1 ] = *dv; + b.verts[ h * b.width + 1 ] = *dv; } } /* PrintMesh( in ); - Sys_Printf( "\n" ); PrintMesh( f ); - Sys_Printf( "\n" ); PrintMesh( b ); - Sys_Printf( "\n" ); */ - FreeMesh( in ); + in.freeVerts(); + + if ( d[0][0] > 0 ) + return { f, b }; + else + return { b, f }; + + } @@ -242,74 +222,72 @@ static void SplitMeshByPlane( mesh_t *in, const Plane3f& plane, mesh_t **front, */ static bool ChopPatchSurfaceByBrush( mapDrawSurface_t& ds, const brush_t *b ){ - int i, j; - mesh_t *outside[MAX_BRUSH_SIDES]; - int numOutside; - mesh_t *m, *front, *back; + mesh_t outside[MAX_BRUSH_SIDES]; + int numOutside = 0; - m = DrawSurfToMesh( ds ); - numOutside = 0; + mesh_t m = DrawSurfToMesh( ds ); // only split by the top and bottom planes to avoid // some messy patch clipping issues - for ( i = 4; i <= 5; ++i ) { + for ( int i = 4; i <= 5; ++i ) { const plane_t& plane = mapplanes[ b->sides[ i ].planenum ]; - SplitMeshByPlane( m, plane.plane, &front, &back ); + auto [front, back] = SplitMeshByPlane( m, plane.plane ); if ( !back ) { // nothing actually contained inside - for ( j = 0; j < numOutside; ++j ) { - FreeMesh( outside[j] ); + for ( int j = 0; j < numOutside; ++j ) { + outside[j].freeVerts(); } + front->freeVerts(); return false; } - m = back; + m = *back; if ( front ) { if ( numOutside == MAX_BRUSH_SIDES ) { Error( "MAX_BRUSH_SIDES" ); } - outside[ numOutside ] = front; + outside[ numOutside ] = *front; numOutside++; } } /* all of outside fragments become separate drawsurfs */ numFogPatchFragments += numOutside; - for ( i = 0; i < numOutside; ++i ) + for ( int i = 0; i < numOutside; ++i ) { /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */ - outside[ i ] = TransposeMesh( outside[ i ] ); + TransposeMesh( outside[ i ] ); InvertMesh( outside[ i ] ); /* ydnar: do this the hacky right way */ mapDrawSurface_t& newds = AllocDrawSurface( ESurfaceType::Patch ); newds = ds; - newds.patchWidth = outside[ i ]->width; - newds.patchHeight = outside[ i ]->height; - newds.verts.assign( outside[ i ]->verts, outside[ i ]->verts + newds.patchWidth * newds.patchHeight ); + newds.patchWidth = outside[ i ].width; + newds.patchHeight = outside[ i ].height; + newds.verts.assign( outside[ i ].verts, outside[ i ].verts + outside[ i ].numVerts() ); /* free the source mesh */ - FreeMesh( outside[ i ] ); + outside[ i ].freeVerts(); } /* only rejigger this patch if it was chopped */ - //% Sys_Printf( "Inside: %d x %d\n", m->width, m->height ); + //% Sys_Printf( "Inside: %d x %d\n", m.width, m.height ); if ( numOutside > 0 ) { /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */ - m = TransposeMesh( m ); + TransposeMesh( m ); InvertMesh( m ); /* replace ds with m */ - ds.patchWidth = m->width; - ds.patchHeight = m->height; - ds.verts.assign( m->verts, m->verts + ds.patchWidth * ds.patchHeight ); + ds.patchWidth = m.width; + ds.patchHeight = m.height; + ds.verts.assign( m.verts, m.verts + m.numVerts() ); } /* free the source mesh and return */ - FreeMesh( m ); + m.freeVerts(); return true; } diff --git a/tools/quake3/q3map2/light_bounce.cpp b/tools/quake3/q3map2/light_bounce.cpp index 1e82bd16..390440d2 100644 --- a/tools/quake3/q3map2/light_bounce.cpp +++ b/tools/quake3/q3map2/light_bounce.cpp @@ -648,22 +648,13 @@ void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, const shader /* build a subdivided mesh identical to shadow facets for this patch */ /* this MUST MATCH FacetsForPatch() identically! */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = bogus; - //% mesh_t *subdivided = SubdivideMesh( src, 8, 512 ); - mesh_t *subdivided = SubdivideMesh2( src, info.patchIterations ); - PutMeshOnCurve( *subdivided ); - //% MakeMeshNormals( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, bogus ), info.patchIterations ); free( bogus ); /* FIXME: build interpolation table into color[ 1 ] */ /* fix up color indexes */ - for ( bspDrawVert_t& vert : Span( mesh->verts, mesh->width * mesh->height ) ) + for ( bspDrawVert_t& vert : Span( mesh.verts, mesh.numVerts() ) ) { if ( vert.color[ 0 ][ 0 ] >= ds.numVerts ) { vert.color[ 0 ][ 0 ] = ds.numVerts - 1; @@ -671,27 +662,27 @@ void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, const shader } /* iterate through the mesh quads */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* get drawverts */ const bspDrawVert_t *dv[ 4 ] = { - &mesh->verts[ pw[ r + 0 ] ], - &mesh->verts[ pw[ r + 1 ] ], - &mesh->verts[ pw[ r + 2 ] ], - &mesh->verts[ pw[ r + 3 ] ] + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] }; /* planar? */ Plane3f plane; @@ -756,7 +747,7 @@ void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, const shader } /* free the mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); } diff --git a/tools/quake3/q3map2/light_trace.cpp b/tools/quake3/q3map2/light_trace.cpp index ae4ce3ba..303c0ab0 100644 --- a/tools/quake3/q3map2/light_trace.cpp +++ b/tools/quake3/q3map2/light_trace.cpp @@ -891,56 +891,43 @@ static void PopulateWithBSPModel( const bspModel_t& model, const Matrix4& transf case MST_PATCH: { /* subdivide the surface */ - mesh_t srcMesh; - srcMesh.width = ds.patchWidth; - srcMesh.height = ds.patchHeight; - srcMesh.verts = &bspDrawVerts[ ds.firstVert ]; - //% mesh_t *subdivided = SubdivideMesh( srcMesh, 8, 512 ); - mesh_t *subdivided = SubdivideMesh2( srcMesh, info.patchIterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); - - /* set verts */ - const bspDrawVert_t *verts = mesh->verts; + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, &bspDrawVerts[ ds.firstVert ] ), info.patchIterations ); /* subdivide each quad to place the models */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* make first triangle */ - tw.v[ 0 ].xyz = verts[ pw[ r + 0 ] ].xyz; - tw.v[ 0 ].st = verts[ pw[ r + 0 ] ].st; - tw.v[ 1 ].xyz = verts[ pw[ r + 1 ] ].xyz; - tw.v[ 1 ].st = verts[ pw[ r + 1 ] ].st; - tw.v[ 2 ].xyz = verts[ pw[ r + 2 ] ].xyz; - tw.v[ 2 ].st = verts[ pw[ r + 2 ] ].st; + tw.v[ 0 ].xyz = mesh.verts[ pw[ r + 0 ] ].xyz; + tw.v[ 0 ].st = mesh.verts[ pw[ r + 0 ] ].st; + tw.v[ 1 ].xyz = mesh.verts[ pw[ r + 1 ] ].xyz; + tw.v[ 1 ].st = mesh.verts[ pw[ r + 1 ] ].st; + tw.v[ 2 ].xyz = mesh.verts[ pw[ r + 2 ] ].xyz; + tw.v[ 2 ].st = mesh.verts[ pw[ r + 2 ] ].st; matrix4_transform_point( transform, tw.v[ 0 ].xyz ); matrix4_transform_point( transform, tw.v[ 1 ].xyz ); matrix4_transform_point( transform, tw.v[ 2 ].xyz ); FilterTraceWindingIntoNodes_r( &tw, nodeNum ); /* make second triangle */ - tw.v[ 0 ].xyz = verts[ pw[ r + 0 ] ].xyz; - tw.v[ 0 ].st = verts[ pw[ r + 0 ] ].st; - tw.v[ 1 ].xyz = verts[ pw[ r + 2 ] ].xyz; - tw.v[ 1 ].st = verts[ pw[ r + 2 ] ].st; - tw.v[ 2 ].xyz = verts[ pw[ r + 3 ] ].xyz; - tw.v[ 2 ].st = verts[ pw[ r + 3 ] ].st; + tw.v[ 0 ].xyz = mesh.verts[ pw[ r + 0 ] ].xyz; + tw.v[ 0 ].st = mesh.verts[ pw[ r + 0 ] ].st; + tw.v[ 1 ].xyz = mesh.verts[ pw[ r + 2 ] ].xyz; + tw.v[ 1 ].st = mesh.verts[ pw[ r + 2 ] ].st; + tw.v[ 2 ].xyz = mesh.verts[ pw[ r + 3 ] ].xyz; + tw.v[ 2 ].st = mesh.verts[ pw[ r + 3 ] ].st; matrix4_transform_point( transform, tw.v[ 0 ].xyz ); matrix4_transform_point( transform, tw.v[ 1 ].xyz ); matrix4_transform_point( transform, tw.v[ 2 ].xyz ); @@ -949,7 +936,7 @@ static void PopulateWithBSPModel( const bspModel_t& model, const Matrix4& transf } /* free the subdivided mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); break; } /* handle triangle surfaces */ diff --git a/tools/quake3/q3map2/light_ydnar.cpp b/tools/quake3/q3map2/light_ydnar.cpp index 3cbe4f0e..dc6fb9b4 100644 --- a/tools/quake3/q3map2/light_ydnar.cpp +++ b/tools/quake3/q3map2/light_ydnar.cpp @@ -1218,20 +1218,7 @@ void MapRawLightmap( int rawLightmapNum ){ case MST_PATCH: { /* make a mesh from the drawsurf */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = &yDrawVerts[ ds.firstVert ]; - //% mesh_t *subdivided = SubdivideMesh( src, 8, 512 ); - mesh_t *subdivided = SubdivideMesh2( src, info->patchIterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); - - /* get verts */ - const bspDrawVert_t *verts = mesh->verts; + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, &yDrawVerts[ ds.firstVert ] ), info->patchIterations ); /* debug code */ #if 0 @@ -1248,59 +1235,59 @@ void MapRawLightmap( int rawLightmapNum ){ for ( mapNonAxial = 0; mapNonAxial < 2; ++mapNonAxial ) { - for ( y = 0; y < ( mesh->height - 1 ); ++y ) + for ( y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( x = 0; x < ( mesh->width - 1 ); ++x ) + for ( x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* get drawverts and map first triangle */ MapTriangle( lm, info, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 1 ] ], - &verts[ pw[ r + 2 ] ] }, mapNonAxial ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ] }, mapNonAxial ); /* get drawverts and map second triangle */ MapTriangle( lm, info, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 2 ] ], - &verts[ pw[ r + 3 ] ] }, mapNonAxial ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] }, mapNonAxial ); } } } #else - for ( y = 0; y < ( mesh->height - 1 ); ++y ) + for ( y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( x = 0; x < ( mesh->width - 1 ); ++x ) + for ( x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* attempt to map quad first */ if ( MapQuad( lm, info, QuadRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 1 ] ], - &verts[ pw[ r + 2 ] ], - &verts[ pw[ r + 3 ] ] } ) ) { + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] } ) ) { continue; } @@ -1308,15 +1295,15 @@ void MapRawLightmap( int rawLightmapNum ){ { /* get drawverts and map first triangle */ MapTriangle( lm, info, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 1 ] ], - &verts[ pw[ r + 2 ] ] }, mapNonAxial ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ] }, mapNonAxial ); /* get drawverts and map second triangle */ MapTriangle( lm, info, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 2 ] ], - &verts[ pw[ r + 3 ] ] }, mapNonAxial ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] }, mapNonAxial ); } } } @@ -1324,7 +1311,7 @@ void MapRawLightmap( int rawLightmapNum ){ #endif /* free the mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); break; } default: diff --git a/tools/quake3/q3map2/lightmaps_ydnar.cpp b/tools/quake3/q3map2/lightmaps_ydnar.cpp index f9a15298..3a6e5eab 100644 --- a/tools/quake3/q3map2/lightmaps_ydnar.cpp +++ b/tools/quake3/q3map2/lightmaps_ydnar.cpp @@ -452,7 +452,6 @@ static void FinishRawLightmap( rawLightmap_t& lm ){ */ static bool AddPatchToRawLightmap( int num, rawLightmap_t& lm ){ - bspDrawVert_t *verts, *a, *b; float sBasis, tBasis, s, t; float length, widthTable[ MAX_EXPANDED_AXIS ] = {0}, heightTable[ MAX_EXPANDED_AXIS ] = {0}; @@ -465,43 +464,32 @@ static bool AddPatchToRawLightmap( int num, rawLightmap_t& lm ){ const surfaceInfo_t& info = surfaceInfos[ num ]; /* make a temporary mesh from the drawsurf */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = &yDrawVerts[ ds.firstVert ]; - //% mesh_t *subdivided = SubdivideMesh( src, 8, 512 ); - mesh_t *subdivided = SubdivideMesh2( src, info.patchIterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, &yDrawVerts[ ds.firstVert ] ), info.patchIterations ); /* find the longest distance on each row/column */ - verts = mesh->verts; - for ( int y = 0; y < mesh->height; ++y ) + for ( int y = 0; y < mesh.height; ++y ) { - for ( int x = 0; x < mesh->width; ++x ) + for ( int x = 0; x < mesh.width; ++x ) { /* get width */ - if ( x + 1 < mesh->width ) { - a = &verts[ ( y * mesh->width ) + x ]; - b = &verts[ ( y * mesh->width ) + x + 1 ]; - value_maximize( widthTable[ x ], (float)vector3_length( a->xyz - b->xyz ) ); + if ( x + 1 < mesh.width ) { + const bspDrawVert_t& a = mesh.verts[ ( y * mesh.width ) + x ]; + const bspDrawVert_t& b = mesh.verts[ ( y * mesh.width ) + x + 1 ]; + value_maximize( widthTable[ x ], (float)vector3_length( a.xyz - b.xyz ) ); } /* get height */ - if ( y + 1 < mesh->height ) { - a = &verts[ ( y * mesh->width ) + x ]; - b = &verts[ ( ( y + 1 ) * mesh->width ) + x ]; - value_maximize( heightTable[ y ], (float)vector3_length( a->xyz - b->xyz ) ); + if ( y + 1 < mesh.height ) { + const bspDrawVert_t& a = mesh.verts[ ( y * mesh.width ) + x ]; + const bspDrawVert_t& b = mesh.verts[ ( ( y + 1 ) * mesh.width ) + x ]; + value_maximize( heightTable[ y ], (float)vector3_length( a.xyz - b.xyz ) ); } } } /* determine lightmap width */ length = 0; - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) length += widthTable[ x ]; lm.w = lm.sampleSize != 0 ? ceil( length / lm.sampleSize ) + 1 : 0; value_maximize( lm.w, ds.patchWidth ); @@ -510,7 +498,7 @@ static bool AddPatchToRawLightmap( int num, rawLightmap_t& lm ){ /* determine lightmap height */ length = 0; - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) length += heightTable[ y ]; lm.h = lm.sampleSize != 0 ? ceil( length / lm.sampleSize ) + 1 : 0; value_maximize( lm.h, ds.patchHeight ); @@ -518,12 +506,12 @@ static bool AddPatchToRawLightmap( int num, rawLightmap_t& lm ){ tBasis = (float) ( lm.h - 1 ) / ( ds.patchHeight - 1 ); /* free the temporary mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); /* set the lightmap texture coordinates in yDrawVerts */ lm.wrap[ 0 ] = true; lm.wrap[ 1 ] = true; - verts = &yDrawVerts[ ds.firstVert ]; + bspDrawVert_t *verts = &yDrawVerts[ ds.firstVert ]; for ( int y = 0; y < ds.patchHeight; ++y ) { t = ( tBasis * y ) + 0.5f; @@ -1675,56 +1663,43 @@ static bool ApproximateLightmap( rawLightmap_t *lm ){ case MST_PATCH: { /* make a mesh from the drawsurf */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = &yDrawVerts[ ds.firstVert ]; - //% mesh_t *subdivided = SubdivideMesh( src, 8, 512 ); - mesh_t *subdivided = SubdivideMesh2( src, info.patchIterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); - - /* get verts */ - const bspDrawVert_t *verts = mesh->verts; + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, &yDrawVerts[ ds.firstVert ] ), info.patchIterations ); /* map the mesh quads */ info.approximated = true; - for ( int y = 0; y < ( mesh->height - 1 ) && info.approximated; ++y ) + for ( int y = 0; y < ( mesh.height - 1 ) && info.approximated; ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ) && info.approximated; ++x ) + for ( int x = 0; x < ( mesh.width - 1 ) && info.approximated; ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* get drawverts and map first triangle */ info.approximated = ApproximateTriangle_r( lm, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 1 ] ], - &verts[ pw[ r + 2 ] ] } ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ] } ); /* get drawverts and map second triangle */ if ( info.approximated ) { info.approximated = ApproximateTriangle_r( lm, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 2 ] ], - &verts[ pw[ r + 3 ] ] } ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] } ); } } } /* free the mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); break; } default: diff --git a/tools/quake3/q3map2/map.cpp b/tools/quake3/q3map2/map.cpp index cc9f8881..aba6766e 100644 --- a/tools/quake3/q3map2/map.cpp +++ b/tools/quake3/q3map2/map.cpp @@ -1240,7 +1240,7 @@ static void AdjustBrushesForOrigin( entity_t& ent ){ /* walk patch list */ for ( parseMesh_t& p : ent.patches ) { - for ( bspDrawVert_t& vert : Span( p.mesh.verts, p.mesh.width * p.mesh.height ) ) + for ( bspDrawVert_t& vert : Span( p.mesh.verts, p.mesh.numVerts() ) ) vert.xyz -= ent.originbrush_origin; } } @@ -1303,7 +1303,7 @@ static void SetEntityBounds( entity_t& e ){ } for ( const parseMesh_t& p : e.patches ) { - for ( const bspDrawVert_t& vert : Span( p.mesh.verts, p.mesh.width * p.mesh.height ) ) + for ( const bspDrawVert_t& vert : Span( p.mesh.verts, p.mesh.numVerts() ) ) minmax.extend( vert.xyz ); } diff --git a/tools/quake3/q3map2/mesh.cpp b/tools/quake3/q3map2/mesh.cpp index c6dab39c..a8e36d7d 100644 --- a/tools/quake3/q3map2/mesh.cpp +++ b/tools/quake3/q3map2/mesh.cpp @@ -91,33 +91,24 @@ void LerpDrawVertAmount( bspDrawVert_t *a, bspDrawVert_t *b, float amount, bspDr } -void FreeMesh( mesh_t *m ) { - free( m->verts ); - free( m ); -} - -void PrintMesh( mesh_t *m ) { - for ( int i = 0; i < m->height; ++i ) { - for ( int j = 0; j < m->width; ++j ) { +void PrintMesh( const mesh_t m ) { + for ( int i = 0; i < m.height; ++i ) { + for ( int j = 0; j < m.width; ++j ) { Sys_Printf( "(%5.2f %5.2f %5.2f) " - , m->verts[i * m->width + j].xyz[0] - , m->verts[i * m->width + j].xyz[1] - , m->verts[i * m->width + j].xyz[2] ); + , m.verts[i * m.width + j].xyz[0] + , m.verts[i * m.width + j].xyz[1] + , m.verts[i * m.width + j].xyz[2] ); } Sys_Printf( "\n" ); } + Sys_Printf( "\n" ); } -mesh_t *CopyMesh( mesh_t *mesh ) { - mesh_t *out = safe_malloc( sizeof( *out ) ); - out->width = mesh->width; - out->height = mesh->height; - - const size_t size = out->width * out->height * sizeof( *out->verts ); - out->verts = safe_malloc( size ); - memcpy( out->verts, mesh->verts, size ); - +mesh_t CopyMesh( const mesh_t m ) { + const size_t size = m.numVerts() * sizeof( *m.verts ); + mesh_t out( m.width, m.height, safe_malloc( size ) ); + memcpy( out.verts, m.verts, size ); return out; } @@ -127,29 +118,25 @@ mesh_t *CopyMesh( mesh_t *mesh ) { returns a transposed copy of the mesh, freeing the original */ -mesh_t *TransposeMesh( mesh_t *in ) { - mesh_t *out = safe_malloc( sizeof( *out ) ); - out->width = in->height; - out->height = in->width; - out->verts = safe_malloc( out->width * out->height * sizeof( bspDrawVert_t ) ); +void TransposeMesh( mesh_t& m ) { + mesh_t out( m.height, m.width, safe_malloc( m.numVerts() * sizeof( *m.verts ) ) ); // swap width/height - for ( int h = 0; h < in->height; ++h ) { - for ( int w = 0; w < in->width; ++w ) { - out->verts[ w * in->height + h ] = in->verts[ h * in->width + w ]; + for ( int h = 0; h < m.height; ++h ) { + for ( int w = 0; w < m.width; ++w ) { + out.verts[ w * m.height + h ] = m.verts[ h * m.width + w ]; } } - FreeMesh( in ); - - return out; + m.freeVerts(); + m = out; } -void InvertMesh( mesh_t *in ) { - for ( int h = 0; h < in->height; ++h ) { - for ( int w = 0; w < in->width / 2; ++w ) { +void InvertMesh( mesh_t& m ) { + for ( int h = 0; h < m.height; ++h ) { + for ( int w = 0; w < m.width / 2; ++w ) { std::swap( - in->verts[ h * in->width + w ], - in->verts[ h * in->width + in->width - 1 - w ] + m.verts[ h * m.width + w ], + m.verts[ h * m.width + m.width - 1 - w ] ); } } @@ -161,7 +148,7 @@ void InvertMesh( mesh_t *in ) { ================= */ -void MakeMeshNormals( mesh_t in ){ +void MakeMeshNormals( mesh_t& in ){ int i, j, k, dist; int count; int x, y; @@ -267,7 +254,7 @@ void MakeMeshNormals( mesh_t in ){ drops the aproximating points onto the curve */ -void PutMeshOnCurve( mesh_t in ) { +void PutMeshOnCurve( mesh_t& in ) { const auto lerp3 = []( const bspDrawVert_t& prev, bspDrawVert_t& mid, const bspDrawVert_t& next ){ mid.xyz = ( prev.xyz + mid.xyz * 2 + next.xyz ) * .25; /* ydnar: interpolating st coords */ @@ -296,19 +283,13 @@ void PutMeshOnCurve( mesh_t in ) { ================= */ -mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ){ - mesh_t out; +mesh_t SubdivideMesh( const mesh_t in, float maxError, float minLength ){ bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS]; + mesh_t out( in.width, in.height, expand[0] ); - out.width = in.width; - out.height = in.height; - - for ( int i = 0; i < in.width; ++i ) { - for ( int j = 0; j < in.height; ++j ) { - expand[j][i] = in.verts[j * in.width + i]; - } - } + for ( int h = 0; h < in.height; ++h ) + std::copy_n( &in.verts[h * in.width], in.width, expand[h] ); // horizontal subdivisions for ( int i, j = 0; j + 2 < out.width; j += 2 ) { @@ -399,12 +380,11 @@ mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ){ // collapse the verts - out.verts = &expand[0][0]; for ( int i = 1; i < out.height; ++i ) { memmove( &out.verts[i * out.width], expand[i], out.width * sizeof( bspDrawVert_t ) ); } - return CopyMesh( &out ); + return CopyMesh( out ); } @@ -438,19 +418,14 @@ int IterationsForCurve( float len, int subdivisions ){ subdivides each mesh quad a specified number of times */ -mesh_t *SubdivideMesh2( mesh_t in, int iterations ){ - mesh_t out; +mesh_t SubdivideMesh2( const mesh_t in, int iterations ){ bspDrawVert_t expand[ MAX_EXPANDED_AXIS ][ MAX_EXPANDED_AXIS ]; + mesh_t out( in.width, in.height, expand[0] ); /* initial setup */ - out.width = in.width; - out.height = in.height; - for ( int i = 0; i < in.width; ++i ) - { - for ( int j = 0; j < in.height; ++j ) - expand[ j ][ i ] = in.verts[ j * in.width + i ]; - } + for ( int h = 0; h < in.height; ++h ) + std::copy_n( &in.verts[h * in.width], in.width, expand[h] ); /* keep chopping */ for ( ; iterations > 0; --iterations ) @@ -499,12 +474,11 @@ mesh_t *SubdivideMesh2( mesh_t in, int iterations ){ } /* collapse the verts */ - out.verts = &expand[ 0 ][ 0 ]; for ( int i = 1; i < out.height; ++i ) memmove( &out.verts[ i * out.width ], expand[ i ], out.width * sizeof( bspDrawVert_t ) ); /* return to sender */ - return CopyMesh( &out ); + return CopyMesh( out ); } @@ -530,46 +504,38 @@ inline Vector3 ProjectPointOntoVector( const Vector3& point, const Vector3& vSta RemoveLinearMeshColumsRows ================ */ -mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) { - int i, j, k; - mesh_t out; - +mesh_t RemoveLinearMeshColumnsRows( const mesh_t in ) { bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS]; + mesh_t out( in.width, in.height, expand[0] ); - out.width = in->width; - out.height = in->height; + for ( int h = 0; h < in.height; ++h ) + std::copy_n( &in.verts[h * in.width], in.width, expand[h] ); - for ( i = 0; i < in->width; ++i ) { - for ( j = 0; j < in->height; ++j ) { - expand[j][i] = in->verts[j * in->width + i]; - } - } - - for ( j = 1; j < out.width - 1; ++j ) { + for ( int j = 1; j < out.width - 1; ++j ) { double maxLength = 0; - for ( i = 0; i < out.height; ++i ) { + for ( int i = 0; i < out.height; ++i ) { value_maximize( maxLength, vector3_length( expand[i][j].xyz - ProjectPointOntoVector( expand[i][j].xyz, expand[i][j - 1].xyz, expand[i][j + 1].xyz ) ) ); } if ( maxLength < 0.1 ) { out.width--; - for ( i = 0; i < out.height; ++i ) { - for ( k = j; k < out.width; ++k ) { + for ( int i = 0; i < out.height; ++i ) { + for ( int k = j; k < out.width; ++k ) { expand[i][k] = expand[i][k + 1]; } } j--; } } - for ( j = 1; j < out.height - 1; ++j ) { + for ( int j = 1; j < out.height - 1; ++j ) { double maxLength = 0; - for ( i = 0; i < out.width; ++i ) { + for ( int i = 0; i < out.width; ++i ) { value_maximize( maxLength, vector3_length( expand[j][i].xyz - ProjectPointOntoVector( expand[j][i].xyz, expand[j - 1][i].xyz, expand[j + 1][i].xyz ) ) ); } if ( maxLength < 0.1 ) { out.height--; - for ( i = 0; i < out.width; ++i ) { - for ( k = j; k < out.height; ++k ) { + for ( int i = 0; i < out.width; ++i ) { + for ( int k = j; k < out.height; ++k ) { expand[k][i] = expand[k + 1][i]; } } @@ -577,34 +543,40 @@ mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) { } } // collapse the verts - out.verts = &expand[0][0]; - for ( i = 1; i < out.height; ++i ) { + for ( int i = 1; i < out.height; ++i ) { memmove( &out.verts[i * out.width], expand[i], out.width * sizeof( bspDrawVert_t ) ); } - return CopyMesh( &out ); + return CopyMesh( out ); } +mesh_t TessellatedMesh( const mesh_t in, int iterations ){ + //% mesh_t subdivided = SubdivideMesh( in, 8, 512 ); + mesh_t subdivided = SubdivideMesh2( in, iterations ); + + /* fit it to the curve and remove colinear verts on rows/columns */ + PutMeshOnCurve( subdivided ); + mesh_t mesh = RemoveLinearMeshColumnsRows( subdivided ); + subdivided.freeVerts(); + //% MakeMeshNormals( mesh ); + + return mesh; +} + /* ================= SubdivideMeshQuads ================= */ -static mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int *widthtable, int *heighttable ){ +static mesh_t SubdivideMeshQuads( const mesh_t in, float minLength, int maxsize, int *widthtable, int *heighttable ){ int i, j, k, w, h, maxsubdivisions, subdivisions; - mesh_t out; bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS]; + mesh_t out( in.width, in.height, expand[0] ); - out.width = in->width; - out.height = in->height; - - for ( i = 0; i < in->width; ++i ) { - for ( j = 0; j < in->height; ++j ) { - expand[j][i] = in->verts[j * in->width + i]; - } - } + for ( int h = 0; h < in.height; ++h ) + std::copy_n( &in.verts[h * in.width], in.width, expand[h] ); if ( maxsize > MAX_EXPANDED_AXIS ) { Error( "SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS" ); @@ -612,9 +584,9 @@ static mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int // horizontal subdivisions - maxsubdivisions = ( maxsize - in->width ) / ( in->width - 1 ); + maxsubdivisions = ( maxsize - in.width ) / ( in.width - 1 ); - for ( w = 0, j = 0; w < in->width - 1; ++w, j += subdivisions + 1 ) { + for ( w = 0, j = 0; w < in.width - 1; ++w, j += subdivisions + 1 ) { double maxLength = 0; for ( i = 0; i < out.height; ++i ) { value_maximize( maxLength, vector3_length( expand[i][j + 1].xyz - expand[i][j].xyz ) ); @@ -641,9 +613,9 @@ static mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int } } - maxsubdivisions = ( maxsize - in->height ) / ( in->height - 1 ); + maxsubdivisions = ( maxsize - in.height ) / ( in.height - 1 ); - for ( h = 0, j = 0; h < in->height - 1; ++h, j += subdivisions + 1 ) { + for ( h = 0, j = 0; h < in.height - 1; ++h, j += subdivisions + 1 ) { double maxLength = 0; for ( i = 0; i < out.width; ++i ) { value_maximize( maxLength, vector3_length( expand[j + 1][i].xyz - expand[j][i].xyz ) ); @@ -671,10 +643,9 @@ static mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int } // collapse the verts - out.verts = &expand[0][0]; for ( i = 1; i < out.height; ++i ) { memmove( &out.verts[i * out.width], expand[i], out.width * sizeof( bspDrawVert_t ) ); } - return CopyMesh( &out ); + return CopyMesh( out ); } diff --git a/tools/quake3/q3map2/patch.cpp b/tools/quake3/q3map2/patch.cpp index dc5b3bc5..f7ef6ea7 100644 --- a/tools/quake3/q3map2/patch.cpp +++ b/tools/quake3/q3map2/patch.cpp @@ -210,8 +210,8 @@ void ParsePatch( bool onlyLights, entity_t& mapEnt, int mapPrimitiveNum ){ Parse1DMatrix( 5, info ); m.width = info[0]; m.height = info[1]; - const int size = ( m.width * m.height ); - bspDrawVert_t *verts = m.verts = safe_malloc( size * sizeof( m.verts[0] ) ); + const int numVerts = m.numVerts(); + bspDrawVert_t *verts = m.verts = safe_malloc( numVerts * sizeof( m.verts[0] ) ); if ( m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE ) { Error( "ParsePatch: bad size" ); @@ -259,7 +259,7 @@ void ParsePatch( bool onlyLights, entity_t& mapEnt, int mapPrimitiveNum ){ degenerate = true; /* find first valid vector */ - for ( int i = 1; i < size && delta[ 3 ] == 0; ++i ) + for ( int i = 1; i < numVerts && delta[ 3 ] == 0; ++i ) { delta.vec3() = m.verts[ 0 ].xyz - m.verts[ i ].xyz; delta[ 3 ] = VectorNormalize( delta.vec3() ); @@ -272,7 +272,7 @@ void ParsePatch( bool onlyLights, entity_t& mapEnt, int mapPrimitiveNum ){ else { /* if all vectors match this or are zero, then this is a degenerate patch */ - for ( int i = 1; i < size && degenerate; ++i ) + for ( int i = 1; i < numVerts && degenerate; ++i ) { Vector4 delta2( m.verts[ 0 ].xyz - m.verts[ i ].xyz, 0 ); delta2[ 3 ] = VectorNormalize( delta2.vec3() ); @@ -292,7 +292,7 @@ void ParsePatch( bool onlyLights, entity_t& mapEnt, int mapPrimitiveNum ){ /* warn and select degenerate patch */ if ( degenerate ) { xml_Select( "degenerate patch", mapEnt.mapEntityNum, mapPrimitiveNum, false ); - free( m.verts ); + m.freeVerts(); return; } @@ -400,8 +400,8 @@ void PatchMapDrawSurfs( entity_t& e ){ meshes[m1].bordering[m2] = meshes[m2].bordering[m1] = - std::ranges::any_of( Span( mesh1.verts, mesh1.width * mesh1.height ), [mesh2]( const bspDrawVert_t& v1 ){ - return std::ranges::any_of( Span( mesh2.verts, mesh2.width * mesh2.height ), [v1]( const bspDrawVert_t& v2 ){ + std::ranges::any_of( Span( mesh1.verts, mesh1.numVerts() ), [mesh2]( const bspDrawVert_t& v1 ){ + return std::ranges::any_of( Span( mesh2.verts, mesh2.numVerts() ), [v1]( const bspDrawVert_t& v2 ){ return vector3_equal_epsilon( v1.xyz, v2.xyz, 1.f ); } ); } ); @@ -428,7 +428,7 @@ void PatchMapDrawSurfs( entity_t& e ){ { if ( m.group ) { m.grouped = true; - std::for_each_n( m.mesh.mesh.verts, m.mesh.mesh.width * m.mesh.mesh.height, + std::for_each_n( m.mesh.mesh.verts, m.mesh.mesh.numVerts(), [&bounds]( const bspDrawVert_t& v ){ bounds.extend( v.xyz ); } ); } } diff --git a/tools/quake3/q3map2/q3map2.h b/tools/quake3/q3map2/q3map2.h index 44e1cabc..7e1df53a 100644 --- a/tools/quake3/q3map2/q3map2.h +++ b/tools/quake3/q3map2/q3map2.h @@ -762,6 +762,15 @@ struct mesh_t { int width, height; bspDrawVert_t *verts; + + mesh_t() = default; + mesh_t( int width, int height, bspDrawVert_t *verts ) : width( width ), height( height ), verts( verts ){} + size_t numVerts() const { + return width * height; + } + void freeVerts(){ + free( verts ); + } }; @@ -1513,17 +1522,17 @@ bool WindingIsTiny( const winding_t& w ); /* mesh.c */ bspDrawVert_t LerpDrawVert( const bspDrawVert_t& a, const bspDrawVert_t& b ); void LerpDrawVertAmount( bspDrawVert_t *a, bspDrawVert_t *b, float amount, bspDrawVert_t *out ); -void FreeMesh( mesh_t *m ); -mesh_t *CopyMesh( mesh_t *mesh ); -void PrintMesh( mesh_t *m ); -mesh_t *TransposeMesh( mesh_t *in ); -void InvertMesh( mesh_t *m ); -mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ); +mesh_t CopyMesh( const mesh_t m ); +void PrintMesh( const mesh_t m ); +void TransposeMesh( mesh_t& m ); +void InvertMesh( mesh_t& m ); +mesh_t SubdivideMesh( const mesh_t in, float maxError, float minLength ); int IterationsForCurve( float len, int subdivisions ); -mesh_t *SubdivideMesh2( mesh_t in, int iterations ); -mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ); -void MakeMeshNormals( mesh_t in ); -void PutMeshOnCurve( mesh_t in ); +mesh_t SubdivideMesh2( const mesh_t in, int iterations ); +mesh_t RemoveLinearMeshColumnsRows( const mesh_t in ); +mesh_t TessellatedMesh( const mesh_t in, int iterations ); +void MakeMeshNormals( mesh_t& in ); +void PutMeshOnCurve( mesh_t& in ); /* map.c */ diff --git a/tools/quake3/q3map2/surface.cpp b/tools/quake3/q3map2/surface.cpp index 44f6a836..1fabc30c 100644 --- a/tools/quake3/q3map2/surface.cpp +++ b/tools/quake3/q3map2/surface.cpp @@ -773,7 +773,6 @@ mapDrawSurface_t *DrawSurfaceForSide( const entity_t& e, const brush_t& b, const mapDrawSurface_t *DrawSurfaceForMesh( const entity_t& e, parseMesh_t& p, mesh_t *mesh ){ Plane3f plane; shaderInfo_t *si, *parent; - mesh_t *copy; byte shaderIndexes[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ]; float offsets[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ]; @@ -788,35 +787,35 @@ mapDrawSurface_t *DrawSurfaceForMesh( const entity_t& e, parseMesh_t& p, mesh_t } /* get vertex count */ - const int numVerts = mesh->width * mesh->height; + const int numVerts = mesh->numVerts(); /* to make valid normals for patches with degenerate edges, we need to make a copy of the mesh and put the aproximating points onto the curve */ /* create a copy of the mesh */ - copy = CopyMesh( mesh ); + mesh_t copy = CopyMesh( *mesh ); /* store off the original (potentially bad) normals */ - MakeMeshNormals( *copy ); + MakeMeshNormals( copy ); for ( int i = 0; i < numVerts; ++i ) - mesh->verts[ i ].normal = copy->verts[ i ].normal; + mesh->verts[ i ].normal = copy.verts[ i ].normal; /* put the mesh on the curve */ - PutMeshOnCurve( *copy ); + PutMeshOnCurve( copy ); /* find new normals (to take into account degenerate/flipped edges */ - MakeMeshNormals( *copy ); + MakeMeshNormals( copy ); for ( int i = 0; i < numVerts; ++i ) { /* ydnar: only copy normals that are significantly different from the originals */ - if ( vector3_dot( copy->verts[ i ].normal, mesh->verts[ i ].normal ) < 0.75f ) { - mesh->verts[ i ].normal = copy->verts[ i ].normal; + if ( vector3_dot( copy.verts[ i ].normal, mesh->verts[ i ].normal ) < 0.75f ) { + mesh->verts[ i ].normal = copy.verts[ i ].normal; } } /* free the old mesh */ - FreeMesh( copy ); + copy.freeVerts(); /* ydnar: gs mods: check for indexed shader */ const bool indexed = ( si->indexed && p.im != nullptr ); @@ -2844,40 +2843,29 @@ static int AddSurfaceModels( mapDrawSurface_t& ds, entity_t& entity ){ case ESurfaceType::Patch: { /* subdivide the surface */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = ds.verts.data(); - //% mesh_t *subdivided = SubdivideMesh( src, 8.0f, 512 ); - const int iterations = IterationsForCurve( ds.longestCurve, patchSubdivisions ); - mesh_t *subdivided = SubdivideMesh2( src, iterations ); - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, ds.verts.data() ), IterationsForCurve( ds.longestCurve, patchSubdivisions ) ); /* subdivide each quad to place the models */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ), /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ), /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* triangle 1 */ const int n = AddSurfaceModelsToTriangle_r( ds, model, TriRef{ - &mesh->verts[ pw[ r + 0 ] ], - &mesh->verts[ pw[ r + 1 ] ], - &mesh->verts[ pw[ r + 2 ] ] }, entity ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ] }, entity ); if ( n < 0 ) { return n; } @@ -2885,9 +2873,9 @@ static int AddSurfaceModels( mapDrawSurface_t& ds, entity_t& entity ){ /* triangle 2 */ const int n2 = AddSurfaceModelsToTriangle_r( ds, model, TriRef{ - &mesh->verts[ pw[ r + 0 ] ], - &mesh->verts[ pw[ r + 2 ] ], - &mesh->verts[ pw[ r + 3 ] ] }, entity ); + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] }, entity ); if ( n2 < 0 ) { return n2; } @@ -2896,7 +2884,7 @@ static int AddSurfaceModels( mapDrawSurface_t& ds, entity_t& entity ){ } /* free the subdivided mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); break; } /* handle triangle surfaces */ diff --git a/tools/quake3/q3map2/surface_foliage.cpp b/tools/quake3/q3map2/surface_foliage.cpp index 8caef37c..bdbfe3b8 100644 --- a/tools/quake3/q3map2/surface_foliage.cpp +++ b/tools/quake3/q3map2/surface_foliage.cpp @@ -186,53 +186,47 @@ void Foliage( mapDrawSurface_t& src, entity_t& entity ){ case ESurfaceType::Patch: { /* make a mesh from the drawsurf */ - mesh_t srcMesh; - srcMesh.width = src.patchWidth; - srcMesh.height = src.patchHeight; - srcMesh.verts = src.verts.data(); - mesh_t *subdivided = SubdivideMesh( srcMesh, 8, 512 ); + mesh_t srcMesh( src.patchWidth, src.patchHeight, src.verts.data() ); + mesh_t subdivided = SubdivideMesh( srcMesh, 8, 512 ); /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); - - /* get verts */ - const bspDrawVert_t *verts = mesh->verts; + PutMeshOnCurve( subdivided ); + mesh_t mesh = RemoveLinearMeshColumnsRows( subdivided ); + subdivided.freeVerts(); /* map the mesh quads */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; /* get drawverts and map first triangle */ SubdivideFoliageTriangle_r( foliage, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 1 ] ], - &verts[ pw[ r + 2 ] ] + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 1 ] ], + &mesh.verts[ pw[ r + 2 ] ] } ); /* get drawverts and map second triangle */ SubdivideFoliageTriangle_r( foliage, TriRef{ - &verts[ pw[ r + 0 ] ], - &verts[ pw[ r + 2 ] ], - &verts[ pw[ r + 3 ] ] + &mesh.verts[ pw[ r + 0 ] ], + &mesh.verts[ pw[ r + 2 ] ], + &mesh.verts[ pw[ r + 3 ] ] } ); } } /* free the mesh */ - FreeMesh( mesh ); + mesh.freeVerts(); break; } default: diff --git a/tools/quake3/q3map2/surface_meta.cpp b/tools/quake3/q3map2/surface_meta.cpp index f5ea6db3..a73e8e72 100644 --- a/tools/quake3/q3map2/surface_meta.cpp +++ b/tools/quake3/q3map2/surface_meta.cpp @@ -361,12 +361,6 @@ static void TriangulatePatchSurface( const entity_t& e, mapDrawSurface_t& ds ){ if ( ds.verts.empty() || ds.type != ESurfaceType::Patch || ( !patchMeta && !forcePatchMeta ) ) { return; } - /* make a mesh from the drawsurf */ - mesh_t src; - src.width = ds.patchWidth; - src.height = ds.patchHeight; - src.verts = ds.verts.data(); - //% mesh_t *subdivided = SubdivideMesh( src, 8, 999 ); int iterations; if ( int patchSubdivision; e.read_keyvalue( patchSubdivision, "_patchSubdivide", "patchSubdivide" ) ) { @@ -376,14 +370,8 @@ static void TriangulatePatchSurface( const entity_t& e, mapDrawSurface_t& ds ){ const int patchQuality = e.intForKey( "_patchQuality", "patchQuality" ); iterations = IterationsForCurve( ds.longestCurve, patchSubdivisions / ( patchQuality == 0? 1 : patchQuality ) ); } - - mesh_t *subdivided = SubdivideMesh2( src, iterations ); //% ds.maxIterations - - /* fit it to the curve and remove colinear verts on rows/columns */ - PutMeshOnCurve( *subdivided ); - mesh_t *mesh = RemoveLinearMeshColumnsRows( subdivided ); - FreeMesh( subdivided ); - //% MakeMeshNormals( mesh ); + /* make a mesh from the drawsurf */ + mesh_t mesh = TessellatedMesh( mesh_t( ds.patchWidth, ds.patchHeight, ds.verts.data() ), iterations ); //% ds.maxIterations /* make a copy of the drawsurface */ mapDrawSurface_t& dsNew = AllocDrawSurface( ESurfaceType::Meta ); @@ -397,23 +385,23 @@ static void TriangulatePatchSurface( const entity_t& e, mapDrawSurface_t& ds ){ /* basic transmogrification */ dsNew.type = ESurfaceType::Meta; dsNew.indexes.clear(); - dsNew.indexes.reserve( ( mesh->width - 1 ) * ( mesh->height - 1 ) * 6 ); + dsNew.indexes.reserve( ( mesh.width - 1 ) * ( mesh.height - 1 ) * 6 ); /* copy the verts in */ - dsNew.verts.assign( mesh->verts, mesh->verts + mesh->width * mesh->height ); + dsNew.verts.assign( mesh.verts, mesh.verts + mesh.numVerts() ); /* iterate through the mesh quads */ - for ( int y = 0; y < ( mesh->height - 1 ); ++y ) + for ( int y = 0; y < ( mesh.height - 1 ); ++y ) { - for ( int x = 0; x < ( mesh->width - 1 ); ++x ) + for ( int x = 0; x < ( mesh.width - 1 ); ++x ) { /* set indexes */ const int pw[ 5 ] = { - x + ( y * mesh->width ), - x + ( ( y + 1 ) * mesh->width ), - x + 1 + ( ( y + 1 ) * mesh->width ), - x + 1 + ( y * mesh->width ), - x + ( y * mesh->width ) /* same as pw[ 0 ] */ + x + ( y * mesh.width ), + x + ( ( y + 1 ) * mesh.width ), + x + 1 + ( ( y + 1 ) * mesh.width ), + x + 1 + ( y * mesh.width ), + x + ( y * mesh.width ) /* same as pw[ 0 ] */ }; /* set radix */ const int r = ( x + y ) & 1; @@ -430,7 +418,7 @@ static void TriangulatePatchSurface( const entity_t& e, mapDrawSurface_t& ds ){ } } - FreeMesh( mesh ); + mesh.freeVerts(); /* add to count */ numPatchMetaSurfaces++; diff --git a/tools/quake3/q3map2/writebsp.cpp b/tools/quake3/q3map2/writebsp.cpp index 2e70835c..b76ccc31 100644 --- a/tools/quake3/q3map2/writebsp.cpp +++ b/tools/quake3/q3map2/writebsp.cpp @@ -495,7 +495,7 @@ void BeginModel( const entity_t& e ){ /* bound patches */ for ( const parseMesh_t& p : e.patches ) { - for ( const bspDrawVert_t& vert : Span( p.mesh.verts, p.mesh.width * p.mesh.height ) ) + for ( const bspDrawVert_t& vert : Span( p.mesh.verts, p.mesh.numVerts() ) ) minmax.extend( vert.xyz ); }