From dbcdb4044eaab6627af66d75dfb8c8855ba779cb Mon Sep 17 00:00:00 2001 From: Garux Date: Tue, 17 Dec 2024 09:29:30 +0500 Subject: [PATCH] * fix deformvertexes autosprite2 on brushes and models broken for brushes in c166e4214eb529d502658e55dfde3ffaa77116aa by new triangualtion broken for models forever fixes https://github.com/TTimo/GtkRadiant/issues/658 --- docs/changelog-custom.txt | 2 + tools/quake3/q3map2/surface.cpp | 101 +++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/docs/changelog-custom.txt b/docs/changelog-custom.txt index ccccd6df..014230f1 100644 --- a/docs/changelog-custom.txt +++ b/docs/changelog-custom.txt @@ -178,6 +178,7 @@ Q3map2: * -nobrushsnap: disable brush vertices snapping (of drawsurfs) (experimental, keep eye on meta merge efficiency) * improved brush faces triangulation wrt degenerate triangles production (non -maxarea algorithm) * -globalflag : add surface flag to every bsp shader. Reusable, e.g. -globalflag slick -globalflag nodamage. + * fix deformvertexes autosprite2 on brushes and models @@ -195,6 +196,7 @@ MBSPC: * append _decompiled suffix to decompiled map file names * improve decompilation quality of Q1, HL maps greatly (texturing correctness, less brushes) * -lessbrushes switch: less brushes when decompiling Q1, HL maps at the expense of texturing + * fix build speed penalty due to terminal output diff --git a/tools/quake3/q3map2/surface.cpp b/tools/quake3/q3map2/surface.cpp index b1198497..28553bff 100644 --- a/tools/quake3/q3map2/surface.cpp +++ b/tools/quake3/q3map2/surface.cpp @@ -2258,6 +2258,89 @@ static void EmitPatchSurface( const entity_t& e, mapDrawSurface_t *ds ){ numSurfacesByType[ static_cast( ds->type ) ]++; } +/* + Autosprite2Deform() function in vanilla Q3 engine and most of sourceports + produces inconsistent results, which depend on vertex order and indexing. + Try to please that windy lady. +*/ +static void FixAutosprite2Surface( mapDrawSurface_t *ds ){ + if( ds->numVerts != 4 || ds->numIndexes != 6 ){ + Sys_Warning( "autosprite2 surface: ds->numVerts != 4 or ds->numIndexes != 6: must be simple rectangle\n" ); + return; + } + + Plane3f plane; + if( !PlaneFromPoints( plane, ds->verts[ds->indexes[0]].xyz, ds->verts[ds->indexes[1]].xyz, ds->verts[ds->indexes[2]].xyz ) ){ + Sys_Warning( "autosprite2 surface: degenerate triangle\n" ); + return; + } + + // reproduce Autosprite2Deform() calculations + const int edgeVerts[6][2] = { + { 0, 1 }, + { 0, 2 }, + { 0, 3 }, + { 1, 2 }, + { 1, 3 }, + { 2, 3 } + }; + float lengths[2] = { 999999, 999999 }; + int edgeIdx[2] = {0}; + + // identify the two shortest edges + for ( int j = 0; j < 6; ++j ) { + const float l = vector3_length_squared( ds->verts[edgeVerts[j][0]].xyz - ds->verts[edgeVerts[j][1]].xyz ); + + if ( l < lengths[0] ) { + edgeIdx[1] = edgeIdx[0]; + lengths[1] = lengths[0]; + edgeIdx[0] = j; + lengths[0] = l; + } else if ( l < lengths[1] ) { + edgeIdx[1] = j; + lengths[1] = l; + } + } + // ref edges + const bspDrawVert_t *edges[2][2] = { { ds->verts + edgeVerts[edgeIdx[0]][0], ds->verts + edgeVerts[edgeIdx[0]][1] }, + { ds->verts + edgeVerts[edgeIdx[1]][0], ds->verts + edgeVerts[edgeIdx[1]][1] } }; + + if( edges[0][0] == edges[1][0] + || edges[0][0] == edges[1][1] + || edges[0][1] == edges[1][0] + || edges[0][1] == edges[1][1] ){ + Sys_Warning( "autosprite2 surface: two shortest edges share a vertex\n" ); // note also fails on exact square + return; + } + + // find the midpoints + const Vector3 mid[2] = { vector3_mid( edges[0][0]->xyz, edges[0][1]->xyz ), + vector3_mid( edges[1][0]->xyz, edges[1][1]->xyz ) }; + + // find the vector of the major axis + const Vector3 major = mid[1] - mid[0]; + + // cross this with the view direction to get minor axis + const Vector3 minor = vector3_cross( major, -plane.normal() ); + + /* the rest of Autosprite2Deform() algorithm is srsly hot trash + thus simply force the order and indexing, which are known as working */ + +/* 1-----------2 C C 1-----------2 A ^minor + | | | | | + B 0-----------3 A B 0-----------3 -----------> major */ + + if( vector3_dot( edges[0][1]->xyz - edges[0][0]->xyz, minor ) < 0 ) + std::swap( edges[0][0], edges[0][1] ); + if( vector3_dot( edges[1][1]->xyz - edges[1][0]->xyz, minor ) > 0 ) + std::swap( edges[1][0], edges[1][1] ); + + const bspDrawVert_t outverts[4] = { *edges[0][0], *edges[0][1], *edges[1][0], *edges[1][1] }; + std::copy_n( outverts, 4, ds->verts ); + + std::copy_n( std::array{ 3, 0, 2, 2, 0, 1 }.data(), 6, ds->indexes ); +} + /* OptimizeTriangleSurface() - ydnar optimizes the vertex/index data in a triangle surface @@ -2383,16 +2466,12 @@ static void OptimizeTriangleSurface( mapDrawSurface_t *ds ){ */ static void EmitTriangleSurface( mapDrawSurface_t *ds ){ - int i, temp; - /* invert the surface if necessary */ if ( ds->backSide || ds->shaderInfo->invert ) { /* walk the indexes, reverse the triangle order */ - for ( i = 0; i < ds->numIndexes; i += 3 ) + for ( int i = 0; i < ds->numIndexes; i += 3 ) { - temp = ds->indexes[ i ]; - ds->indexes[ i ] = ds->indexes[ i + 1 ]; - ds->indexes[ i + 1 ] = temp; + std::swap( ds->indexes[ i ], ds->indexes[ i + 1 ] ); } /* walk the verts, flip the normal */ @@ -2403,6 +2482,12 @@ static void EmitTriangleSurface( mapDrawSurface_t *ds ){ vector3_negate( ds->lightmapVecs[ 2 ] ); } + if( ds->shaderInfo->autosprite + && ds->shaderInfo->shaderText != nullptr + && strIstr( ds->shaderInfo->shaderText, "autosprite2" ) != nullptr ){ + FixAutosprite2Surface( ds ); + } + /* allocate a new surface */ bspDrawSurface_t& out = bspDrawSurfaces.emplace_back(); ds->outputNum = bspDrawSurfaces.size() - 1; @@ -2443,7 +2528,7 @@ static void EmitTriangleSurface( mapDrawSurface_t *ds ){ bspDrawVert_t *a, *b, *c; /* walk triangle list */ - for ( i = 0; i < ds->numIndexes; i += 3 ) + for ( int i = 0; i < ds->numIndexes; i += 3 ) { /* get verts */ a = &ds->verts[ ds->indexes[ i ] ]; @@ -2461,7 +2546,7 @@ static void EmitTriangleSurface( mapDrawSurface_t *ds ){ } /* RBSP */ - for ( i = 0; i < MAX_LIGHTMAPS; i++ ) + for ( int i = 0; i < MAX_LIGHTMAPS; i++ ) { out.lightmapNum[ i ] = -3; out.lightmapStyles[ i ] = LS_NONE;