Files
netradiant-custom/tools/quake3/q3map2/decals.cpp
T
2025-11-06 19:30:15 +05:00

761 lines
21 KiB
C++

/* -------------------------------------------------------------------------------
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* dependencies */
#include "q3map2.h"
#include "timer.h"
#define MAX_PROJECTORS 1024
struct decalProjector_t
{
shaderInfo_t *si;
MinMax minmax;
Vector3 center;
float radius, radius2;
int numPlanes; /* either 5 or 6, for quad or triangle projectors */
Plane3f planes[ 6 ];
Vector4 texMat[ 2 ];
};
static int numProjectors = 0;
static decalProjector_t projectors[ MAX_PROJECTORS ];
static int numDecalSurfaces = 0;
static Vector3 entityOrigin;
/*
MakeTextureMatrix()
generates a texture projection matrix for a triangle
returns false if a texture matrix cannot be created
*/
static bool MakeTextureMatrix( decalProjector_t *dp, const Plane3f& projection, const bspDrawVert_t *a, const bspDrawVert_t *b, const bspDrawVert_t *c ){
int i, j;
double bb, s, t;
DoubleVector3 pa, pb, pc;
DoubleVector3 bary, xyz;
DoubleVector3 vecs[ 3 ], axis[ 3 ], lengths;
/* project triangle onto plane of projection */
pa = plane3_project_point( projection, a->xyz );
pb = plane3_project_point( projection, b->xyz );
pc = plane3_project_point( projection, c->xyz );
/* two methods */
#if 1
{
/* old code */
/* calculate barycentric basis for the triangle */
bb = ( b->st[ 0 ] - a->st[ 0 ] ) * ( c->st[ 1 ] - a->st[ 1 ] ) - ( c->st[ 0 ] - a->st[ 0 ] ) * ( b->st[ 1 ] - a->st[ 1 ] );
if ( std::fabs( bb ) < 0.00000001 ) {
return false;
}
/* calculate texture origin */
#if 0
s = 0;
t = 0;
bary[ 0 ] = ( ( b->st[ 0 ] - s ) * ( c->st[ 1 ] - t ) - ( c->st[ 0 ] - s ) * ( b->st[ 1 ] - t ) ) / bb;
bary[ 1 ] = ( ( c->st[ 0 ] - s ) * ( a->st[ 1 ] - t ) - ( a->st[ 0 ] - s ) * ( c->st[ 1 ] - t ) ) / bb;
bary[ 2 ] = ( ( a->st[ 0 ] - s ) * ( b->st[ 1 ] - t ) - ( b->st[ 0 ] - s ) * ( a->st[ 1 ] - t ) ) / bb;
origin[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
origin[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
origin[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
#endif
/* calculate s vector */
s = a->st[ 0 ] + 1.0;
t = a->st[ 1 ] + 0.0;
bary[ 0 ] = ( ( b->st[ 0 ] - s ) * ( c->st[ 1 ] - t ) - ( c->st[ 0 ] - s ) * ( b->st[ 1 ] - t ) ) / bb;
bary[ 1 ] = ( ( c->st[ 0 ] - s ) * ( a->st[ 1 ] - t ) - ( a->st[ 0 ] - s ) * ( c->st[ 1 ] - t ) ) / bb;
bary[ 2 ] = ( ( a->st[ 0 ] - s ) * ( b->st[ 1 ] - t ) - ( b->st[ 0 ] - s ) * ( a->st[ 1 ] - t ) ) / bb;
xyz[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
xyz[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
xyz[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
//% vecs[ 0 ] = xyz - origin;
vecs[ 0 ] = xyz - pa;
/* calculate t vector */
s = a->st[ 0 ] + 0.0;
t = a->st[ 1 ] + 1.0;
bary[ 0 ] = ( ( b->st[ 0 ] - s ) * ( c->st[ 1 ] - t ) - ( c->st[ 0 ] - s ) * ( b->st[ 1 ] - t ) ) / bb;
bary[ 1 ] = ( ( c->st[ 0 ] - s ) * ( a->st[ 1 ] - t ) - ( a->st[ 0 ] - s ) * ( c->st[ 1 ] - t ) ) / bb;
bary[ 2 ] = ( ( a->st[ 0 ] - s ) * ( b->st[ 1 ] - t ) - ( b->st[ 0 ] - s ) * ( a->st[ 1 ] - t ) ) / bb;
xyz[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
xyz[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
xyz[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
//% vecs[ 1 ] = xyz - origin;
vecs[ 1 ] = xyz - pa;
/* calcuate r vector */
vecs[ 2 ] = -projection.normal();
/* calculate transform axis */
for ( i = 0; i < 3; ++i ){
axis[ i ] = vecs[ i ];
lengths[ i ] = VectorNormalize( axis[ i ] );
}
for ( i = 0; i < 2; ++i )
for ( j = 0; j < 3; ++j )
dp->texMat[ i ][ j ] = lengths[ i ] != 0 ? ( axis[ i ][ j ] / lengths[ i ] ) : 0.0;
//% dp->texMat[ i ][ j ] = std::fabs( vecs[ i ][ j ] ) > 0 ? ( 1.0 / vecs[ i ][ j ] ) : 0.0;
//% dp->texMat[ i ][ j ] = axis[ i ][ j ] > 0 ? ( 1.0 / axis[ i ][ j ] ) : 0.0;
/* calculalate translation component */
dp->texMat[ 0 ][ 3 ] = a->st[ 0 ] - vector3_dot( a->xyz, dp->texMat[ 0 ].vec3() );
dp->texMat[ 1 ][ 3 ] = a->st[ 1 ] - vector3_dot( a->xyz, dp->texMat[ 1 ].vec3() );
}
#else
{
int k;
DoubleVector3 deltas[ 3 ];
BasicVector2<double> texDeltas[ 3 ];
double delta, texDelta;
/* new code */
/* calculate deltas */
deltas[ 0 ] = pa - pb;
deltas[ 1 ] = pa - pc;
deltas[ 2 ] = pb - pc;
texDeltas[ 0 ] = a->st - b->st;
texDeltas[ 1 ] = a->st - c->st;
texDeltas[ 2 ] = b->st - c->st;
/* walk st */
for ( i = 0; i < 2; ++i )
{
/* walk xyz */
for ( j = 0; j < 3; ++j )
{
/* clear deltas */
delta = 0;
texDelta = 0;
/* walk deltas */
for ( k = 0; k < 3; ++k )
{
if ( std::fabs( deltas[ k ][ j ] ) > delta &&
std::fabs( texDeltas[ k ][ i ] ) > texDelta ) {
delta = deltas[ k ][ j ];
texDelta = texDeltas[ k ][ i ];
}
}
/* set texture matrix component */
if ( std::fabs( delta ) > 0 ) {
dp->texMat[ i ][ j ] = texDelta / delta;
}
else{
dp->texMat[ i ][ j ] = 0;
}
}
/* set translation component */
dp->texMat[ i ][ 3 ] = a->st[ i ] - vector3_dot( pa, dp->texMat[ i ].vec3() );
}
}
#endif
/* debug code */
#if 1
Sys_Printf( "Mat: [ %f %f %f %f ] [ %f %f %f %f ] Theta: %lf (%lf)\n",
dp->texMat[ 0 ][ 0 ], dp->texMat[ 0 ][ 1 ], dp->texMat[ 0 ][ 2 ], dp->texMat[ 0 ][ 3 ],
dp->texMat[ 1 ][ 0 ], dp->texMat[ 1 ][ 1 ], dp->texMat[ 1 ][ 2 ], dp->texMat[ 1 ][ 3 ],
radians_to_degrees( acos( vector3_dot( dp->texMat[ 0 ].vec3(), dp->texMat[ 1 ].vec3() ) ) ),
radians_to_degrees( acos( vector3_dot( axis[ 0 ], axis[ 1 ] ) ) ) );
Sys_Printf( "XYZ: %f %f %f ST: %f %f ST(t): %lf %lf\n",
a->xyz[ 0 ], a->xyz[ 1 ], a->xyz[ 2 ],
a->st[ 0 ], a->st[ 1 ],
vector3_dot( a->xyz, dp->texMat[ 0 ].vec3() ) + dp->texMat[ 0 ][ 3 ], vector3_dot( a->xyz, dp->texMat[ 1 ].vec3() ) + dp->texMat[ 1 ][ 3 ] );
#endif
/* test texture matrix */
s = vector3_dot( a->xyz, dp->texMat[ 0 ].vec3() ) + dp->texMat[ 0 ][ 3 ];
t = vector3_dot( a->xyz, dp->texMat[ 1 ].vec3() ) + dp->texMat[ 1 ][ 3 ];
if ( !float_equal_epsilon( s, a->st[ 0 ], 0.01 ) || !float_equal_epsilon( t, a->st[ 1 ], 0.01 ) ) {
Sys_Printf( "Bad texture matrix! (A) (%f, %f) != (%f, %f)\n",
s, t, a->st[ 0 ], a->st[ 1 ] );
//% return false;
}
s = vector3_dot( b->xyz, dp->texMat[ 0 ].vec3() ) + dp->texMat[ 0 ][ 3 ];
t = vector3_dot( b->xyz, dp->texMat[ 1 ].vec3() ) + dp->texMat[ 1 ][ 3 ];
if ( !float_equal_epsilon( s, b->st[ 0 ], 0.01 ) || !float_equal_epsilon( t, b->st[ 1 ], 0.01 ) ) {
Sys_Printf( "Bad texture matrix! (B) (%f, %f) != (%f, %f)\n",
s, t, b->st[ 0 ], b->st[ 1 ] );
//% return false;
}
s = vector3_dot( c->xyz, dp->texMat[ 0 ].vec3() ) + dp->texMat[ 0 ][ 3 ];
t = vector3_dot( c->xyz, dp->texMat[ 1 ].vec3() ) + dp->texMat[ 1 ][ 3 ];
if ( !float_equal_epsilon( s, c->st[ 0 ], 0.01 ) || !float_equal_epsilon( t, c->st[ 1 ], 0.01 ) ) {
Sys_Printf( "Bad texture matrix! (C) (%f, %f) != (%f, %f)\n",
s, t, c->st[ 0 ], c->st[ 1 ] );
//% return false;
}
/* disco */
return true;
}
/*
TransformDecalProjector()
transforms a decal projector
note: non-normalized axes will screw up the plane transform
*/
static void TransformDecalProjector( decalProjector_t *in, const Vector3 (&axis)[ 3 ], const Vector3& origin, decalProjector_t *out ){
/* copy misc stuff */
out->si = in->si;
out->numPlanes = in->numPlanes;
/* translate bounding box and sphere (note: rotated projector bounding box will be invalid!) */
out->minmax.mins = in->minmax.mins - origin;
out->minmax.maxs = in->minmax.maxs - origin;
out->center = in->center - origin;
out->radius = in->radius;
out->radius2 = in->radius2;
/* translate planes */
for ( int i = 0; i < in->numPlanes; ++i )
{
out->planes[ i ].a = vector3_dot( in->planes[ i ].normal(), axis[ 0 ] );
out->planes[ i ].b = vector3_dot( in->planes[ i ].normal(), axis[ 1 ] );
out->planes[ i ].c = vector3_dot( in->planes[ i ].normal(), axis[ 2 ] );
out->planes[ i ].d = in->planes[ i ].dist() - vector3_dot( out->planes[ i ].normal(), origin );
}
/* translate texture matrix */
for ( int i = 0; i < 2; ++i )
{
out->texMat[ i ][ 0 ] = vector3_dot( in->texMat[ i ].vec3(), axis[ 0 ] );
out->texMat[ i ][ 1 ] = vector3_dot( in->texMat[ i ].vec3(), axis[ 1 ] );
out->texMat[ i ][ 2 ] = vector3_dot( in->texMat[ i ].vec3(), axis[ 2 ] );
out->texMat[ i ][ 3 ] = vector3_dot( out->texMat[ i ].vec3(), origin ) + in->texMat[ i ][ 3 ];
}
}
/*
MakeDecalProjector()
creates a new decal projector from a triangle
*/
static int MakeDecalProjector( shaderInfo_t *si, const Plane3f& projection, float distance, int numVerts, const bspDrawVert_t **dv ){
int i, j;
decalProjector_t *dp;
/* dummy check */
if ( numVerts != 3 && numVerts != 4 ) {
return -1;
}
/* limit check */
if ( numProjectors >= MAX_PROJECTORS ) {
Sys_Warning( "MAX_PROJECTORS (%d) exceeded, no more decal projectors available.\n", MAX_PROJECTORS );
return -2;
}
/* create a new projector */
dp = &projectors[ numProjectors ];
memset( dp, 0, sizeof( *dp ) );
/* basic setup */
dp->si = si;
dp->numPlanes = numVerts + 2;
/* make texture matrix */
if ( !MakeTextureMatrix( dp, projection, dv[ 0 ], dv[ 1 ], dv[ 2 ] ) ) {
return -1;
}
/* bound the projector */
dp->minmax.clear();
for ( i = 0; i < numVerts; ++i )
{
dp->minmax.extend( dv[ i ]->xyz );
dp->minmax.extend( dv[ i ]->xyz + projection.normal() * distance );
}
/* make bouding sphere */
dp->center = dp->minmax.origin();
dp->radius = vector3_length( dp->minmax.maxs - dp->center );
dp->radius2 = dp->radius * dp->radius;
/* make the front plane */
if ( !PlaneFromPoints( dp->planes[ 0 ], dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) ) {
return -1;
}
/* make the back plane */
dp->planes[ 1 ].normal() = -dp->planes[ 0 ].normal();
dp->planes[ 1 ].dist() = vector3_dot( dv[ 0 ]->xyz + projection.normal() * distance, dp->planes[ 1 ].normal() );
/* make the side planes */
for ( i = 0; i < numVerts; ++i )
{
j = ( i + 1 ) % numVerts;
if ( !PlaneFromPoints( dp->planes[ i + 2 ], dv[ j ]->xyz, dv[ i ]->xyz, dv[ i ]->xyz + projection.normal() * distance ) ) {
return -1;
}
}
/* return ok */
return numProjectors++;
}
/*
ProcessDecals()
finds all decal entities and creates decal projectors
*/
#define PLANAR_EPSILON 0.5f
void ProcessDecals(){
float distance;
Plane3f projection, plane;
/* note it */
Sys_FPrintf( SYS_VRB, "--- ProcessDecals ---\n" );
/* walk entity list */
for ( auto& e : entities )
{
if ( !e.classname_is( "_decal" ) ) {
continue;
}
/* any patches? */
if ( e.patches.empty() ) {
Sys_Warning( "Decal entity without any patch meshes, ignoring.\n" );
e.epairs.clear();
continue;
}
/* find target */
entity_t *e2 = FindTargetEntity( e.valueForKey( "target" ) );
/* no target? */
if ( e2 == nullptr ) {
Sys_Warning( "Decal entity without a valid target, ignoring.\n" );
continue;
}
/* walk entity patches */
for ( parseMesh_t& p : e.patches )
{
/* setup projector */
Vector3 origin;
if ( VectorCompare( e.origin, g_vector3_identity ) ) {
origin = p.eMinmax.origin();
}
else{
origin = e.origin;
}
/* setup projection plane */
projection.normal() = e2->origin - origin;
distance = VectorNormalize( projection.normal() );
projection.dist() = vector3_dot( origin, projection.normal() );
/* create projectors */
if ( distance > 0.125f ) {
/* tesselate the patch */
mesh_t mesh = TessellatedMesh( p.mesh, IterationsForCurve( p.longestCurve, patchSubdivisions ) );
/* offset by projector origin */
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 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 ] */
};
/* 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 ] ]
};
/* 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 ) &&
std::fabs( plane3_distance_to_point( plane, dv[ 1 ]->xyz ) ) <= PLANAR_EPSILON ) {
/* make a quad projector */
MakeDecalProjector( p.shaderInfo, projection, distance, 4, dv );
}
else
{
/* make first triangle */
MakeDecalProjector( p.shaderInfo, projection, distance, 3, dv );
/* make second triangle */
dv[ 1 ] = dv[ 2 ];
dv[ 2 ] = dv[ 3 ];
MakeDecalProjector( p.shaderInfo, projection, distance, 3, dv );
}
}
}
/* clean up */
mesh.freeVerts();
}
}
/* remove patches from entity */
e.patches.clear();
/* push patch to worldspawn (enable this to debug projectors) */
#if 0
entities[ 0 ].patches.splice_after( entities[ 0 ].patches.cbefore_begin(), e.patches );
#endif
}
/* emit some stats */
Sys_FPrintf( SYS_VRB, "%9d decal projectors\n", numProjectors );
}
/*
ProjectDecalOntoWinding()
projects a decal onto a winding
*/
static void ProjectDecalOntoWinding( decalProjector_t& dp, const mapDrawSurface_t& ds, winding_t& w ){
/* dummy check */
if ( w.size() < 3 ) {
return;
}
/* offset by entity origin */
for ( Vector3& p : w )
p += entityOrigin;
/* make a plane from the winding */
Plane3f plane;
if ( !PlaneFromPoints( plane, w.data() ) ) {
return;
}
/* backface check */
if ( vector3_dot( dp.planes[ 0 ].normal(), plane.normal() ) < -0.0001f ) {
return;
}
/* walk list of planes */
for ( int i = 0; i < dp.numPlanes; ++i )
{
/* chop winding by the plane */
auto [front, back] = ClipWindingEpsilonStrict( w, dp.planes[ i ], 0.0625f ); /* strict, if identical plane we don't want to keep it */
/* lose the front fragment */
/* if nothing left in back, then bail */
if ( back.empty() ) {
return;
}
/* reset winding */
w.swap( back );
}
/* nothing left? */
if ( w.size() < 3 ) {
return;
}
/* add to counts */
numDecalSurfaces++;
/* make a new surface */
mapDrawSurface_t& ds2 = AllocDrawSurface( ESurfaceType::Decal );
/* set it up */
ds2.entityNum = ds.entityNum;
ds2.castShadows = ds.castShadows;
ds2.recvShadows = ds.recvShadows;
ds2.shaderInfo = dp.si;
ds2.fogNum = ds.fogNum; /* why was this -1? */
ds2.lightmapScale = ds.lightmapScale;
ds2.shadeAngleDegrees = ds.shadeAngleDegrees;
ds2.ambientColor = ds.ambientColor;
ds2.verts.resize( w.size(), c_bspDrawVert_t0 );
/* set vertexes */
for ( size_t i = 0; i < w.size(); ++i )
{
/* get vertex */
bspDrawVert_t& dv = ds2.verts[ i ];
/* set alpha */
const float d = plane3_distance_to_point( dp.planes[ 0 ], w[ i ] );
const float d2 = plane3_distance_to_point( dp.planes[ 1 ], w[ i ] );
const float alpha = 255.0f * d2 / ( d + d2 );
/* set misc */
dv.xyz = w[ i ] - entityOrigin;
dv.normal = plane.normal();
dv.st[ 0 ] = vector3_dot( dv.xyz, dp.texMat[ 0 ].vec3() ) + dp.texMat[ 0 ][ 3 ];
dv.st[ 1 ] = vector3_dot( dv.xyz, dp.texMat[ 1 ].vec3() ) + dp.texMat[ 1 ][ 3 ];
/* set color */
for ( int j = 0; j < MAX_LIGHTMAPS; ++j )
{
dv.color[ j ] = { 255, 255, 255, color_to_byte( alpha ) };
}
}
}
/*
ProjectDecalOntoFace()
projects a decal onto a brushface surface
*/
static void ProjectDecalOntoFace( decalProjector_t& dp, const mapDrawSurface_t& ds ){
/* dummy check */
if ( ds.sideRef == nullptr ) {
return;
}
/* backface check */
if ( ds.planar ) {
if ( vector3_dot( dp.planes[ 0 ].normal(), mapplanes[ ds.planeNum ].normal() ) < -0.0001f ) {
return;
}
}
/* generate decal */
winding_t w = WindingFromDrawSurf( ds );
ProjectDecalOntoWinding( dp, ds, w );
}
/*
ProjectDecalOntoPatch()
projects a decal onto a patch surface
*/
static void ProjectDecalOntoPatch( decalProjector_t& dp, mapDrawSurface_t& ds ){
/* backface check */
if ( ds.planar )
if ( vector3_dot( dp.planes[ 0 ].normal(), mapplanes[ ds.planeNum ].normal() ) < -0.0001f )
return;
/* tesselate the patch */
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 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 ] */
};
/* 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 };
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 };
ProjectDecalOntoWinding( dp, ds, w2 );
}
}
/* clean up */
mesh.freeVerts();
}
/*
ProjectDecalOntoTriangles()
projects a decal onto a triangle surface
*/
static void ProjectDecalOntoTriangles( decalProjector_t& dp, const mapDrawSurface_t& ds ){
/* triangle surfaces without shaders don't get marks by default */
if ( ds.type == ESurfaceType::Triangles && ds.shaderInfo->shaderText == nullptr ) {
return;
}
/* backface check */
if ( ds.planar ) {
if ( vector3_dot( dp.planes[ 0 ].normal(), mapplanes[ ds.planeNum ].normal() ) < -0.0001f ) {
return;
}
}
/* iterate through triangles */
for ( auto i = ds.indexes.cbegin(); i != ds.indexes.cend(); i += 3 )
{
/* generate decal */
winding_t w{
ds.verts[ *( i + 0 ) ].xyz,
ds.verts[ *( i + 1 ) ].xyz,
ds.verts[ *( i + 2 ) ].xyz };
ProjectDecalOntoWinding( dp, ds, w );
}
}
/*
MakeEntityDecals()
projects decals onto world surfaces
*/
void MakeEntityDecals( const entity_t& e ){
int i, j, fOld;
decalProjector_t dp;
/* note it */
Sys_FPrintf( SYS_VRB, "--- MakeEntityDecals ---\n" );
/* transform projector instead of geometry */
entityOrigin.set( 0 );
/* init pacifier */
fOld = -1;
Timer timer;
/* walk the list of decal projectors */
for ( i = 0; i < numProjectors; ++i )
{
/* print pacifier */
if ( const int f = 10 * i / numProjectors; f != fOld ) {
fOld = f;
Sys_FPrintf( SYS_VRB, "%d...", f );
}
/* get projector */
TransformDecalProjector( &projectors[ i ], g_vector3_axes, e.origin, &dp );
/* walk the list of surfaces in the entity */
for ( j = e.firstDrawSurf; j < numMapDrawSurfs; ++j )
{
/* get surface */
mapDrawSurface_t& ds = mapDrawSurfs[ j ];
if ( ds.verts.empty() ) {
continue;
}
/* ignore autosprite or nomarks */
if ( ds.shaderInfo->autosprite || ( ds.shaderInfo->compileFlags & C_NOMARKS ) ) {
continue;
}
/* bounds check */
if ( !ds.minmax.test( dp.center, dp.radius ) ) {
continue;
}
/* switch on type */
switch ( ds.type )
{
case ESurfaceType::Face:
ProjectDecalOntoFace( dp, ds );
break;
case ESurfaceType::Patch:
ProjectDecalOntoPatch( dp, ds );
break;
case ESurfaceType::Triangles:
case ESurfaceType::ForcedMeta:
case ESurfaceType::Meta:
ProjectDecalOntoTriangles( dp, ds );
break;
default:
break;
}
}
}
/* print time */
Sys_FPrintf( SYS_VRB, " (%d)\n", int( timer.elapsed_sec() ) );
/* emit some stats */
Sys_FPrintf( SYS_VRB, "%9d decal surfaces\n", numDecalSurfaces );
}