diff --git a/tools/quake3/q3map2/model.cpp b/tools/quake3/q3map2/model.cpp index eefde33b..3080ec89 100644 --- a/tools/quake3/q3map2/model.cpp +++ b/tools/quake3/q3map2/model.cpp @@ -354,31 +354,651 @@ std::vector LoadModelWalker( const char *name, int frame ) +enum EModelFlags{ + eRMG_BSP = 1 << 0, + eClipModel = 1 << 1, + eForceMeta = 1 << 2, + eExtrudeFaceNormals = 1 << 3, + eExtrudeTerrain = 1 << 4, + eColorToAlpha = 1 << 5, + eNoSmooth = 1 << 6, + eExtrudeVertexNormals = 1 << 7, + ePyramidaClip = 1 << 8, + eExtrudeDownwards = 1 << 9, + eExtrudeUpwards = 1 << 10, + eMaxExtrude = 1 << 11, + eAxialBackplane = 1 << 12, + eClipFlags = eClipModel | eExtrudeFaceNormals | eExtrudeTerrain | eExtrudeVertexNormals | ePyramidaClip | eExtrudeDownwards | eExtrudeUpwards | eMaxExtrude | eAxialBackplane, +}; + +inline void nonax_clip_dbg( const Plane3f (&p)[3] ){ +#if 0 + for ( int j = 0; j < 3; ++j ){ + for ( int k = 0; k < 3; ++k ){ + const Vector3& n = p[j].normal(); + if ( fabs( n[k] ) < 0.00025 && n[k] != 0 ){ + Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n", n[0], n[1], n[2] ); + } + } + } +#endif +} + +inline size_t normal_make_axial( Vector3& normal ){ + const size_t i = vector3_max_abs_component_index( normal ); + normal = normal[i] >= 0? g_vector3_axes[i] : -g_vector3_axes[i]; + return i; +} + +template // N = 4 or 5 +static void make_brush_sides( const Plane3f plane, const Plane3f (&p)[3], const Plane3f& reverse, Vector3 (&points)[4], shaderInfo_t *si ){ + /* set up brush sides */ + buildBrush.sides.clear(); // clear, so resize() will value-initialize elements + buildBrush.sides.resize( N ); + + if( debugClip ){ + buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); + for ( size_t i = 1; i < N; ++i ) + buildBrush.sides[i].shaderInfo = ShaderInfoForShader( "debugclip" ); // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works + } + else{ + buildBrush.sides[0].shaderInfo = si; + buildBrush.sides[0].surfaceFlags = si->surfaceFlags; + for ( size_t i = 1; i < N; ++i ) + buildBrush.sides[i].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works + } + + points[3] = points[0]; // for cyclic usage + + buildBrush.sides[0].planenum = FindFloatPlane( plane, 3, points ); + buildBrush.sides[1].planenum = FindFloatPlane( p[0], 2, &points[0] ); // p[0] contains points[0] and points[1] + buildBrush.sides[2].planenum = FindFloatPlane( p[1], 2, &points[1] ); // p[1] contains points[1] and points[2] + buildBrush.sides[3].planenum = FindFloatPlane( p[2], 2, &points[2] ); // p[2] contains points[2] and points[0] (copied to points[3] + if constexpr( N == 5 ) + buildBrush.sides[4].planenum = FindFloatPlane( reverse, 0, NULL ); +} + +static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t *si, const mapDrawSurface_t *ds ){ + const int spf = ( spawnFlags & ( eClipFlags & ~eClipModel ) ); + + /* ydnar: giant hack land: generate clipping brushes for model triangles */ + if ( ( si->clipModel && spf == 0 ) // default CLIPMODEL + || ( spawnFlags & eClipFlags ) == eClipModel //default CLIPMODEL + || spf == eExtrudeFaceNormals + || spf == eExtrudeTerrain + || spf == eExtrudeVertexNormals + || spf == ePyramidaClip + || spf == eExtrudeDownwards + || spf == eExtrudeUpwards + || spf == eAxialBackplane // default sides + axial backplane + || spf == ( eExtrudeFaceNormals | ePyramidaClip ) // extrude 45 + || spf == ( eExtrudeTerrain | eMaxExtrude ) + || spf == ( eExtrudeTerrain | eAxialBackplane ) + || spf == ( eExtrudeVertexNormals | ePyramidaClip ) // vertex normals + don't check for sides, sticking outwards + || spf == ( ePyramidaClip | eAxialBackplane ) // pyramid with 3 of 4 sides axial (->small bsp) + || spf == ( eExtrudeDownwards | eExtrudeUpwards ) + || spf == ( eExtrudeDownwards | eMaxExtrude ) + || spf == ( eExtrudeDownwards | eAxialBackplane ) + || spf == ( eExtrudeDownwards | eExtrudeUpwards | eMaxExtrude ) + || spf == ( eExtrudeDownwards | eExtrudeUpwards | eAxialBackplane ) + || spf == ( eExtrudeUpwards | eMaxExtrude ) + || spf == ( eExtrudeUpwards | eAxialBackplane ) ){ + int i, j, k; + //int ok=0, notok=0; + float limDepth = 0; + if ( clipDepth < 0 ){ + limDepth = -clipDepth; + clipDepth = 2.0; + } + Vector3 points[ 4 ]; + Plane3f plane, reverse, p[3]; + MinMax minmax; + Vector3 avgDirection( 0 ); + int axis; + + /* temp hack */ + if ( !si->clipModel && !( si->compileFlags & C_SOLID ) ) { + return; + } + + //wont snap these in normal way, or will explode + // const double normalEpsilon_save = normalEpsilon; + //normalEpsilon = 0.000001; + + + if ( ( spf & eMaxExtrude ) || ( spf & eExtrudeTerrain ) ){ + + for ( i = 0; i < ds->numIndexes; i += 3 ){ + for ( j = 0; j < 3; ++j ){ + points[j] = ds->verts[ds->indexes[i + j]].xyz; + } + if ( PlaneFromPoints( plane, points ) ){ + if ( spf & eExtrudeTerrain ) + avgDirection += plane.normal(); //calculate average mesh facing direction + + //get min/max + for ( j = 0; j < 3; ++j ){ + minmax.extend( points[j] ); + } + } + } + //unify avg direction + if ( spf & eExtrudeTerrain ){ + if ( vector3_length( avgDirection ) == 0 ) + avgDirection = g_vector3_axis_z; + axis = normal_make_axial( avgDirection ); + } + } + + /* prepare a brush */ + buildBrush.sides.reserve( MAX_BUILD_SIDES ); + buildBrush.entityNum = mapEntityNum; + buildBrush.contentShader = si; + buildBrush.compileFlags = si->compileFlags; + buildBrush.contentFlags = si->contentFlags; + buildBrush.detail = true; + + /* walk triangle list */ + for ( i = 0; i < ds->numIndexes; i += 3 ){ + /* make points */ + for ( j = 0; j < 3; ++j ){ + /* copy xyz */ + points[j] = ds->verts[ds->indexes[i + j] ].xyz; + } + + /* make plane for triangle */ + if ( PlaneFromPoints( plane, points ) ) { + //snap points before using them for further calculations + //precision suffers a lot, when two of normal values are under .00025 (often no collision, knocking up effect in ioq3) + //also broken drawsurfs in case of normal brushes + bool snpd = false; + for ( j = 0; j < 3; ++j ) + { + if ( fabs( plane.normal()[j] ) < 0.00025 && fabs( plane.normal()[( j + 1) % 3] ) < 0.00025 + && ( plane.normal()[j] != 0.0 || plane.normal()[( j + 1 ) % 3] != 0.0 ) ){ + const Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; + points[0][( j + 2 ) % 3] = points[1][(j + 2 ) % 3] = points[2][( j + 2 ) % 3] = cnt[( j + 2 ) % 3]; + snpd = true; + break; + } + } + + //snap pairs of points to prevent bad side planes + for ( j = 0; j < 3; ++j ) + { + const Vector3 nrm = VectorNormalized( points[j] - points[( j + 1 ) % 3] ); + for ( k = 0; k < 3; ++k ) + { + if ( nrm[k] != 0.0 && fabs( nrm[k] ) < 0.00025 ){ + //Sys_Printf( "b4(%6.6f %6.6f %6.6f)(%6.6f %6.6f %6.6f)\n", points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2] ); + points[j][k] = points[( j + 1 ) % 3][k] = ( points[j][k] + points[( j + 1 ) % 3][k] ) / 2.0; + //Sys_Printf( "sn(%6.6f %6.6f %6.6f)(%6.6f %6.6f %6.6f)\n", points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2] ); + snpd = true; + } + } + } + + if ( snpd ) { + PlaneFromPoints( plane, points ); + snpd = false; + } + + //vector-is-close-to-be-on-axis check again, happens after previous code sometimes + for ( j = 0; j < 3; ++j ) + { + if ( fabs( plane.normal()[j] ) < 0.00025 && fabs( plane.normal()[( j + 1 ) % 3] ) < 0.00025 + && ( plane.normal()[j] != 0.0 || plane.normal()[( j + 1 ) % 3] != 0.0 ) ){ + const Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; + points[0][( j + 2 ) % 3] = points[1][( j + 2 ) % 3] = points[2][( j + 2 ) % 3] = cnt[( j + 2 ) % 3]; + PlaneFromPoints( plane, points ); + break; + } + } + + //snap single snappable normal components + for ( j = 0; j < 3; ++j ) + { + if ( plane.normal()[j] != 0 && fabs( plane.normal()[j] ) < 0.00005 ){ + plane.normal()[j] = 0; + snpd = true; + } + } + + //adjust plane dist + if ( snpd ) { + const Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; + VectorNormalize( plane.normal() ); + plane.dist() = vector3_dot( plane.normal(), cnt ); + + //project points to resulting plane to keep intersections precision + for ( j = 0; j < 3; ++j ) + { + //Sys_Printf( "b4 %i (%6.7f %6.7f %6.7f)\n", j, points[j][0], points[j][1], points[j][2] ); + points[j] = plane3_project_point( plane, points[j] ); + //Sys_Printf( "sn %i (%6.7f %6.7f %6.7f)\n", j, points[j][0], points[j][1], points[j][2] ); + } + //Sys_Printf( "sn pln (%6.7f %6.7f %6.7f %6.7f)\n", plane.a, plane.b, plane.c, plane.d ); + //PlaneFromPoints( plane, points ); + //Sys_Printf( "pts pln (%6.7f %6.7f %6.7f %6.7f)\n", plane.a, plane.b, plane.c, plane.d ); + } + + /* sanity check */ + { + const Vector3 d1 = points[1] - points[0]; + const Vector3 d2 = points[2] - points[0]; + const Vector3 normal = vector3_cross( d2, d1 ); + /* https://en.wikipedia.org/wiki/Cross_product#Geometric_meaning + cross( a, b ).length = a.length b.length sin( angle ) */ + const double lengthsSquared = vector3_length_squared( d1 ) * vector3_length_squared( d2 ); + if ( lengthsSquared == 0 || ( vector3_length_squared( normal ) / lengthsSquared ) < 1e-8 ) { + Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped: points on line\n", + points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], name ); + continue; + } + } + + + if ( spf == ( ePyramidaClip | eAxialBackplane ) ){ // pyramid with 3 of 4 sides axial (->small bsp) + + for ( j = 0; j < 3; ++j ) + if ( fabs( plane.normal()[j] ) < 0.05 && fabs( plane.normal()[( j + 1 ) % 3] ) < 0.05 ) //no way, close to lay on two axes + goto default_CLIPMODEL; + + // best axial normal + Vector3 bestNormal = plane.normal(); + axis = normal_make_axial( bestNormal ); + + float mindist = 999999; + + for ( j = 0; j < 3; ++j ){ // planes + float bestdist = 999999, bestangle = 1; + + for ( k = 0; k < 3; ++k ){ // axes + Vector3 nrm = points[( j + 1 ) % 3] - points[j]; + if ( k == axis ){ + reverse.normal() = vector3_cross( bestNormal, nrm ); + } + else{ + Vector3 vnrm( 0 ); + if ( ( k + 1 ) % 3 == axis ){ + if ( nrm[( k + 2 ) % 3] == 0 ) + continue; + vnrm[( k + 2 ) % 3] = nrm[( k + 2 ) % 3]; + } + else{ + if ( nrm[( k + 1 ) % 3] == 0 ) + continue; + vnrm[( k + 1 ) % 3] = nrm[( k + 1 ) % 3]; + } + const Vector3 enrm = vector3_cross( bestNormal, vnrm ); + reverse.normal() = vector3_cross( enrm, nrm ); + } + VectorNormalize( reverse.normal() ); + reverse.dist() = vector3_dot( points[ j ], reverse.normal() ); + //check facing, thickness + const float currdist = reverse.dist() - vector3_dot( reverse.normal(), points[( j + 2 ) % 3] ); + const float currangle = vector3_dot( reverse.normal(), plane.normal() ); + if ( ( ( currdist > 0.1 ) && ( currdist < bestdist ) && ( currangle < 0 ) ) || + ( ( currangle >= 0 ) && ( currangle <= bestangle ) ) ){ + bestangle = currangle; + if ( currangle < 0 ) + bestdist = currdist; + p[j] = reverse; + } + } + if ( bestdist == 999999 && bestangle == 1 ){ + // Sys_Printf( "default_CLIPMODEL\n" ); + goto default_CLIPMODEL; + } + value_minimize( mindist, bestdist ); + } + if ( (limDepth != 0.0) && (mindist > limDepth) ) + goto default_CLIPMODEL; + + nonax_clip_dbg( p ); + + make_brush_sides<4>( plane, p, reverse, points, si ); + } + + + else if ( spf == eExtrudeTerrain + || spf == eExtrudeDownwards + || spf == eExtrudeUpwards + || spf == eAxialBackplane + || spf == ( eExtrudeTerrain | eMaxExtrude ) + || spf == ( eExtrudeTerrain | eAxialBackplane ) + || spf == ( eExtrudeDownwards | eExtrudeUpwards ) + || spf == ( eExtrudeDownwards | eMaxExtrude ) + || spf == ( eExtrudeDownwards | eAxialBackplane ) + || spf == ( eExtrudeDownwards | eExtrudeUpwards | eMaxExtrude ) + || spf == ( eExtrudeDownwards | eExtrudeUpwards | eAxialBackplane ) + || spf == ( eExtrudeUpwards | eMaxExtrude ) + || spf == ( eExtrudeUpwards | eAxialBackplane ) ){ + + Vector3 bestNormal; + + if ( spf & eExtrudeTerrain ){ //autodirection + bestNormal = avgDirection; + } + else{ + axis = 2; + if ( ( spf & eExtrudeDownwards ) && ( spf & eExtrudeUpwards ) ){ + bestNormal = plane.normal()[2] >= 0? g_vector3_axis_z : -g_vector3_axis_z; + } + else if ( spf & eExtrudeDownwards ){ + bestNormal = g_vector3_axis_z; + } + else if ( spf & eExtrudeUpwards ){ + bestNormal = -g_vector3_axis_z; + } + else{ // best axial normal + bestNormal = plane.normal(); + axis = normal_make_axial( bestNormal ); + } + } + + if ( vector3_dot( plane.normal(), bestNormal ) < 0.05 ){ + goto default_CLIPMODEL; + } + + + /* make side planes */ + for ( j = 0; j < 3; ++j ) + { + p[j].normal() = VectorNormalized( vector3_cross( bestNormal, points[( j + 1 ) % 3] - points[j] ) ); + p[j].dist() = vector3_dot( points[j], p[j].normal() ); + } + + /* make back plane */ + if ( spf & eMaxExtrude ){ //max extrude + reverse.normal() = -bestNormal; + if ( bestNormal[axis] > 0 ){ + reverse.dist() = -minmax.mins[axis] + clipDepth; + } + else{ + reverse.dist() = minmax.maxs[axis] + clipDepth; + } + } + else if ( spf & eAxialBackplane ){ //axial backplane + reverse.normal() = -bestNormal; + reverse.dist() = points[0][axis]; + if ( bestNormal[axis] > 0 ){ + for ( j = 1; j < 3; ++j ){ + value_minimize( reverse.dist(), points[j][axis] ); + } + reverse.dist() = -reverse.dist() + clipDepth; + } + else{ + for ( j = 1; j < 3; ++j ){ + value_maximize( reverse.dist(), points[j][axis] ); + } + reverse.dist() += clipDepth; + } + if ( limDepth != 0.0 ){ + Vector3 cnt = points[0]; + if ( bestNormal[axis] > 0 ){ + for ( j = 1; j < 3; ++j ){ + if ( points[j][axis] > cnt[axis] ){ + cnt = points[j]; + } + } + } + else { + for ( j = 1; j < 3; ++j ){ + if ( points[j][axis] < cnt[axis] ){ + cnt = points[j]; + } + } + } + cnt = plane3_project_point( reverse, cnt ); + if ( -plane3_distance_to_point( plane, cnt ) > limDepth ){ + reverse = plane3_flipped( plane ); + reverse.dist() += clipDepth; + } + } + } + else{ //normal backplane + reverse = plane3_flipped( plane ); + reverse.dist() += clipDepth; + } + + nonax_clip_dbg( p ); + + make_brush_sides<5>( plane, p, reverse, points, si ); + } + + + else if ( spf == ( eExtrudeFaceNormals | ePyramidaClip ) ){ // extrude 45 + //45 degrees normals for side planes + for ( j = 0; j < 3; ++j ) + { + const Vector3 nrm = points[( j + 1 ) % 3] - points[ j ]; + Vector3 enrm = VectorNormalized( vector3_cross( plane.normal(), nrm ) ); + enrm += plane.normal(); + VectorNormalize( enrm ); + /* make side planes */ + p[j].normal() = VectorNormalized( vector3_cross( enrm, nrm ) ); + p[j].dist() = vector3_dot( points[j], p[j].normal() ); + //snap nearly axial side planes + snpd = false; + for ( k = 0; k < 3; ++k ) + { + if ( fabs( p[j].normal()[k] ) < 0.00025 && p[j].normal()[k] != 0.0 ){ + p[j].normal()[k] = 0.0; + snpd = true; + } + } + if ( snpd ){ + VectorNormalize( p[j].normal() ); + p[j].dist() = vector3_dot( ( points[j] + points[( j + 1 ) % 3] ) / 2.0, p[j].normal() ); + } + } + + /* make back plane */ + reverse = plane3_flipped( plane ); + reverse.dist() += clipDepth; + + make_brush_sides<5>( plane, p, reverse, points, si ); + } + + + else if ( spf == eExtrudeVertexNormals + || spf == ( eExtrudeVertexNormals | ePyramidaClip ) ){ // vertex normals + don't check for sides, sticking outwards + Vector3 Vnorm[3], Enorm[3]; + /* get vertex normals */ + for ( j = 0; j < 3; ++j ){ + /* copy normal */ + Vnorm[j] = ds->verts[ds->indexes[i + j]].normal; + } + + //avg normals for side planes + for ( j = 0; j < 3; ++j ) + { + Enorm[ j ] = VectorNormalized( Vnorm[ j ] + Vnorm[( j + 1 ) % 3] ); + //check fuer bad ones + const Vector3 nrm = VectorNormalized( vector3_cross( plane.normal(), points[( j + 1 ) % 3] - points[ j ] ) ); + //check for negative or outside direction + if ( vector3_dot( Enorm[j], plane.normal() ) > 0.1 ){ + if ( ( vector3_dot( Enorm[j], nrm ) > -0.2 ) || ( spf & ePyramidaClip ) ){ + //ok++; + continue; + } + } + //notok++; + //Sys_Printf( "faulty Enormal %i/%i\n", notok, ok ); + //use 45 normal + Enorm[ j ] = plane.normal() + nrm; + VectorNormalize( Enorm[ j ] ); + } + + /* make side planes */ + for ( j = 0; j < 3; ++j ) + { + p[j].normal() = VectorNormalized( vector3_cross( Enorm[j], points[( j + 1 ) % 3] - points[j] ) ); + p[j].dist() = vector3_dot( points[j], p[j].normal() ); + //snap nearly axial side planes + snpd = false; + for ( k = 0; k < 3; ++k ) + { + if ( fabs( p[j].normal()[k] ) < 0.00025 && p[j].normal()[k] != 0.0 ){ + //Sys_Printf( "init plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); + p[j].normal()[k] = 0.0; + snpd = true; + } + } + if ( snpd ){ + VectorNormalize( p[j].normal() ); + //Sys_Printf( "nrm plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); + p[j].dist() = vector3_dot( ( points[j] + points[( j + 1 ) % 3] ) / 2.0, p[j].normal() ); + //Sys_Printf( "dst plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); + } + } + + /* make back plane */ + reverse = plane3_flipped( plane ); + reverse.dist() += clipDepth; + + make_brush_sides<5>( plane, p, reverse, points, si ); + } + + + else if ( spf == eExtrudeFaceNormals ){ + + /* make side planes */ + for ( j = 0; j < 3; ++j ) + { + p[j].normal() = VectorNormalized( vector3_cross( plane.normal(), points[( j + 1 ) % 3] - points[j] ) ); + p[j].dist() = vector3_dot( points[j], p[j].normal() ); + //snap nearly axial side planes + snpd = false; + for ( k = 0; k < 3; ++k ) + { + if ( fabs( p[j].normal()[k] ) < 0.00025 && p[j].normal()[k] != 0.0 ){ + //Sys_Printf( "init plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); + p[j].normal()[k] = 0.0; + snpd = true; + } + } + if ( snpd ){ + VectorNormalize( p[j].normal() ); + //Sys_Printf( "nrm plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); + const Vector3 cnt = ( points[j] + points[( j + 1 ) % 3] ) / 2.0; + p[j].dist() = vector3_dot( cnt, p[j].normal() ); + //Sys_Printf( "dst plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); + } + } + + /* make back plane */ + reverse = plane3_flipped( plane ); + reverse.dist() += clipDepth; + + nonax_clip_dbg( p ); + + make_brush_sides<5>( plane, p, reverse, points, si ); + } + + + else if ( spf == ePyramidaClip ){ + + /* calculate center */ + Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; + + /* make back pyramid point */ + cnt -= plane.normal() * clipDepth; + + /* make 3 more planes */ + if( PlaneFromPoints( p[0], points[2], points[1], cnt ) && + PlaneFromPoints( p[1], points[1], points[0], cnt ) && + PlaneFromPoints( p[2], points[0], points[2], cnt ) ) { + + //check for dangerous planes + while( (( p[0].a != 0.0 || p[0].b != 0.0 ) && fabs( p[0].a ) < 0.00025 && fabs( p[0].b ) < 0.00025) || + (( p[0].a != 0.0 || p[0].c != 0.0 ) && fabs( p[0].a ) < 0.00025 && fabs( p[0].c ) < 0.00025) || + (( p[0].c != 0.0 || p[0].b != 0.0 ) && fabs( p[0].c ) < 0.00025 && fabs( p[0].b ) < 0.00025) || + (( p[1].a != 0.0 || p[1].b != 0.0 ) && fabs( p[1].a ) < 0.00025 && fabs( p[1].b ) < 0.00025) || + (( p[1].a != 0.0 || p[1].c != 0.0 ) && fabs( p[1].a ) < 0.00025 && fabs( p[1].c ) < 0.00025) || + (( p[1].c != 0.0 || p[1].b != 0.0 ) && fabs( p[1].c ) < 0.00025 && fabs( p[1].b ) < 0.00025) || + (( p[2].a != 0.0 || p[2].b != 0.0 ) && fabs( p[2].a ) < 0.00025 && fabs( p[2].b ) < 0.00025) || + (( p[2].a != 0.0 || p[2].c != 0.0 ) && fabs( p[2].a ) < 0.00025 && fabs( p[2].c ) < 0.00025) || + (( p[2].c != 0.0 || p[2].b != 0.0 ) && fabs( p[2].c ) < 0.00025 && fabs( p[2].b ) < 0.00025) ) { + cnt -= plane.normal() * 0.1f; + // Sys_Printf( "shifting pyramid point\n" ); + PlaneFromPoints( p[0], points[2], points[1], cnt ); + PlaneFromPoints( p[1], points[1], points[0], cnt ); + PlaneFromPoints( p[2], points[0], points[2], cnt ); + } + + nonax_clip_dbg( p ); + + make_brush_sides<4>( plane, p, reverse, points, si ); + } + else + { + Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", + points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], name ); + continue; + } + } + + + else if ( ( si->clipModel && spf == 0 ) || ( spawnFlags & eClipFlags ) == eClipModel ){ //default CLIPMODEL + +default_CLIPMODEL: + // axial normal + Vector3 bestNormal = plane.normal(); + normal_make_axial( bestNormal ); + + /* make side planes */ + for ( j = 0; j < 3; ++j ) + { + p[j].normal() = VectorNormalized( vector3_cross( bestNormal, points[( j + 1 ) % 3] - points[j] ) ); + p[j].dist() = vector3_dot( points[j], p[j].normal() ); + } + + /* make back plane */ + reverse = plane3_flipped( plane ); + reverse.dist() += vector3_dot( bestNormal, plane.normal() ) * clipDepth; + + nonax_clip_dbg( p ); + + make_brush_sides<5>( plane, p, reverse, points, si ); + } + + + /* add to entity */ + if ( CreateBrushWindings( buildBrush ) ) { + AddBrushBevels(); + //% EmitBrushes( buildBrush, NULL, NULL ); + brush_t& newBrush = entities[ mapEntityNum ].brushes.emplace_front( buildBrush ); + newBrush.original = &newBrush; + entities[ mapEntityNum ].numBrushes++; + } + else{ + Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", + points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], name ); + } + } + } + // normalEpsilon = normalEpsilon_save; + } + else if ( spawnFlags & eClipFlags ){ + Sys_Warning( "nonexistent clipping mode selected\n" ); + } +} + /* InsertModel() - ydnar adds a picomodel into the bsp */ void InsertModel( const char *name, int skin, int frame, const Matrix4& transform, const std::list *remaps, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle, float clipDepth ){ - int i, j, k; + int i, j; const Matrix4 nTransform( matrix4_for_normal_transform( transform ) ); const bool transform_lefthanded = MATRIX4_LEFTHANDED == matrix4_handedness( transform ); AssModel *model; shaderInfo_t *si; mapDrawSurface_t *ds; const char *picoShaderName; - char *skinfilecontent; - int skinfilesize; - char *skinfileptr, *skinfilenextptr; - //int ok=0, notok=0; - int spf = ( spawnFlags & 8088 ); - float limDepth=0; - - - if ( clipDepth < 0 ){ - limDepth = -clipDepth; - clipDepth = 2.0; - } /* get model */ @@ -388,57 +1008,60 @@ void InsertModel( const char *name, int skin, int frame, const Matrix4& transfor } /* load skin file */ - auto skinfilename = StringOutputStream(99)( PathExtensionless( name ), '_', skin, ".skin" ); - skinfilesize = vfsLoadFile( skinfilename, (void**) &skinfilecontent, 0 ); - if ( skinfilesize < 0 && skin != 0 ) { - /* fallback to skin 0 if invalid */ - skinfilename( PathExtensionless( name ), "_0.skin" ); - skinfilesize = vfsLoadFile( skinfilename, (void**) &skinfilecontent, 0 ); - if ( skinfilesize >= 0 ) { - Sys_Printf( "Skin %d of %s does not exist, using 0 instead\n", skin, name ); - } - } std::list skins; - if ( skinfilesize >= 0 ) { - Sys_Printf( "Using skin %d of %s\n", skin, name ); - for ( skinfileptr = skinfilecontent; !strEmpty( skinfileptr ); skinfileptr = skinfilenextptr ) - { - // for fscanf - char format[64]; - - skinfilenextptr = strchr( skinfileptr, '\r' ); - if ( skinfilenextptr != NULL ) { - strClear( skinfilenextptr++ ); + { + auto skinfilename = StringOutputStream(99)( PathExtensionless( name ), '_', skin, ".skin" ); + char *skinfilecontent; + int skinfilesize = vfsLoadFile( skinfilename, (void**) &skinfilecontent, 0 ); + if ( skinfilesize < 0 && skin != 0 ) { + /* fallback to skin 0 if invalid */ + skinfilename( PathExtensionless( name ), "_0.skin" ); + skinfilesize = vfsLoadFile( skinfilename, (void**) &skinfilecontent, 0 ); + if ( skinfilesize >= 0 ) { + Sys_Printf( "Skin %d of %s does not exist, using 0 instead\n", skin, name ); } - else + } + if ( skinfilesize >= 0 ) { + Sys_Printf( "Using skin %d of %s\n", skin, name ); + for ( char *skinfilenextptr, *skinfileptr = skinfilecontent; !strEmpty( skinfileptr ); skinfileptr = skinfilenextptr ) { - skinfilenextptr = strchr( skinfileptr, '\n' ); + // for fscanf + char format[64]; + + skinfilenextptr = strchr( skinfileptr, '\r' ); if ( skinfilenextptr != NULL ) { strClear( skinfilenextptr++ ); } - else{ - skinfilenextptr = skinfileptr + strlen( skinfileptr ); + else + { + skinfilenextptr = strchr( skinfileptr, '\n' ); + if ( skinfilenextptr != NULL ) { + strClear( skinfilenextptr++ ); + } + else{ + skinfilenextptr = skinfileptr + strlen( skinfileptr ); + } } - } - /* create new item */ - remap_t skin; + /* create new item */ + remap_t skin; - sprintf( format, "replace %%%ds %%%ds", (int)sizeof( skin.from ) - 1, (int)sizeof( skin.to ) - 1 ); - if ( sscanf( skinfileptr, format, skin.from, skin.to ) == 2 ) { - skins.push_back( skin ); - continue; - } - sprintf( format, " %%%d[^, ] ,%%%ds", (int)sizeof( skin.from ) - 1, (int)sizeof( skin.to ) - 1 ); - if ( sscanf( skinfileptr, format, skin.from, skin.to ) == 2 ) { - skins.push_back( skin ); - continue; - } + sprintf( format, "replace %%%ds %%%ds", (int)sizeof( skin.from ) - 1, (int)sizeof( skin.to ) - 1 ); + if ( sscanf( skinfileptr, format, skin.from, skin.to ) == 2 ) { + skins.push_back( skin ); + continue; + } + sprintf( format, " %%%d[^, ] ,%%%ds", (int)sizeof( skin.from ) - 1, (int)sizeof( skin.to ) - 1 ); + if ( sscanf( skinfileptr, format, skin.from, skin.to ) == 2 ) { + skins.push_back( skin ); + continue; + } - /* invalid input line -> discard skin struct */ - Sys_Printf( "Discarding skin directive in %s: %s\n", skinfilename.c_str(), skinfileptr ); + /* invalid input line -> discard skin struct */ + Sys_Printf( "Discarding skin directive in %s: %s\n", skinfilename.c_str(), skinfileptr ); + } + free( skinfilecontent ); } - free( skinfilecontent ); } /* hack: Stable-1_2 and trunk have differing row/column major matrix order @@ -506,7 +1129,7 @@ void InsertModel( const char *name, int skin, int frame, const Matrix4& transfor /* shader renaming for sof2 */ if ( renameModelShaders ) { auto shaderName = String64()( PathExtensionless( picoShaderName ) ); - if ( spawnFlags & 1 ) { + if ( spawnFlags & eRMG_BSP ) { shaderName << "_RMG_BSP"; } else{ @@ -528,12 +1151,12 @@ void InsertModel( const char *name, int skin, int frame, const Matrix4& transfor ds->shaderInfo = si; /* force to meta? */ - if ( ( si != NULL && si->forceMeta ) || ( spawnFlags & 4 ) ) { /* 3rd bit */ + if ( ( si != NULL && si->forceMeta ) || ( spawnFlags & eForceMeta ) ) { /* 3rd bit */ ds->type = ESurfaceType::ForcedMeta; } /* fix the surface's normals (jal: conditioned by shader info) */ - if ( !( spawnFlags & 64 ) && ( shadeAngle == 0.0f || ds->type != ESurfaceType::ForcedMeta ) ) { + if ( !( spawnFlags & eNoSmooth ) && ( shadeAngle == 0.0f || ds->type != ESurfaceType::ForcedMeta ) ) { // PicoFixSurfaceNormals( surface ); } @@ -600,7 +1223,7 @@ void InsertModel( const char *name, int skin, int frame, const Matrix4& transfor for ( j = 0; j < MAX_LIGHTMAPS; j++ ) { dv.lightmap[ j ] = { 0, 0 }; - if ( spawnFlags & 32 ) { // spawnflag 32: model color -> alpha hack + if ( spawnFlags & eColorToAlpha ) { // spawnflag 32: model color -> alpha hack dv.color[ j ] = { 255, 255, 255, color_to_byte( RGBTOGRAY( color ) * 255 ) }; } else @@ -632,779 +1255,7 @@ void InsertModel( const char *name, int skin, int frame, const Matrix4& transfor /* set cel shader */ ds->celShader = celShader; - /* ydnar: giant hack land: generate clipping brushes for model triangles */ - if ( ( si->clipModel && !( spf ) ) || //default CLIPMODEL - ( ( spawnFlags & 8090 ) == 2 ) || //default CLIPMODEL - ( spf == 8 ) || //EXTRUDE_FACE_NORMALS - ( spf == 16 ) || //EXTRUDE_TERRAIN - ( spf == 128 ) || //EXTRUDE_VERTEX_NORMALS - ( spf == 256 ) || //PYRAMIDAL_CLIP - ( spf == 512 ) || //EXTRUDE_DOWNWARDS - ( spf == 1024 ) || //EXTRUDE_UPWARDS - ( spf == 4096 ) || //default CLIPMODEL + AXIAL_BACKPLANE - ( spf == 264 ) || //EXTRUDE_FACE_NORMALS+PYRAMIDAL_CLIP (extrude 45) - ( spf == 2064 ) || //EXTRUDE_TERRAIN+MAX_EXTRUDE - ( spf == 4112 ) || //EXTRUDE_TERRAIN+AXIAL_BACKPLANE - ( spf == 384 ) || //EXTRUDE_VERTEX_NORMALS + PYRAMIDAL_CLIP - vertex normals + don't check for sides, sticking outwards - ( spf == 4352 ) || //PYRAMIDAL_CLIP+AXIAL_BACKPLANE - ( spf == 1536 ) || //EXTRUDE_DOWNWARDS+EXTRUDE_UPWARDS - ( spf == 2560 ) || //EXTRUDE_DOWNWARDS+MAX_EXTRUDE - ( spf == 4608 ) || //EXTRUDE_DOWNWARDS+AXIAL_BACKPLANE - ( spf == 3584 ) || //EXTRUDE_DOWNWARDS+EXTRUDE_UPWARDS+MAX_EXTRUDE - ( spf == 5632 ) || //EXTRUDE_DOWNWARDS+EXTRUDE_UPWARDS+AXIAL_BACKPLANE - ( spf == 3072 ) || //EXTRUDE_UPWARDS+MAX_EXTRUDE - ( spf == 5120 ) ){ //EXTRUDE_UPWARDS+AXIAL_BACKPLANE - Vector3 points[ 4 ], cnt, bestNormal, nrm, Vnorm[3], Enorm[3]; - Plane3f plane, reverse, p[3]; - double normalEpsilon_save; - bool snpd; - MinMax minmax; - Vector3 avgDirection( 0 ); - int axis; - #define nonax_clip_dbg 0 - - /* temp hack */ - if ( !si->clipModel && !( si->compileFlags & C_SOLID ) ) { - continue; - } - - //wont snap these in normal way, or will explode - normalEpsilon_save = normalEpsilon; - //normalEpsilon = 0.000001; - - - //MAX_EXTRUDE or EXTRUDE_TERRAIN - if ( ( spawnFlags & 2048 ) || ( spawnFlags & 16 ) ){ - - for ( i = 0; i < ds->numIndexes; i += 3 ) - { - for ( j = 0; j < 3; j++ ) - { - points[ j ] = ds->verts[ ds->indexes[ i + j ] ].xyz; - } - if ( PlaneFromPoints( plane, points ) ){ - if ( spawnFlags & 16 ) - avgDirection += plane.normal(); //calculate average mesh facing direction - - //get min/max - for ( j = 0; j < 3; ++j ){ - minmax.extend( points[j] ); - } - } - } - //unify avg direction - if ( spawnFlags & 16 ){ - if ( vector3_length( avgDirection ) != 0 ){ - axis = vector3_max_abs_component_index( avgDirection ); - } - else{ - axis = 2; - } - avgDirection = avgDirection[axis] >= 0? g_vector3_axes[axis] : -g_vector3_axes[axis]; - } - } - - /* walk triangle list */ - for ( i = 0; i < ds->numIndexes; i += 3 ) - { - /* make points */ - for ( j = 0; j < 3; j++ ) - { - /* copy xyz */ - points[ j ] = ds->verts[ ds->indexes[ i + j ] ].xyz; - } - - /* make plane for triangle */ - if ( PlaneFromPoints( plane, points ) ) { - - /* build a brush */ - buildBrush.sides.reserve( MAX_BUILD_SIDES ); - buildBrush.entityNum = mapEntityNum; - buildBrush.contentShader = si; - buildBrush.compileFlags = si->compileFlags; - buildBrush.contentFlags = si->contentFlags; - buildBrush.detail = true; - - //snap points before using them for further calculations - //precision suffers a lot, when two of normal values are under .00025 (often no collision, knocking up effect in ioq3) - //also broken drawsurfs in case of normal brushes - snpd = false; - for ( j=0; j<3; j++ ) - { - if ( fabs( plane.normal()[j] ) < 0.00025 && fabs( plane.normal()[(j+1)%3] ) < 0.00025 - && ( plane.normal()[j] != 0.0 || plane.normal()[(j+1)%3] != 0.0 ) ){ - cnt = ( points[0] + points[1] + points[2] ) / 3.0; - points[0][(j+2)%3] = points[1][(j+2)%3] = points[2][(j+2)%3] = cnt[(j+2)%3]; - snpd = true; - break; - } - } - - //snap pairs of points to prevent bad side planes - for ( j=0; j<3; j++ ) - { - nrm = VectorNormalized( points[j] - points[(j+1)%3] ); - for ( k=0; k<3; k++ ) - { - if ( nrm[k] != 0.0 && fabs(nrm[k]) < 0.00025 ){ - //Sys_Printf( "b4(%6.6f %6.6f %6.6f)(%6.6f %6.6f %6.6f)\n", points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2] ); - points[j][k]=points[(j+1)%3][k] = ( points[j][k] + points[(j+1)%3][k] ) / 2.0; - //Sys_Printf( "sn(%6.6f %6.6f %6.6f)(%6.6f %6.6f %6.6f)\n", points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2] ); - snpd = true; - } - } - } - - if ( snpd ) { - PlaneFromPoints( plane, points ); - snpd = false; - } - - //vector-is-close-to-be-on-axis check again, happens after previous code sometimes - for ( j=0; j<3; j++ ) - { - if ( fabs( plane.normal()[j] ) < 0.00025 && fabs( plane.normal()[(j+1)%3] ) < 0.00025 - && ( plane.normal()[j] != 0.0 || plane.normal()[(j+1)%3] != 0.0 ) ){ - cnt = ( points[0] + points[1] + points[2] ) / 3.0; - points[0][(j+2)%3] = points[1][(j+2)%3] = points[2][(j+2)%3] = cnt[(j+2)%3]; - PlaneFromPoints( plane, points ); - break; - } - } - - //snap single snappable normal components - for ( j=0; j<3; j++ ) - { - if ( plane.normal()[j] != 0.0 && fabs( plane.normal()[j] ) < 0.00005 ){ - plane.normal()[j]=0.0; - snpd = true; - } - } - - //adjust plane dist - if ( snpd ) { - cnt = ( points[0] + points[1] + points[2] ) / 3.0; - VectorNormalize( plane.normal() ); - plane.dist() = vector3_dot( plane.normal(), cnt ); - - //project points to resulting plane to keep intersections precision - for ( j=0; j<3; j++ ) - { - //Sys_Printf( "b4 %i (%6.7f %6.7f %6.7f)\n", j, points[j][0], points[j][1], points[j][2] ); - points[j] = plane3_project_point( plane, points[j] ); - //Sys_Printf( "sn %i (%6.7f %6.7f %6.7f)\n", j, points[j][0], points[j][1], points[j][2] ); - } - //Sys_Printf( "sn pln (%6.7f %6.7f %6.7f %6.7f)\n", plane[0], plane[1], plane[2], plane[3] ); - //PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ); - //Sys_Printf( "pts pln (%6.7f %6.7f %6.7f %6.7f)\n", plane[0], plane[1], plane[2], plane[3] ); - } - - /* sanity check */ - { - const Vector3 d1 = points[1] - points[0]; - const Vector3 d2 = points[2] - points[0]; - const Vector3 normaL = vector3_cross( d2, d1 ); - /* https://en.wikipedia.org/wiki/Cross_product#Geometric_meaning - cross( a, b ).length = a.length b.length sin( angle ) */ - const double lengthsSquared = vector3_length_squared( d1 ) * vector3_length_squared( d2 ); - if ( lengthsSquared == 0 || fabs( vector3_length_squared( normaL ) / lengthsSquared ) < 1e-8 ) { - Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped: points on line\n", - points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], name ); - continue; - } - } - - - if ( spf == 4352 ){ //PYRAMIDAL_CLIP+AXIAL_BACKPLANE - - for ( j=0; j<3; j++ ) - { - if ( fabs( plane.normal()[j] ) < 0.05 && fabs( plane.normal()[(j+1)%3] ) < 0.05 ){ //no way, close to lay on two axes - goto default_CLIPMODEL; - } - } - - // best axial normal - bestNormal = plane.normal(); - for ( j = 0; j < 3; j++ ){ - if ( fabs(bestNormal[j]) > fabs(bestNormal[(j+1)%3]) ){ - bestNormal[(j+1)%3] = 0.0; - axis = j; - } - else { - bestNormal[j] = 0.0; - } - } - VectorNormalize( bestNormal ); - - - float bestdist, currdist, bestangle, currangle, mindist = 999999; - - for ( j = 0; j < 3; j++ ){//planes - bestdist = 999999; - bestangle = 1; - for ( k = 0; k < 3; k++ ){//axes - nrm = points[(j+1)%3] - points[j]; - if ( k == axis ){ - reverse.normal() = vector3_cross( bestNormal, nrm ); - } - else{ - Vnorm[0].set( 0 ); - if ( (k+1)%3 == axis ){ - if ( nrm[ (k+2)%3 ] == 0 ) continue; - Vnorm[0][ (k+2)%3 ] = nrm[ (k+2)%3 ]; - } - else{ - if ( nrm[ (k+1)%3 ] == 0 ) continue; - Vnorm[0][ (k+1)%3 ] = nrm[ (k+1)%3 ]; - } - Enorm[0] = vector3_cross( bestNormal, Vnorm[0] ); - reverse.normal() = vector3_cross( Enorm[0], nrm ); - } - VectorNormalize( reverse.normal() ); - reverse.dist() = vector3_dot( points[ j ], reverse.normal() ); - //check facing, thickness - currdist = reverse.dist() - vector3_dot( reverse.normal(), points[ (j+2)%3 ] ); - currangle = vector3_dot( reverse.normal(), plane.normal() ); - if ( ( ( currdist > 0.1 ) && ( currdist < bestdist ) && ( currangle < 0 ) ) || - ( ( currangle >= 0 ) && ( currangle <= bestangle ) ) ){ - bestangle = currangle; - if ( currangle < 0 ) bestdist = currdist; - p[j] = reverse; - } - } - //if ( bestdist == 999999 && bestangle == 1 ) Sys_Printf("default_CLIPMODEL\n"); - if ( bestdist == 999999 && bestangle == 1 ) goto default_CLIPMODEL; - value_minimize( mindist, bestdist ); - } - if ( (limDepth != 0.0) && (mindist > limDepth) ) goto default_CLIPMODEL; - - -#if nonax_clip_dbg - for ( j = 0; j < 3; j++ ) - { - for ( k = 0; k < 3; k++ ) - { - if ( fabs(p[j][k]) < 0.00025 && p[j][k] != 0.0 ){ - Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n", p[j][0], p[j][1], p[j][2] ); - } - } - } -#endif - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 4 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 4; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 0 ] ); // p[0] contains points[0] and points[1] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 1 ] ); // p[1] contains points[1] and points[2] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - } - - - else if ( ( spf == 16 ) || //EXTRUDE_TERRAIN - ( spf == 512 ) || //EXTRUDE_DOWNWARDS - ( spf == 1024 ) || //EXTRUDE_UPWARDS - ( spf == 4096 ) || //default CLIPMODEL + AXIAL_BACKPLANE - ( spf == 2064 ) || //EXTRUDE_TERRAIN+MAX_EXTRUDE - ( spf == 4112 ) || //EXTRUDE_TERRAIN+AXIAL_BACKPLANE - ( spf == 1536 ) || //EXTRUDE_DOWNWARDS+EXTRUDE_UPWARDS - ( spf == 2560 ) || //EXTRUDE_DOWNWARDS+MAX_EXTRUDE - ( spf == 4608 ) || //EXTRUDE_DOWNWARDS+AXIAL_BACKPLANE - ( spf == 3584 ) || //EXTRUDE_DOWNWARDS+EXTRUDE_UPWARDS+MAX_EXTRUDE - ( spf == 5632 ) || //EXTRUDE_DOWNWARDS+EXTRUDE_UPWARDS+AXIAL_BACKPLANE - ( spf == 3072 ) || //EXTRUDE_UPWARDS+MAX_EXTRUDE - ( spf == 5120 ) ){ //EXTRUDE_UPWARDS+AXIAL_BACKPLANE - - if ( spawnFlags & 16 ){ //autodirection - bestNormal = avgDirection; - } - else{ - axis = 2; - if ( ( spawnFlags & 1536 ) == 1536 ){ //up+down - bestNormal = plane.normal()[2] >= 0? g_vector3_axis_z : -g_vector3_axis_z; - } - else if ( spawnFlags & 512 ){ //down - bestNormal = g_vector3_axis_z; - } - else if ( spawnFlags & 1024 ){ //up - bestNormal = -g_vector3_axis_z; - } - else{ // best axial normal - bestNormal = plane.normal(); - for ( j = 0; j < 3; j++ ){ - if ( fabs(bestNormal[j]) > fabs(bestNormal[(j+1)%3]) ){ - bestNormal[(j+1)%3] = 0.0; - axis = j; - } - else { - bestNormal[j] = 0.0; - } - } - VectorNormalize( bestNormal ); - } - } - - if ( vector3_dot( plane.normal(), bestNormal ) < 0.05 ){ - goto default_CLIPMODEL; - } - - - /* make side planes */ - for ( j = 0; j < 3; j++ ) - { - p[j].normal() = VectorNormalized( vector3_cross( bestNormal, points[(j+1)%3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - } - - /* make back plane */ - if ( spawnFlags & 2048 ){ //max extrude - reverse.normal() = -bestNormal; - if ( bestNormal[axis] > 0 ){ - reverse.dist() = -minmax.mins[axis] + clipDepth; - } - else{ - reverse.dist() = minmax.maxs[axis] + clipDepth; - } - } - else if ( spawnFlags & 4096 ){ //axial backplane - reverse.normal() = -bestNormal; - reverse.dist() = points[0][axis]; - if ( bestNormal[axis] > 0 ){ - for ( j = 1; j < 3; j++ ){ - value_minimize( reverse.dist(), points[j][axis] ); - } - reverse.dist() = -reverse.dist() + clipDepth; - } - else{ - for ( j = 1; j < 3; j++ ){ - value_maximize( reverse.dist(), points[j][axis] ); - } - reverse.dist() += clipDepth; - } - if ( limDepth != 0.0 ){ - cnt = points[0]; - if ( bestNormal[axis] > 0 ){ - for ( j = 1; j < 3; j++ ){ - if ( points[j][axis] > cnt[axis] ){ - cnt = points[j]; - } - } - } - else { - for ( j = 1; j < 3; j++ ){ - if ( points[j][axis] < cnt[axis] ){ - cnt = points[j]; - } - } - } - cnt = plane3_project_point( reverse, cnt ); - if ( -plane3_distance_to_point( plane, cnt ) > limDepth ){ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; - } - } - } - else{ //normal backplane - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; - } -#if nonax_clip_dbg - for ( j = 0; j < 3; j++ ) - { - for ( k = 0; k < 3; k++ ) - { - if ( fabs(p[j][k]) < 0.00025 && p[j][k] != 0.0 ){ - Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n", p[j][0], p[j][1], p[j][2] ); - } - } - } -#endif - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 5 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 5; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 0 ] ); // p[0] contains points[0] and points[1] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 1 ] ); // p[1] contains points[1] and points[2] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - buildBrush.sides[ 4 ].planenum = FindFloatPlane( reverse, 0, NULL ); - } - - - else if ( spf == 264 ){ //EXTRUDE_FACE_NORMALS+PYRAMIDAL_CLIP (extrude 45) - - //45 degrees normals for side planes - for ( j = 0; j < 3; j++ ) - { - nrm = points[(j+1)%3] - points[ j ]; - Enorm[ j ] = VectorNormalized( vector3_cross( plane.normal(), nrm ) ); - Enorm[ j ] += plane.normal(); - VectorNormalize( Enorm[ j ] ); - /* make side planes */ - p[j].normal() = VectorNormalized( vector3_cross( Enorm[ j ], nrm ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - //snap nearly axial side planes - snpd = false; - for ( k = 0; k < 3; k++ ) - { - if ( fabs( p[j].normal()[k] ) < 0.00025 && p[j].normal()[k] != 0.0 ){ - p[j].normal()[k] = 0.0; - snpd = true; - } - } - if ( snpd ){ - VectorNormalize( p[j].normal() ); - p[j].dist() = vector3_dot( ( points[j] + points[(j+1)%3] ) / 2.0, p[j].normal() ); - } - } - - /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; - - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 5 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 5; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 0 ] ); // p[0] contains points[0] and points[1] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 1 ] ); // p[1] contains points[1] and points[2] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - buildBrush.sides[ 4 ].planenum = FindFloatPlane( reverse, 0, NULL ); - } - - - else if ( ( spf == 128 ) || //EXTRUDE_VERTEX_NORMALS - ( spf == 384 ) ){ //EXTRUDE_VERTEX_NORMALS + PYRAMIDAL_CLIP - vertex normals + don't check for sides, sticking outwards - /* get vertex normals */ - for ( j = 0; j < 3; j++ ) - { - /* copy normal */ - Vnorm[ j ] = ds->verts[ ds->indexes[ i + j ] ].normal; - } - - //avg normals for side planes - for ( j = 0; j < 3; j++ ) - { - Enorm[ j ] = VectorNormalized( Vnorm[ j ] + Vnorm[ (j+1)%3 ] ); - //check fuer bad ones - nrm = VectorNormalized( vector3_cross( plane.normal(), points[(j+1)%3] - points[ j ] ) ); - //check for negative or outside direction - if ( vector3_dot( Enorm[ j ], plane.normal() ) > 0.1 ){ - if ( ( vector3_dot( Enorm[ j ], nrm ) > -0.2 ) || ( spawnFlags & 256 ) ){ - //ok++; - continue; - } - } - //notok++; - //Sys_Printf( "faulty Enormal %i/%i\n", notok, ok ); - //use 45 normal - Enorm[ j ] = plane.normal() + nrm; - VectorNormalize( Enorm[ j ] ); - } - - /* make side planes */ - for ( j = 0; j < 3; j++ ) - { - p[j].normal() = VectorNormalized( vector3_cross( Enorm[ j ], points[(j+1)%3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - //snap nearly axial side planes - snpd = false; - for ( k = 0; k < 3; k++ ) - { - if ( fabs( p[j].normal()[k] ) < 0.00025 && p[j].normal()[k] != 0.0 ){ - //Sys_Printf( "init plane %6.8f %6.8f %6.8f %6.8f\n", p[j][0], p[j][1], p[j][2], p[j][3]); - p[j].normal()[k] = 0.0; - snpd = true; - } - } - if ( snpd ){ - VectorNormalize( p[j].normal() ); - //Sys_Printf( "nrm plane %6.8f %6.8f %6.8f %6.8f\n", p[j][0], p[j][1], p[j][2], p[j][3]); - p[j].dist() = vector3_dot( ( points[j] + points[(j+1)%3] ) / 2.0, p[j].normal() ); - //Sys_Printf( "dst plane %6.8f %6.8f %6.8f %6.8f\n", p[j][0], p[j][1], p[j][2], p[j][3]); - } - } - - /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; - - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 5 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 5; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 0 ] ); // p[0] contains points[0] and points[1] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 1 ] ); // p[1] contains points[1] and points[2] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - buildBrush.sides[ 4 ].planenum = FindFloatPlane( reverse, 0, NULL ); - } - - - else if ( spf == 8 ){ //EXTRUDE_FACE_NORMALS - - /* make side planes */ - for ( j = 0; j < 3; j++ ) - { - p[j].normal() = VectorNormalized( vector3_cross( plane.normal(), points[(j+1)%3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - //snap nearly axial side planes - snpd = false; - for ( k = 0; k < 3; k++ ) - { - if ( fabs( p[j].normal()[k] ) < 0.00025 && p[j].normal()[k] != 0.0 ){ - //Sys_Printf( "init plane %6.8f %6.8f %6.8f %6.8f\n", p[j][0], p[j][1], p[j][2], p[j][3]); - p[j].normal()[k] = 0.0; - snpd = true; - } - } - if ( snpd ){ - VectorNormalize( p[j].normal() ); - //Sys_Printf( "nrm plane %6.8f %6.8f %6.8f %6.8f\n", p[j][0], p[j][1], p[j][2], p[j][3]); - cnt = ( points[j] + points[(j+1)%3] ) / 2.0; - p[j].dist() = vector3_dot( cnt, p[j].normal() ); - //Sys_Printf( "dst plane %6.8f %6.8f %6.8f %6.8f\n", p[j][0], p[j][1], p[j][2], p[j][3]); - } - } - - /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; -#if nonax_clip_dbg - for ( j = 0; j < 3; j++ ) - { - for ( k = 0; k < 3; k++ ) - { - if ( fabs(p[j][k]) < 0.00005 && p[j][k] != 0.0 ){ - Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n", p[j][0], p[j][1], p[j][2] ); - Sys_Printf( "frm src nrm %6.17f %6.17f %6.17f\n", plane[0], plane[1], plane[2]); - } - } - } -#endif - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 5 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 5; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 0 ] ); // p[0] contains points[0] and points[1] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 1 ] ); // p[1] contains points[1] and points[2] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - buildBrush.sides[ 4 ].planenum = FindFloatPlane( reverse, 0, NULL ); - } - - - else if ( spf == 256 ){ //PYRAMIDAL_CLIP - - /* calculate center */ - cnt = ( points[0] + points[1] + points[2] ) / 3.0; - - /* make back pyramid point */ - cnt -= plane.normal() * clipDepth; - - /* make 3 more planes */ - if( PlaneFromPoints( p[0], points[ 2 ], points[ 1 ], cnt ) && - PlaneFromPoints( p[1], points[ 1 ], points[ 0 ], cnt ) && - PlaneFromPoints( p[2], points[ 0 ], points[ 2 ], cnt ) ) { - - //check for dangerous planes - while( (( p[0].a != 0.0 || p[0].b != 0.0 ) && fabs( p[0].a ) < 0.00025 && fabs( p[0].b ) < 0.00025) || - (( p[0].a != 0.0 || p[0].c != 0.0 ) && fabs( p[0].a ) < 0.00025 && fabs( p[0].c ) < 0.00025) || - (( p[0].c != 0.0 || p[0].b != 0.0 ) && fabs( p[0].c ) < 0.00025 && fabs( p[0].b ) < 0.00025) || - (( p[1].a != 0.0 || p[1].b != 0.0 ) && fabs( p[1].a ) < 0.00025 && fabs( p[1].b ) < 0.00025) || - (( p[1].a != 0.0 || p[1].c != 0.0 ) && fabs( p[1].a ) < 0.00025 && fabs( p[1].c ) < 0.00025) || - (( p[1].c != 0.0 || p[1].b != 0.0 ) && fabs( p[1].c ) < 0.00025 && fabs( p[1].b ) < 0.00025) || - (( p[2].a != 0.0 || p[2].b != 0.0 ) && fabs( p[2].a ) < 0.00025 && fabs( p[2].b ) < 0.00025) || - (( p[2].a != 0.0 || p[2].c != 0.0 ) && fabs( p[2].a ) < 0.00025 && fabs( p[2].c ) < 0.00025) || - (( p[2].c != 0.0 || p[2].b != 0.0 ) && fabs( p[2].c ) < 0.00025 && fabs( p[2].b ) < 0.00025) ) { - cnt -= plane.normal() * 0.1f; - // Sys_Printf( "shifting pyramid point\n" ); - PlaneFromPoints( p[0], points[ 2 ], points[ 1 ], cnt ); - PlaneFromPoints( p[1], points[ 1 ], points[ 0 ], cnt ); - PlaneFromPoints( p[2], points[ 0 ], points[ 2 ], cnt ); - } -#if nonax_clip_dbg - for ( j = 0; j < 3; j++ ) - { - for ( k = 0; k < 3; k++ ) - { - if ( fabs(p[j][k]) < 0.00005 && p[j][k] != 0.0 ){ - Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n (%6.8f %6.8f %6.8f)\n (%6.8f %6.8f %6.8f)\n (%6.8f %6.8f %6.8f)\n", p[j][0], p[j][1], p[j][2], points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2], cnt[0], cnt[1], cnt[2] ); - } - } - } -#endif - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 4 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 4; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 1 ] ); // p[0] contains points[1] and points[2] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 0 ] ); // p[1] contains points[0] and points[1] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - } - else - { - Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", - points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], name ); - continue; - } - } - - - else if ( ( si->clipModel && !( spf ) ) || ( ( spawnFlags & 8090 ) == 2 ) ){ //default CLIPMODEL - -default_CLIPMODEL: - // axial normal - bestNormal = plane.normal(); - for ( j = 0; j < 3; j++ ){ - if ( fabs(bestNormal[j]) > fabs(bestNormal[(j+1)%3]) ){ - bestNormal[(j+1)%3] = 0.0; - } - else { - bestNormal[j] = 0.0; - } - } - VectorNormalize( bestNormal ); - - /* make side planes */ - for ( j = 0; j < 3; j++ ) - { - p[j].normal() = VectorNormalized( vector3_cross( bestNormal, points[(j+1)%3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - } - - /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += vector3_dot( bestNormal, plane.normal() ) * clipDepth; -#if nonax_clip_dbg - for ( j = 0; j < 3; j++ ) - { - for ( k = 0; k < 3; k++ ) - { - if ( fabs(p[j][k]) < 0.00025 && p[j][k] != 0.0 ){ - Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n", p[j][0], p[j][1], p[j][2] ); - } - } - } -#endif - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( 5 ); - buildBrush.sides[ 0 ].shaderInfo = si; - buildBrush.sides[ 0 ].surfaceFlags = si->surfaceFlags; - for ( j = 1; j < 5; j++ ) { - if ( debugClip ) { - buildBrush.sides[ 0 ].shaderInfo = ShaderInfoForShader( "debugclip2" ); - buildBrush.sides[ j ].shaderInfo = ShaderInfoForShader( "debugclip" ); - } - else { - buildBrush.sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works - } - } - points[3] = points[0]; // for cyclic usage - - buildBrush.sides[ 0 ].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[ 1 ].planenum = FindFloatPlane( p[0], 2, &points[ 0 ] ); // p[0] contains points[0] and points[1] - buildBrush.sides[ 2 ].planenum = FindFloatPlane( p[1], 2, &points[ 1 ] ); // p[1] contains points[1] and points[2] - buildBrush.sides[ 3 ].planenum = FindFloatPlane( p[2], 2, &points[ 2 ] ); // p[2] contains points[2] and points[0] (copied to points[3] - buildBrush.sides[ 4 ].planenum = FindFloatPlane( reverse, 0, NULL ); - } - - - /* add to entity */ - if ( CreateBrushWindings( buildBrush ) ) { - AddBrushBevels(); - //% EmitBrushes( buildBrush, NULL, NULL ); - brush_t& newBrush = entities[ mapEntityNum ].brushes.emplace_front( buildBrush ); - newBrush.original = &newBrush; - entities[ mapEntityNum ].numBrushes++; - } - else{ - Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", - points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], name ); - } - } - } - normalEpsilon = normalEpsilon_save; - } - else if ( spawnFlags & 8090 ){ - Sys_Warning( "nonexistent clipping mode selected\n" ); - } + ClipModel( spawnFlags, clipDepth, si, ds ); } }