mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-11-24 04:51:33 +00:00
756 lines
26 KiB
C++
756 lines
26 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "../idlib/precompiled.h"
|
|
|
|
#include "tr_local.h"
|
|
#include "Model_local.h"
|
|
|
|
#include "../idlib/geometry/DrawVert_intrinsics.h"
|
|
|
|
// decalFade filter 5 0.1
|
|
// polygonOffset
|
|
// {
|
|
// map invertColor( textures/splat )
|
|
// blend GL_ZERO GL_ONE_MINUS_SRC
|
|
// vertexColor
|
|
// clamp
|
|
// }
|
|
|
|
/*
|
|
==================
|
|
idRenderModelDecal::idRenderModelDecal
|
|
==================
|
|
*/
|
|
idRenderModelDecal::idRenderModelDecal() :
|
|
firstDecal( 0 ),
|
|
nextDecal( 0 ),
|
|
firstDeferredDecal( 0 ),
|
|
nextDeferredDecal( 0 ),
|
|
numDecalMaterials( 0 ) {
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idRenderModelDecal::~idRenderModelDecal
|
|
==================
|
|
*/
|
|
idRenderModelDecal::~idRenderModelDecal() {
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderModelDecal::CreateProjectionParms
|
|
=================
|
|
*/
|
|
bool idRenderModelDecal::CreateProjectionParms( decalProjectionParms_t &parms, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) {
|
|
|
|
if ( winding.GetNumPoints() != NUM_DECAL_BOUNDING_PLANES - 2 ) {
|
|
common->Printf( "idRenderModelDecal::CreateProjectionInfo: winding must have %d points\n", NUM_DECAL_BOUNDING_PLANES - 2 );
|
|
return false;
|
|
}
|
|
|
|
assert( material != NULL );
|
|
|
|
parms.projectionOrigin = projectionOrigin;
|
|
parms.material = material;
|
|
parms.parallel = parallel;
|
|
parms.fadeDepth = fadeDepth;
|
|
parms.startTime = startTime;
|
|
parms.force = false;
|
|
|
|
// get the winding plane and the depth of the projection volume
|
|
idPlane windingPlane;
|
|
winding.GetPlane( windingPlane );
|
|
float depth = windingPlane.Distance( projectionOrigin );
|
|
|
|
// find the bounds for the projection
|
|
winding.GetBounds( parms.projectionBounds );
|
|
if ( parallel ) {
|
|
parms.projectionBounds.ExpandSelf( depth );
|
|
} else {
|
|
parms.projectionBounds.AddPoint( projectionOrigin );
|
|
}
|
|
|
|
// calculate the world space projection volume bounding planes, positive sides face outside the decal
|
|
if ( parallel ) {
|
|
for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
|
|
idVec3 edge = winding[( i + 1 ) % winding.GetNumPoints()].ToVec3() - winding[i].ToVec3();
|
|
parms.boundingPlanes[i].Normal().Cross( windingPlane.Normal(), edge );
|
|
parms.boundingPlanes[i].Normalize();
|
|
parms.boundingPlanes[i].FitThroughPoint( winding[i].ToVec3() );
|
|
}
|
|
} else {
|
|
for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
|
|
parms.boundingPlanes[i].FromPoints( projectionOrigin, winding[i].ToVec3(), winding[(i+1)%winding.GetNumPoints()].ToVec3() );
|
|
}
|
|
}
|
|
parms.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2] = windingPlane;
|
|
parms.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2][3] -= depth;
|
|
parms.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 1] = -windingPlane;
|
|
|
|
// fades will be from these plane
|
|
parms.fadePlanes[0] = windingPlane;
|
|
parms.fadePlanes[0][3] -= fadeDepth;
|
|
parms.fadePlanes[1] = -windingPlane;
|
|
parms.fadePlanes[1][3] += depth - fadeDepth;
|
|
|
|
// calculate the texture vectors for the winding
|
|
float len, texArea, inva;
|
|
idVec3 temp;
|
|
idVec5 d0, d1;
|
|
|
|
const idVec5 &a = winding[0];
|
|
const idVec5 &b = winding[1];
|
|
const idVec5 &c = winding[2];
|
|
|
|
d0 = b.ToVec3() - a.ToVec3();
|
|
d0.s = b.s - a.s;
|
|
d0.t = b.t - a.t;
|
|
d1 = c.ToVec3() - a.ToVec3();
|
|
d1.s = c.s - a.s;
|
|
d1.t = c.t - a.t;
|
|
|
|
texArea = ( d0[3] * d1[4] ) - ( d0[4] * d1[3] );
|
|
inva = 1.0f / texArea;
|
|
|
|
temp[0] = ( d0[0] * d1[4] - d0[4] * d1[0] ) * inva;
|
|
temp[1] = ( d0[1] * d1[4] - d0[4] * d1[1] ) * inva;
|
|
temp[2] = ( d0[2] * d1[4] - d0[4] * d1[2] ) * inva;
|
|
len = temp.Normalize();
|
|
parms.textureAxis[0].Normal() = temp * ( 1.0f / len );
|
|
parms.textureAxis[0][3] = winding[0].s - ( winding[0].ToVec3() * parms.textureAxis[0].Normal() );
|
|
|
|
temp[0] = ( d0[3] * d1[0] - d0[0] * d1[3] ) * inva;
|
|
temp[1] = ( d0[3] * d1[1] - d0[1] * d1[3] ) * inva;
|
|
temp[2] = ( d0[3] * d1[2] - d0[2] * d1[3] ) * inva;
|
|
len = temp.Normalize();
|
|
parms.textureAxis[1].Normal() = temp * ( 1.0f / len );
|
|
parms.textureAxis[1][3] = winding[0].t - ( winding[0].ToVec3() * parms.textureAxis[1].Normal() );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderModelDecal::GlobalProjectionParmsToLocal
|
|
=================
|
|
*/
|
|
void idRenderModelDecal::GlobalProjectionParmsToLocal( decalProjectionParms_t &localParms, const decalProjectionParms_t &globalParms, const idVec3 &origin, const idMat3 &axis ) {
|
|
float modelMatrix[16];
|
|
|
|
R_AxisToModelMatrix( axis, origin, modelMatrix );
|
|
|
|
for ( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) {
|
|
R_GlobalPlaneToLocal( modelMatrix, globalParms.boundingPlanes[j], localParms.boundingPlanes[j] );
|
|
}
|
|
R_GlobalPlaneToLocal( modelMatrix, globalParms.fadePlanes[0], localParms.fadePlanes[0] );
|
|
R_GlobalPlaneToLocal( modelMatrix, globalParms.fadePlanes[1], localParms.fadePlanes[1] );
|
|
R_GlobalPlaneToLocal( modelMatrix, globalParms.textureAxis[0], localParms.textureAxis[0] );
|
|
R_GlobalPlaneToLocal( modelMatrix, globalParms.textureAxis[1], localParms.textureAxis[1] );
|
|
R_GlobalPointToLocal( modelMatrix, globalParms.projectionOrigin, localParms.projectionOrigin );
|
|
localParms.projectionBounds = globalParms.projectionBounds;
|
|
localParms.projectionBounds.TranslateSelf( -origin );
|
|
localParms.projectionBounds.RotateSelf( axis.Transpose() );
|
|
localParms.material = globalParms.material;
|
|
localParms.parallel = globalParms.parallel;
|
|
localParms.fadeDepth = globalParms.fadeDepth;
|
|
localParms.startTime = globalParms.startTime;
|
|
localParms.force = globalParms.force;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderModelDecal::ReUse
|
|
=================
|
|
*/
|
|
void idRenderModelDecal::ReUse() {
|
|
firstDecal = 0;
|
|
nextDecal = 0;
|
|
firstDeferredDecal = 0;
|
|
nextDeferredDecal = 0;
|
|
numDecalMaterials = 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderModelDecal::CreateDecalFromWinding
|
|
=================
|
|
*/
|
|
void idRenderModelDecal::CreateDecalFromWinding( const idWinding &w, const idMaterial *decalMaterial, const idPlane fadePlanes[2], float fadeDepth, int startTime ) {
|
|
// Often we are appending a new triangle to an existing decal, so merge with the previous decal if possible
|
|
int decalIndex = ( nextDecal - 1 ) & ( MAX_DECALS - 1 );
|
|
if ( decalIndex >= 0
|
|
&& decals[decalIndex].material == decalMaterial
|
|
&& decals[decalIndex].startTime == startTime
|
|
&& decals[decalIndex].numVerts + w.GetNumPoints() <= MAX_DECAL_VERTS
|
|
&& decals[decalIndex].numIndexes + 3 * ( w.GetNumPoints() - 2 ) <= MAX_DECAL_INDEXES ) {
|
|
} else {
|
|
decalIndex = nextDecal++ & ( MAX_DECALS - 1 );
|
|
decals[decalIndex].material = decalMaterial;
|
|
decals[decalIndex].startTime = startTime;
|
|
decals[decalIndex].numVerts = 0;
|
|
decals[decalIndex].numIndexes = 0;
|
|
assert( w.GetNumPoints() <= MAX_DECAL_VERTS );
|
|
if ( nextDecal - firstDecal > MAX_DECALS ) {
|
|
firstDecal = nextDecal - MAX_DECALS;
|
|
}
|
|
}
|
|
|
|
decal_t & decal = decals[decalIndex];
|
|
|
|
const float invFadeDepth = -1.0f / fadeDepth;
|
|
|
|
int firstVert = decal.numVerts;
|
|
|
|
// create the vertices
|
|
for ( int i = 0; i < w.GetNumPoints(); i++ ) {
|
|
float depthFade = fadePlanes[0].Distance( w[i].ToVec3() ) * invFadeDepth;
|
|
if ( depthFade < 0.0f ) {
|
|
depthFade = fadePlanes[1].Distance( w[i].ToVec3() ) * invFadeDepth;
|
|
}
|
|
if ( depthFade < 0.0f ) {
|
|
depthFade = 0.0f;
|
|
} else if ( depthFade > 0.99f ) {
|
|
depthFade = 1.0f;
|
|
}
|
|
decal.vertDepthFade[decal.numVerts] = 1.0f - depthFade;
|
|
decal.verts[decal.numVerts].Clear();
|
|
decal.verts[decal.numVerts].xyz = w[i].ToVec3();
|
|
decal.verts[decal.numVerts].SetTexCoord( w[i].s, w[i].t );
|
|
decal.numVerts++;
|
|
}
|
|
|
|
// create the indexes
|
|
for ( int i = 2; i < w.GetNumPoints(); i++ ) {
|
|
assert( decal.numIndexes + 3 <= MAX_DECAL_INDEXES );
|
|
decal.indexes[decal.numIndexes + 0] = firstVert;
|
|
decal.indexes[decal.numIndexes + 1] = firstVert + i - 1;
|
|
decal.indexes[decal.numIndexes + 2] = firstVert + i;
|
|
decal.numIndexes += 3;
|
|
}
|
|
|
|
// add degenerate triangles until the index size is a multiple of 16 bytes
|
|
for ( ; ( ( ( decal.numIndexes * sizeof( triIndex_t ) ) & 15 ) != 0 ); decal.numIndexes += 3 ) {
|
|
assert( decal.numIndexes + 3 <= MAX_DECAL_INDEXES );
|
|
decal.indexes[decal.numIndexes + 0] = 0;
|
|
decal.indexes[decal.numIndexes + 1] = 0;
|
|
decal.indexes[decal.numIndexes + 2] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
R_DecalPointCullStatic
|
|
============
|
|
*/
|
|
static void R_DecalPointCullStatic( byte * cullBits, const idPlane * planes, const idDrawVert * verts, const int numVerts ) {
|
|
assert_16_byte_aligned( cullBits );
|
|
assert_16_byte_aligned( verts );
|
|
|
|
|
|
idODSStreamedArray< idDrawVert, 16, SBT_DOUBLE, 4 > vertsODS( verts, numVerts );
|
|
|
|
const __m128 vector_float_zero = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
const __m128i vector_int_mask0 = _mm_set1_epi32( 1 << 0 );
|
|
const __m128i vector_int_mask1 = _mm_set1_epi32( 1 << 1 );
|
|
const __m128i vector_int_mask2 = _mm_set1_epi32( 1 << 2 );
|
|
const __m128i vector_int_mask3 = _mm_set1_epi32( 1 << 3 );
|
|
const __m128i vector_int_mask4 = _mm_set1_epi32( 1 << 4 );
|
|
const __m128i vector_int_mask5 = _mm_set1_epi32( 1 << 5 );
|
|
|
|
const __m128 p0 = _mm_loadu_ps( planes[0].ToFloatPtr() );
|
|
const __m128 p1 = _mm_loadu_ps( planes[1].ToFloatPtr() );
|
|
const __m128 p2 = _mm_loadu_ps( planes[2].ToFloatPtr() );
|
|
const __m128 p3 = _mm_loadu_ps( planes[3].ToFloatPtr() );
|
|
const __m128 p4 = _mm_loadu_ps( planes[4].ToFloatPtr() );
|
|
const __m128 p5 = _mm_loadu_ps( planes[5].ToFloatPtr() );
|
|
|
|
const __m128 p0X = _mm_splat_ps( p0, 0 );
|
|
const __m128 p0Y = _mm_splat_ps( p0, 1 );
|
|
const __m128 p0Z = _mm_splat_ps( p0, 2 );
|
|
const __m128 p0W = _mm_splat_ps( p0, 3 );
|
|
|
|
const __m128 p1X = _mm_splat_ps( p1, 0 );
|
|
const __m128 p1Y = _mm_splat_ps( p1, 1 );
|
|
const __m128 p1Z = _mm_splat_ps( p1, 2 );
|
|
const __m128 p1W = _mm_splat_ps( p1, 3 );
|
|
|
|
const __m128 p2X = _mm_splat_ps( p2, 0 );
|
|
const __m128 p2Y = _mm_splat_ps( p2, 1 );
|
|
const __m128 p2Z = _mm_splat_ps( p2, 2 );
|
|
const __m128 p2W = _mm_splat_ps( p2, 3 );
|
|
|
|
const __m128 p3X = _mm_splat_ps( p3, 0 );
|
|
const __m128 p3Y = _mm_splat_ps( p3, 1 );
|
|
const __m128 p3Z = _mm_splat_ps( p3, 2 );
|
|
const __m128 p3W = _mm_splat_ps( p3, 3 );
|
|
|
|
const __m128 p4X = _mm_splat_ps( p4, 0 );
|
|
const __m128 p4Y = _mm_splat_ps( p4, 1 );
|
|
const __m128 p4Z = _mm_splat_ps( p4, 2 );
|
|
const __m128 p4W = _mm_splat_ps( p4, 3 );
|
|
|
|
const __m128 p5X = _mm_splat_ps( p5, 0 );
|
|
const __m128 p5Y = _mm_splat_ps( p5, 1 );
|
|
const __m128 p5Z = _mm_splat_ps( p5, 2 );
|
|
const __m128 p5W = _mm_splat_ps( p5, 3 );
|
|
|
|
for ( int i = 0; i < numVerts; ) {
|
|
|
|
const int nextNumVerts = vertsODS.FetchNextBatch() - 4;
|
|
|
|
for ( ; i <= nextNumVerts; i += 4 ) {
|
|
const __m128 v0 = _mm_load_ps( vertsODS[i + 0].xyz.ToFloatPtr() );
|
|
const __m128 v1 = _mm_load_ps( vertsODS[i + 1].xyz.ToFloatPtr() );
|
|
const __m128 v2 = _mm_load_ps( vertsODS[i + 2].xyz.ToFloatPtr() );
|
|
const __m128 v3 = _mm_load_ps( vertsODS[i + 3].xyz.ToFloatPtr() );
|
|
|
|
const __m128 r0 = _mm_unpacklo_ps( v0, v2 ); // v0.x, v2.x, v0.z, v2.z
|
|
const __m128 r1 = _mm_unpackhi_ps( v0, v2 ); // v0.y, v2.y, v0.w, v2.w
|
|
const __m128 r2 = _mm_unpacklo_ps( v1, v3 ); // v1.x, v3.x, v1.z, v3.z
|
|
const __m128 r3 = _mm_unpackhi_ps( v1, v3 ); // v1.y, v3.y, v1.w, v3.w
|
|
|
|
const __m128 vX = _mm_unpacklo_ps( r0, r2 ); // v0.x, v1.x, v2.x, v3.x
|
|
const __m128 vY = _mm_unpackhi_ps( r0, r2 ); // v0.y, v1.y, v2.y, v3.y
|
|
const __m128 vZ = _mm_unpacklo_ps( r1, r3 ); // v0.z, v1.z, v2.z, v3.z
|
|
|
|
const __m128 d0 = _mm_madd_ps( vX, p0X, _mm_madd_ps( vY, p0Y, _mm_madd_ps( vZ, p0Z, p0W ) ) );
|
|
const __m128 d1 = _mm_madd_ps( vX, p1X, _mm_madd_ps( vY, p1Y, _mm_madd_ps( vZ, p1Z, p1W ) ) );
|
|
const __m128 d2 = _mm_madd_ps( vX, p2X, _mm_madd_ps( vY, p2Y, _mm_madd_ps( vZ, p2Z, p2W ) ) );
|
|
const __m128 d3 = _mm_madd_ps( vX, p3X, _mm_madd_ps( vY, p3Y, _mm_madd_ps( vZ, p3Z, p3W ) ) );
|
|
const __m128 d4 = _mm_madd_ps( vX, p4X, _mm_madd_ps( vY, p4Y, _mm_madd_ps( vZ, p4Z, p4W ) ) );
|
|
const __m128 d5 = _mm_madd_ps( vX, p5X, _mm_madd_ps( vY, p5Y, _mm_madd_ps( vZ, p5Z, p5W ) ) );
|
|
|
|
__m128i c0 = __m128c( _mm_cmpgt_ps( d0, vector_float_zero ) );
|
|
__m128i c1 = __m128c( _mm_cmpgt_ps( d1, vector_float_zero ) );
|
|
__m128i c2 = __m128c( _mm_cmpgt_ps( d2, vector_float_zero ) );
|
|
__m128i c3 = __m128c( _mm_cmpgt_ps( d3, vector_float_zero ) );
|
|
__m128i c4 = __m128c( _mm_cmpgt_ps( d4, vector_float_zero ) );
|
|
__m128i c5 = __m128c( _mm_cmpgt_ps( d5, vector_float_zero ) );
|
|
|
|
c0 = _mm_and_si128( c0, vector_int_mask0 );
|
|
c1 = _mm_and_si128( c1, vector_int_mask1 );
|
|
c2 = _mm_and_si128( c2, vector_int_mask2 );
|
|
c3 = _mm_and_si128( c3, vector_int_mask3 );
|
|
c4 = _mm_and_si128( c4, vector_int_mask4 );
|
|
c5 = _mm_and_si128( c5, vector_int_mask5 );
|
|
|
|
c0 = _mm_or_si128( c0, c1 );
|
|
c2 = _mm_or_si128( c2, c3 );
|
|
c4 = _mm_or_si128( c4, c5 );
|
|
|
|
c0 = _mm_or_si128( c0, c2 );
|
|
c0 = _mm_or_si128( c0, c4 );
|
|
|
|
__m128i s0 = _mm_packs_epi32( c0, c0 );
|
|
__m128i b0 = _mm_packus_epi16( s0, s0 );
|
|
|
|
*(unsigned int *)&cullBits[i] = _mm_cvtsi128_si32( b0 );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderModelDecal::CreateDecal
|
|
=================
|
|
*/
|
|
void idRenderModelDecal::CreateDecal( const idRenderModel *model, const decalProjectionParms_t &localParms ) {
|
|
int maxVerts = 0;
|
|
for ( int surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) {
|
|
const modelSurface_t *surf = model->Surface( surfNum );
|
|
if ( surf->geometry != NULL && surf->shader != NULL ) {
|
|
maxVerts = Max( maxVerts, surf->geometry->numVerts );
|
|
}
|
|
}
|
|
|
|
idTempArray< byte > cullBits( ALIGN( maxVerts, 4 ) );
|
|
|
|
// check all model surfaces
|
|
for ( int surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) {
|
|
const modelSurface_t *surf = model->Surface( surfNum );
|
|
|
|
// if no geometry or no shader
|
|
if ( surf->geometry == NULL || surf->shader == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
// decals and overlays use the same rules
|
|
if ( !localParms.force && !surf->shader->AllowOverlays() ) {
|
|
continue;
|
|
}
|
|
|
|
srfTriangles_t *tri = surf->geometry;
|
|
|
|
// if the triangle bounds do not overlap with the projection bounds
|
|
if ( !localParms.projectionBounds.IntersectsBounds( tri->bounds ) ) {
|
|
continue;
|
|
}
|
|
|
|
// decals don't work on animated models
|
|
assert( tri->staticModelWithJoints == NULL );
|
|
|
|
// catagorize all points by the planes
|
|
R_DecalPointCullStatic( cullBits.Ptr(), localParms.boundingPlanes, tri->verts, tri->numVerts );
|
|
|
|
// start streaming the indexes
|
|
idODSStreamedArray< triIndex_t, 256, SBT_QUAD, 3 > indexesODS( tri->indexes, tri->numIndexes );
|
|
|
|
// find triangles inside the projection volume
|
|
for ( int i = 0; i < tri->numIndexes; ) {
|
|
|
|
const int nextNumIndexes = indexesODS.FetchNextBatch() - 3;
|
|
|
|
for ( ; i <= nextNumIndexes; i += 3 ) {
|
|
const int i0 = indexesODS[i + 0];
|
|
const int i1 = indexesODS[i + 1];
|
|
const int i2 = indexesODS[i + 2];
|
|
|
|
// skip triangles completely off one side
|
|
if ( cullBits[i0] & cullBits[i1] & cullBits[i2] ) {
|
|
continue;
|
|
}
|
|
|
|
const idDrawVert * verts[3] = {
|
|
&tri->verts[i0],
|
|
&tri->verts[i1],
|
|
&tri->verts[i2]
|
|
};
|
|
|
|
// skip back facing triangles
|
|
const idPlane plane( verts[0]->xyz, verts[1]->xyz, verts[2]->xyz );
|
|
if ( plane.Normal() * localParms.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2].Normal() < -0.1f ) {
|
|
continue;
|
|
}
|
|
|
|
// create a winding with texture coordinates for the triangle
|
|
idFixedWinding fw;
|
|
fw.SetNumPoints( 3 );
|
|
if ( localParms.parallel ) {
|
|
for ( int j = 0; j < 3; j++ ) {
|
|
fw[j] = verts[j]->xyz;
|
|
fw[j].s = localParms.textureAxis[0].Distance( verts[j]->xyz );
|
|
fw[j].t = localParms.textureAxis[1].Distance( verts[j]->xyz );
|
|
}
|
|
} else {
|
|
for ( int j = 0; j < 3; j++ ) {
|
|
const idVec3 dir = verts[j]->xyz - localParms.projectionOrigin;
|
|
float scale;
|
|
localParms.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 1].RayIntersection( verts[j]->xyz, dir, scale );
|
|
const idVec3 intersection = verts[j]->xyz + scale * dir;
|
|
|
|
fw[j] = verts[j]->xyz;
|
|
fw[j].s = localParms.textureAxis[0].Distance( intersection );
|
|
fw[j].t = localParms.textureAxis[1].Distance( intersection );
|
|
}
|
|
}
|
|
|
|
const int orBits = cullBits[i0] | cullBits[i1] | cullBits[i2];
|
|
|
|
// clip the exact surface triangle to the projection volume
|
|
for ( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) {
|
|
if ( ( orBits & ( 1 << j ) ) != 0 ) {
|
|
if ( !fw.ClipInPlace( -localParms.boundingPlanes[j] ) ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there is a part of the triangle between the bounding planes then clip
|
|
// the triangle based on depth and add decals for the depth faded parts
|
|
if ( fw.GetNumPoints() != 0 ) {
|
|
idFixedWinding back;
|
|
|
|
if ( fw.Split( &back, localParms.fadePlanes[0], 0.1f ) == SIDE_CROSS ) {
|
|
CreateDecalFromWinding( back, localParms.material, localParms.fadePlanes, localParms.fadeDepth, localParms.startTime );
|
|
}
|
|
|
|
if ( fw.Split( &back, localParms.fadePlanes[1], 0.1f ) == SIDE_CROSS ) {
|
|
CreateDecalFromWinding( back, localParms.material, localParms.fadePlanes, localParms.fadeDepth, localParms.startTime );
|
|
}
|
|
|
|
CreateDecalFromWinding( fw, localParms.material, localParms.fadePlanes, localParms.fadeDepth, localParms.startTime );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderModelDecal::CreateDeferredDecals
|
|
=====================
|
|
*/
|
|
void idRenderModelDecal::CreateDeferredDecals( const idRenderModel *model ) {
|
|
for ( unsigned int i = firstDeferredDecal; i < nextDeferredDecal; i++ ) {
|
|
decalProjectionParms_t & parms = deferredDecals[i & ( MAX_DEFERRED_DECALS - 1 )];
|
|
if ( parms.startTime > tr.viewDef->renderView.time[0] - DEFFERED_DECAL_TIMEOUT ) {
|
|
CreateDecal( model, parms );
|
|
}
|
|
}
|
|
firstDeferredDecal = 0;
|
|
nextDeferredDecal = 0;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderModelDecal::AddDeferredDecal
|
|
=====================
|
|
*/
|
|
void idRenderModelDecal::AddDeferredDecal( const decalProjectionParms_t &localParms ) {
|
|
deferredDecals[nextDeferredDecal++ & ( MAX_DEFERRED_DECALS - 1 )] = localParms;
|
|
if ( nextDeferredDecal - firstDeferredDecal > MAX_DEFERRED_DECALS ) {
|
|
firstDeferredDecal = nextDeferredDecal - MAX_DEFERRED_DECALS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderModelDecal::RemoveFadedDecals
|
|
=====================
|
|
*/
|
|
void idRenderModelDecal::RemoveFadedDecals( int time ) {
|
|
for ( unsigned int i = firstDecal; i < nextDecal; i++ ) {
|
|
decal_t & decal = decals[i & ( MAX_DECALS - 1 )];
|
|
|
|
const decalInfo_t decalInfo = decal.material->GetDecalInfo();
|
|
const int minTime = time - ( decalInfo.stayTime + decalInfo.fadeTime );
|
|
|
|
if ( decal.startTime <= minTime ) {
|
|
decal.numVerts = 0;
|
|
decal.numIndexes = 0;
|
|
if ( i == firstDecal ) {
|
|
firstDecal++;
|
|
}
|
|
}
|
|
}
|
|
if ( firstDecal == nextDecal ) {
|
|
firstDecal = 0;
|
|
nextDecal = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_CopyDecalSurface
|
|
=====================
|
|
*/
|
|
static void R_CopyDecalSurface( idDrawVert * verts, int numVerts, triIndex_t * indexes, int numIndexes,
|
|
const decal_t * decal, const float fadeColor[4] ) {
|
|
assert_16_byte_aligned( &verts[numVerts] );
|
|
assert_16_byte_aligned( &indexes[numIndexes] );
|
|
assert_16_byte_aligned( decal->indexes );
|
|
assert_16_byte_aligned( decal->verts );
|
|
assert( ( ( decal->numVerts * sizeof( idDrawVert ) ) & 15 ) == 0 );
|
|
assert( ( ( decal->numIndexes * sizeof( triIndex_t ) ) & 15 ) == 0 );
|
|
assert_16_byte_aligned( fadeColor );
|
|
|
|
|
|
const __m128i vector_int_num_verts = _mm_shuffle_epi32( _mm_cvtsi32_si128( numVerts ), 0 );
|
|
const __m128i vector_short_num_verts = _mm_packs_epi32( vector_int_num_verts, vector_int_num_verts );
|
|
const __m128 vector_fade_color = _mm_load_ps( fadeColor );
|
|
const __m128i vector_color_mask = _mm_set_epi32( 0, -1, 0, 0 );
|
|
|
|
// copy vertices and apply depth/time based fading
|
|
assert_offsetof( idDrawVert, color, 6 * 4 );
|
|
for ( int i = 0; i < decal->numVerts; i++ ) {
|
|
const idDrawVert &srcVert = decal->verts[i];
|
|
idDrawVert &dstVert = verts[numVerts + i];
|
|
|
|
__m128i v0 = _mm_load_si128( (const __m128i *)( (byte *)&srcVert + 0 ) );
|
|
__m128i v1 = _mm_load_si128( (const __m128i *)( (byte *)&srcVert + 16 ) );
|
|
__m128 depthFade = _mm_splat_ps( _mm_load_ss( decal->vertDepthFade + i ), 0 );
|
|
|
|
__m128 timeDepthFade = _mm_mul_ps( depthFade, vector_fade_color );
|
|
__m128i colorInt = _mm_cvtps_epi32( timeDepthFade );
|
|
__m128i colorShort = _mm_packs_epi32( colorInt, colorInt );
|
|
__m128i colorByte = _mm_packus_epi16( colorShort, colorShort );
|
|
v1 = _mm_or_si128( v1, _mm_and_si128( colorByte, vector_color_mask ) );
|
|
|
|
_mm_stream_si128( (__m128i *)( (byte *)&dstVert + 0 ), v0 );
|
|
_mm_stream_si128( (__m128i *)( (byte *)&dstVert + 16 ), v1 );
|
|
}
|
|
|
|
// copy indexes
|
|
assert( ( decal->numIndexes & 7 ) == 0 );
|
|
assert( sizeof( triIndex_t ) == 2 );
|
|
for ( int i = 0; i < decal->numIndexes; i += 8 ) {
|
|
__m128i vi = _mm_load_si128( (const __m128i *)&decal->indexes[i] );
|
|
|
|
vi = _mm_add_epi16( vi, vector_short_num_verts );
|
|
|
|
_mm_stream_si128( (__m128i *)&indexes[numIndexes + i], vi );
|
|
}
|
|
|
|
_mm_sfence();
|
|
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderModelDecal::GetNumDecalDrawSurfs
|
|
=====================
|
|
*/
|
|
unsigned int idRenderModelDecal::GetNumDecalDrawSurfs() {
|
|
numDecalMaterials = 0;
|
|
|
|
for ( unsigned int i = firstDecal; i < nextDecal; i++ ) {
|
|
const decal_t & decal = decals[i & ( MAX_DECALS - 1 )];
|
|
|
|
unsigned int j = 0;
|
|
for ( ; j < numDecalMaterials; j++ ) {
|
|
if ( decalMaterials[j] == decal.material ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( j >= numDecalMaterials ) {
|
|
decalMaterials[numDecalMaterials++] = decal.material;
|
|
}
|
|
}
|
|
|
|
return numDecalMaterials;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderModelDecal::CreateDecalDrawSurf
|
|
=====================
|
|
*/
|
|
drawSurf_t * idRenderModelDecal::CreateDecalDrawSurf( const viewEntity_t *space, unsigned int index ) {
|
|
if ( index < 0 || index >= numDecalMaterials ) {
|
|
return NULL;
|
|
}
|
|
|
|
const idMaterial * material = decalMaterials[index];
|
|
|
|
int maxVerts = 0;
|
|
int maxIndexes = 0;
|
|
for ( unsigned int i = firstDecal; i < nextDecal; i++ ) {
|
|
const decal_t & decal = decals[i & ( MAX_DECALS - 1 )];
|
|
if ( decal.material == material ) {
|
|
maxVerts += decal.numVerts;
|
|
maxIndexes += decal.numIndexes;
|
|
}
|
|
}
|
|
|
|
if ( maxVerts == 0 || maxIndexes == 0 ) {
|
|
return NULL;
|
|
}
|
|
|
|
// create a new triangle surface in frame memory so it gets automatically disposed of
|
|
srfTriangles_t *newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
|
|
newTri->numVerts = maxVerts;
|
|
newTri->numIndexes = maxIndexes;
|
|
|
|
newTri->ambientCache = vertexCache.AllocVertex( NULL, ALIGN( maxVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
|
|
newTri->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
|
|
|
|
idDrawVert * mappedVerts = (idDrawVert *)vertexCache.MappedVertexBuffer( newTri->ambientCache );
|
|
triIndex_t * mappedIndexes = (triIndex_t *)vertexCache.MappedIndexBuffer( newTri->indexCache );
|
|
|
|
const decalInfo_t decalInfo = material->GetDecalInfo();
|
|
const int maxTime = decalInfo.stayTime + decalInfo.fadeTime;
|
|
const int time = tr.viewDef->renderView.time[0];
|
|
|
|
int numVerts = 0;
|
|
int numIndexes = 0;
|
|
for ( unsigned int i = firstDecal; i < nextDecal; i++ ) {
|
|
const decal_t & decal = decals[i & ( MAX_DECALS - 1 )];
|
|
|
|
if ( decal.numVerts == 0 ) {
|
|
if ( i == firstDecal ) {
|
|
firstDecal++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( decal.material != material ) {
|
|
continue;
|
|
}
|
|
|
|
const int deltaTime = time - decal.startTime;
|
|
const int fadeTime = deltaTime - decalInfo.stayTime;
|
|
if ( deltaTime > maxTime ) {
|
|
continue; // already completely faded away, but not yet removed
|
|
}
|
|
|
|
const float f = ( deltaTime > decalInfo.stayTime ) ? ( (float) fadeTime / decalInfo.fadeTime ) : 0.0f;
|
|
|
|
ALIGNTYPE16 float fadeColor[4];
|
|
for ( int j = 0; j < 4; j++ ) {
|
|
fadeColor[j] = 255.0f * ( decalInfo.start[j] + ( decalInfo.end[j] - decalInfo.start[j] ) * f );
|
|
}
|
|
|
|
// use SIMD optimized routine to copy the vertices and indices directly to write-combined memory
|
|
// this also applies any depth/time based fading while copying
|
|
R_CopyDecalSurface( mappedVerts, numVerts, mappedIndexes, numIndexes, &decal, fadeColor );
|
|
|
|
numVerts += decal.numVerts;
|
|
numIndexes += decal.numIndexes;
|
|
}
|
|
newTri->numVerts = numVerts;
|
|
newTri->numIndexes = numIndexes;
|
|
|
|
// create the drawsurf
|
|
drawSurf_t * drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE );
|
|
drawSurf->frontEndGeo = newTri;
|
|
drawSurf->numIndexes = newTri->numIndexes;
|
|
drawSurf->ambientCache = newTri->ambientCache;
|
|
drawSurf->indexCache = newTri->indexCache;
|
|
drawSurf->shadowCache = 0;
|
|
drawSurf->jointCache = 0;
|
|
drawSurf->space = space;
|
|
drawSurf->scissorRect = space->scissorRect;
|
|
drawSurf->extraGLState = 0;
|
|
drawSurf->renderZFail = 0;
|
|
|
|
R_SetupDrawSurfShader( drawSurf, material, &space->entityDef->parms );
|
|
|
|
return drawSurf;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
idRenderModelDecal::ReadFromDemoFile
|
|
====================
|
|
*/
|
|
void idRenderModelDecal::ReadFromDemoFile( idDemoFile *f ) {
|
|
// FIXME: implement
|
|
}
|
|
|
|
/*
|
|
====================
|
|
idRenderModelDecal::WriteToDemoFile
|
|
====================
|
|
*/
|
|
void idRenderModelDecal::WriteToDemoFile( idDemoFile *f ) const {
|
|
// FIXME: implement
|
|
}
|