Skip to content

Commit

Permalink
* fix deformvertexes autosprite2 on brushes and models
Browse files Browse the repository at this point in the history
broken for brushes in c166e42 by new triangualtion
broken for models forever
fixes TTimo/GtkRadiant#658
  • Loading branch information
Garux committed Dec 17, 2024
1 parent 44006f8 commit dbcdb40
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 8 deletions.
2 changes: 2 additions & 0 deletions docs/changelog-custom.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <surfaceparm>: add surface flag to every bsp shader. Reusable, e.g. -globalflag slick -globalflag nodamage.
* fix deformvertexes autosprite2 on brushes and models



Expand All @@ -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



Expand Down
101 changes: 93 additions & 8 deletions tools/quake3/q3map2/surface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,89 @@ static void EmitPatchSurface( const entity_t& e, mapDrawSurface_t *ds ){
numSurfacesByType[ static_cast<std::size_t>( 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<int, 6>{ 3, 0, 2, 2, 0, 1 }.data(), 6, ds->indexes );
}

/*
OptimizeTriangleSurface() - ydnar
optimizes the vertex/index data in a triangle surface
Expand Down Expand Up @@ -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 */
Expand All @@ -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;
Expand Down Expand Up @@ -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 ] ];
Expand All @@ -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;
Expand Down

0 comments on commit dbcdb40

Please sign in to comment.