mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-23 21:02:11 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
1270 lines
35 KiB
C++
1270 lines
35 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 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 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.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "idlib/containers/BinSearch.h"
|
|
#include "renderer/VertexCache.h"
|
|
|
|
#include "renderer/tr_local.h"
|
|
|
|
|
|
/*
|
|
=================
|
|
R_FinishDeform
|
|
|
|
The ambientCache is on the stack, so we don't want to leave a reference
|
|
to it that would try to be freed later. Create the ambientCache immediately.
|
|
=================
|
|
*/
|
|
static void R_FinishDeform( drawSurf_t *drawSurf, srfTriangles_t *newTri, idDrawVert *ac ) {
|
|
if ( !newTri ) {
|
|
return;
|
|
}
|
|
|
|
// generate current normals, tangents, and bitangents
|
|
// We might want to support the possibility of deform functions generating
|
|
// explicit normals, and we might also want to allow the cached deformInfo
|
|
// optimization for these.
|
|
// FIXME: this doesn't work, because the deformed surface is just the
|
|
// ambient one, and there isn't an opportunity to generate light interactions
|
|
if ( drawSurf->material->ReceivesLighting() ) {
|
|
newTri->verts = ac;
|
|
R_DeriveTangents( newTri, false );
|
|
newTri->verts = NULL;
|
|
}
|
|
|
|
newTri->ambientCache = vertexCache.AllocFrameTemp( ac, newTri->numVerts * sizeof( idDrawVert ) );
|
|
// if we are out of vertex cache, leave it the way it is
|
|
if ( newTri->ambientCache ) {
|
|
drawSurf->geo = newTri;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_AutospriteDeform
|
|
|
|
Assuming all the triangles for this shader are independant
|
|
quads, rebuild them as forward facing sprites
|
|
=====================
|
|
*/
|
|
static void R_AutospriteDeform( drawSurf_t *surf ) {
|
|
int i;
|
|
const idDrawVert *v;
|
|
idVec3 mid, delta;
|
|
float radius;
|
|
idVec3 left, up;
|
|
idVec3 leftDir, upDir;
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
|
|
tri = surf->geo;
|
|
|
|
if ( tri->numVerts & 3 ) {
|
|
common->Warning( "R_AutospriteDeform: shader had odd vertex count" );
|
|
return;
|
|
}
|
|
if ( tri->numIndexes != ( tri->numVerts >> 2 ) * 6 ) {
|
|
common->Warning( "R_AutospriteDeform: autosprite had odd index count" );
|
|
return;
|
|
}
|
|
|
|
R_GlobalVectorToLocal( surf->space->modelMatrix, tr.viewDef->renderView.viewaxis[1], leftDir );
|
|
R_GlobalVectorToLocal( surf->space->modelMatrix, tr.viewDef->renderView.viewaxis[2], upDir );
|
|
|
|
if ( tr.viewDef->isMirror ) {
|
|
leftDir = vec3_origin - leftDir;
|
|
}
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
newTri->indexes = (glIndex_t *)R_FrameAlloc( newTri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
for ( i = 0 ; i < tri->numVerts ; i+=4 ) {
|
|
// find the midpoint
|
|
v = &tri->verts[i];
|
|
|
|
mid[0] = 0.25 * (v->xyz[0] + (v+1)->xyz[0] + (v+2)->xyz[0] + (v+3)->xyz[0]);
|
|
mid[1] = 0.25 * (v->xyz[1] + (v+1)->xyz[1] + (v+2)->xyz[1] + (v+3)->xyz[1]);
|
|
mid[2] = 0.25 * (v->xyz[2] + (v+1)->xyz[2] + (v+2)->xyz[2] + (v+3)->xyz[2]);
|
|
|
|
delta = v->xyz - mid;
|
|
radius = delta.Length() * 0.707; // / sqrt(2)
|
|
|
|
left = leftDir * radius;
|
|
up = upDir * radius;
|
|
|
|
ac[i+0].xyz = mid + left + up;
|
|
ac[i+0].st[0] = 0;
|
|
ac[i+0].st[1] = 0;
|
|
ac[i+1].xyz = mid - left + up;
|
|
ac[i+1].st[0] = 1;
|
|
ac[i+1].st[1] = 0;
|
|
ac[i+2].xyz = mid - left - up;
|
|
ac[i+2].st[0] = 1;
|
|
ac[i+2].st[1] = 1;
|
|
ac[i+3].xyz = mid + left - up;
|
|
ac[i+3].st[0] = 0;
|
|
ac[i+3].st[1] = 1;
|
|
|
|
newTri->indexes[6*(i>>2)+0] = i;
|
|
newTri->indexes[6*(i>>2)+1] = i+1;
|
|
newTri->indexes[6*(i>>2)+2] = i+2;
|
|
|
|
newTri->indexes[6*(i>>2)+3] = i;
|
|
newTri->indexes[6*(i>>2)+4] = i+2;
|
|
newTri->indexes[6*(i>>2)+5] = i+3;
|
|
}
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_TubeDeform
|
|
|
|
will pivot a rectangular quad along the center of its long axis
|
|
|
|
Note that a geometric tube with even quite a few sides tube will almost certainly render much faster
|
|
than this, so this should only be for faked volumetric tubes.
|
|
Make sure this is used with twosided translucent shaders, because the exact side
|
|
order may not be correct.
|
|
=====================
|
|
*/
|
|
static void R_TubeDeform( drawSurf_t *surf ) {
|
|
int i, j;
|
|
int indexes;
|
|
const srfTriangles_t *tri;
|
|
static int edgeVerts[6][2] = {
|
|
{ 0, 1 },
|
|
{ 1, 2 },
|
|
{ 2, 0 },
|
|
{ 3, 4 },
|
|
{ 4, 5 },
|
|
{ 5, 3 }
|
|
};
|
|
|
|
tri = surf->geo;
|
|
|
|
if ( tri->numVerts & 3 ) {
|
|
common->Error( "R_AutospriteDeform: shader had odd vertex count" );
|
|
}
|
|
if ( tri->numIndexes != ( tri->numVerts >> 2 ) * 6 ) {
|
|
common->Error( "R_AutospriteDeform: autosprite had odd index count" );
|
|
}
|
|
|
|
// we need the view direction to project the minor axis of the tube
|
|
// as the view changes
|
|
idVec3 localView;
|
|
R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView );
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
srfTriangles_t *newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
newTri->indexes = (glIndex_t *)R_FrameAlloc( newTri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
memcpy( newTri->indexes, tri->indexes, newTri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
memset( ac, 0, sizeof( idDrawVert ) * newTri->numVerts );
|
|
|
|
// this is a lot of work for two triangles...
|
|
// we could precalculate a lot if it is an issue, but it would mess up
|
|
// the shader abstraction
|
|
for ( i = 0, indexes = 0 ; i < tri->numVerts ; i+=4, indexes+=6 ) {
|
|
float lengths[2];
|
|
int nums[2];
|
|
idVec3 mid[2];
|
|
idVec3 major, minor;
|
|
const idDrawVert *v1, *v2;
|
|
|
|
// identify the two shortest edges out of the six defined by the indexes
|
|
nums[0] = nums[1] = 0;
|
|
lengths[0] = lengths[1] = 999999;
|
|
|
|
for ( j = 0 ; j < 6 ; j++ ) {
|
|
float l;
|
|
|
|
v1 = &tri->verts[tri->indexes[i+edgeVerts[j][0]]];
|
|
v2 = &tri->verts[tri->indexes[i+edgeVerts[j][1]]];
|
|
|
|
l = ( v1->xyz - v2->xyz ).Length();
|
|
if ( l < lengths[0] ) {
|
|
nums[1] = nums[0];
|
|
lengths[1] = lengths[0];
|
|
nums[0] = j;
|
|
lengths[0] = l;
|
|
} else if ( l < lengths[1] ) {
|
|
nums[1] = j;
|
|
lengths[1] = l;
|
|
}
|
|
}
|
|
|
|
// find the midpoints of the two short edges, which
|
|
// will give us the major axis in object coordinates
|
|
for ( j = 0 ; j < 2 ; j++ ) {
|
|
v1 = &tri->verts[tri->indexes[i+edgeVerts[nums[j]][0]]];
|
|
v2 = &tri->verts[tri->indexes[i+edgeVerts[nums[j]][1]]];
|
|
|
|
mid[j][0] = 0.5 * (v1->xyz[0] + v2->xyz[0]);
|
|
mid[j][1] = 0.5 * (v1->xyz[1] + v2->xyz[1]);
|
|
mid[j][2] = 0.5 * (v1->xyz[2] + v2->xyz[2]);
|
|
}
|
|
|
|
// find the vector of the major axis
|
|
major = mid[1] - mid[0];
|
|
|
|
// re-project the points
|
|
for ( j = 0 ; j < 2 ; j++ ) {
|
|
float l;
|
|
int i1 = tri->indexes[i+edgeVerts[nums[j]][0]];
|
|
int i2 = tri->indexes[i+edgeVerts[nums[j]][1]];
|
|
|
|
idDrawVert *av1 = &ac[i1];
|
|
idDrawVert *av2 = &ac[i2];
|
|
|
|
*av1 = *(idDrawVert *)&tri->verts[i1];
|
|
*av2 = *(idDrawVert *)&tri->verts[i2];
|
|
|
|
l = 0.5 * lengths[j];
|
|
|
|
// cross this with the view direction to get minor axis
|
|
idVec3 dir = mid[j] - localView;
|
|
minor.Cross( major, dir );
|
|
minor.Normalize();
|
|
|
|
if ( j ) {
|
|
av1->xyz = mid[j] - l * minor;
|
|
av2->xyz = mid[j] + l * minor;
|
|
} else {
|
|
av1->xyz = mid[j] + l * minor;
|
|
av2->xyz = mid[j] - l * minor;
|
|
}
|
|
}
|
|
}
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_WindingFromTriangles
|
|
|
|
=====================
|
|
*/
|
|
#define MAX_TRI_WINDING_INDEXES 16
|
|
int R_WindingFromTriangles( const srfTriangles_t *tri, glIndex_t indexes[MAX_TRI_WINDING_INDEXES] ) {
|
|
int i, j, k, l;
|
|
|
|
indexes[0] = tri->indexes[0];
|
|
int numIndexes = 1;
|
|
int numTris = tri->numIndexes / 3;
|
|
|
|
do {
|
|
// find an edge that goes from the current index to another
|
|
// index that isn't already used, and isn't an internal edge
|
|
for ( i = 0 ; i < numTris ; i++ ) {
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
if ( tri->indexes[i*3+j] != indexes[numIndexes-1] ) {
|
|
continue;
|
|
}
|
|
int next = tri->indexes[i*3+(j+1)%3];
|
|
|
|
// make sure it isn't already used
|
|
if ( numIndexes == 1 ) {
|
|
if ( next == indexes[0] ) {
|
|
continue;
|
|
}
|
|
} else {
|
|
for ( k = 1 ; k < numIndexes ; k++ ) {
|
|
if ( indexes[k] == next ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( k != numIndexes ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// make sure it isn't an interior edge
|
|
for ( k = 0 ; k < numTris ; k++ ) {
|
|
if ( k == i ) {
|
|
continue;
|
|
}
|
|
for ( l = 0 ; l < 3 ; l++ ) {
|
|
int a, b;
|
|
|
|
a = tri->indexes[k*3+l];
|
|
if ( a != next ) {
|
|
continue;
|
|
}
|
|
b = tri->indexes[k*3+(l+1)%3];
|
|
if ( b != indexes[numIndexes-1] ) {
|
|
continue;
|
|
}
|
|
|
|
// this is an interior edge
|
|
break;
|
|
}
|
|
if ( l != 3 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( k != numTris ) {
|
|
continue;
|
|
}
|
|
|
|
// add this to the list
|
|
indexes[numIndexes] = next;
|
|
numIndexes++;
|
|
break;
|
|
}
|
|
if ( j != 3 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( numIndexes == tri->numVerts ) {
|
|
break;
|
|
}
|
|
} while ( i != numTris );
|
|
|
|
return numIndexes;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_FlareDeform
|
|
|
|
=====================
|
|
*/
|
|
/*
|
|
static void R_FlareDeform( drawSurf_t *surf ) {
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
idPlane plane;
|
|
float dot;
|
|
idVec3 localViewer;
|
|
int j;
|
|
|
|
tri = surf->geo;
|
|
|
|
if ( tri->numVerts != 4 || tri->numIndexes != 6 ) {
|
|
//FIXME: temp hack for flares on tripleted models
|
|
common->Warning( "R_FlareDeform: not a single quad" );
|
|
return;
|
|
}
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = 4;
|
|
newTri->numIndexes = 2*3;
|
|
newTri->indexes = (glIndex_t *)R_FrameAlloc( newTri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
// find the plane
|
|
plane.FromPoints( tri->verts[tri->indexes[0]].xyz, tri->verts[tri->indexes[1]].xyz, tri->verts[tri->indexes[2]].xyz );
|
|
|
|
// if viewer is behind the plane, draw nothing
|
|
R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewer );
|
|
float distFromPlane = localViewer * plane.Normal() + plane[3];
|
|
if ( distFromPlane <= 0 ) {
|
|
newTri->numIndexes = 0;
|
|
surf->geo = newTri;
|
|
return;
|
|
}
|
|
|
|
idVec3 center;
|
|
center = tri->verts[0].xyz;
|
|
for ( j = 1 ; j < tri->numVerts ; j++ ) {
|
|
center += tri->verts[j].xyz;
|
|
}
|
|
center *= 1.0/tri->numVerts;
|
|
|
|
idVec3 dir = localViewer - center;
|
|
dir.Normalize();
|
|
|
|
dot = dir * plane.Normal();
|
|
|
|
// set vertex colors based on plane angle
|
|
int color = (int)(dot * 8 * 256);
|
|
if ( color > 255 ) {
|
|
color = 255;
|
|
}
|
|
for ( j = 0 ; j < newTri->numVerts ; j++ ) {
|
|
ac[j].color[0] =
|
|
ac[j].color[1] =
|
|
ac[j].color[2] = color;
|
|
ac[j].color[3] = 255;
|
|
}
|
|
|
|
float spread = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ] * r_flareSize.GetFloat();
|
|
idVec3 edgeDir[4][3];
|
|
glIndex_t indexes[MAX_TRI_WINDING_INDEXES];
|
|
int numIndexes = R_WindingFromTriangles( tri, indexes );
|
|
|
|
surf->material = declManager->FindMaterial( "textures/smf/anamorphicFlare" );
|
|
|
|
// only deal with quads
|
|
if ( numIndexes != 4 ) {
|
|
return;
|
|
}
|
|
|
|
// compute centroid
|
|
idVec3 centroid, toeye, forward, up, left;
|
|
centroid.Set( 0, 0, 0 );
|
|
for ( int i = 0; i < 4; i++ ) {
|
|
centroid += tri->verts[ indexes[i] ].xyz;
|
|
}
|
|
centroid /= 4;
|
|
|
|
// compute basis vectors
|
|
up.Set( 0, 0, 1 );
|
|
|
|
toeye = centroid - localViewer;
|
|
toeye.Normalize();
|
|
left = toeye.Cross( up );
|
|
up = left.Cross( toeye );
|
|
|
|
left = left * 40 * 6;
|
|
up = up * 40;
|
|
|
|
// compute flares
|
|
struct flare_t {
|
|
float angle;
|
|
float length;
|
|
};
|
|
|
|
static flare_t flares[] = {
|
|
{ 0, 100 },
|
|
{ 90, 100 }
|
|
};
|
|
|
|
for ( int i = 0; i < 4; i++ ) {
|
|
memset( ac + i, 0, sizeof( ac[i] ) );
|
|
}
|
|
|
|
ac[0].xyz = centroid - left;
|
|
ac[0].st[0] = 0; ac[0].st[1] = 0;
|
|
|
|
ac[1].xyz = centroid + up;
|
|
ac[1].st[0] = 1; ac[1].st[1] = 0;
|
|
|
|
ac[2].xyz = centroid + left;
|
|
ac[2].st[0] = 1; ac[2].st[1] = 1;
|
|
|
|
ac[3].xyz = centroid - up;
|
|
ac[3].st[0] = 0; ac[3].st[1] = 1;
|
|
|
|
// setup colors
|
|
for ( j = 0 ; j < newTri->numVerts ; j++ ) {
|
|
ac[j].color[0] =
|
|
ac[j].color[1] =
|
|
ac[j].color[2] = 255;
|
|
ac[j].color[3] = 255;
|
|
}
|
|
|
|
// setup indexes
|
|
static glIndex_t triIndexes[2*3] = {
|
|
0,1,2, 0,2,3
|
|
};
|
|
|
|
memcpy( newTri->indexes, triIndexes, sizeof( triIndexes ) );
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
*/
|
|
|
|
static void R_FlareDeform( drawSurf_t *surf ) {
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
idPlane plane;
|
|
float dot;
|
|
idVec3 localViewer;
|
|
int j;
|
|
|
|
tri = surf->geo;
|
|
|
|
if ( tri->numVerts != 4 || tri->numIndexes != 6 ) {
|
|
//FIXME: temp hack for flares on tripleted models
|
|
common->Warning( "R_FlareDeform: not a single quad" );
|
|
return;
|
|
}
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = 16;
|
|
newTri->numIndexes = 18*3;
|
|
newTri->indexes = (glIndex_t *)R_FrameAlloc( newTri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
// find the plane
|
|
if (!plane.FromPoints( tri->verts[tri->indexes[0]].xyz, tri->verts[tri->indexes[1]].xyz, tri->verts[tri->indexes[2]].xyz )) {
|
|
common->Warning( "R_FlareDeform: plane.FromPoints failed" );
|
|
return;
|
|
}
|
|
|
|
// if viewer is behind the plane, draw nothing
|
|
R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewer );
|
|
float distFromPlane = localViewer * plane.Normal() + plane[3];
|
|
if ( distFromPlane <= 0 ) {
|
|
newTri->numIndexes = 0;
|
|
surf->geo = newTri;
|
|
return;
|
|
}
|
|
|
|
idVec3 center;
|
|
center = tri->verts[0].xyz;
|
|
for ( j = 1 ; j < tri->numVerts ; j++ ) {
|
|
center += tri->verts[j].xyz;
|
|
}
|
|
center *= 1.0/tri->numVerts;
|
|
|
|
idVec3 dir = localViewer - center;
|
|
dir.Normalize();
|
|
|
|
dot = dir * plane.Normal();
|
|
|
|
// set vertex colors based on plane angle
|
|
int color = (int)(dot * 8 * 256);
|
|
if ( color > 255 ) {
|
|
color = 255;
|
|
}
|
|
for ( j = 0 ; j < newTri->numVerts ; j++ ) {
|
|
ac[j].color[0] =
|
|
ac[j].color[1] =
|
|
ac[j].color[2] = color;
|
|
ac[j].color[3] = 255;
|
|
}
|
|
|
|
float spread = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ] * r_flareSize.GetFloat();
|
|
idVec3 edgeDir[4][3];
|
|
glIndex_t indexes[MAX_TRI_WINDING_INDEXES];
|
|
int numIndexes = R_WindingFromTriangles( tri, indexes );
|
|
|
|
|
|
// only deal with quads
|
|
if ( numIndexes != 4 ) {
|
|
return;
|
|
}
|
|
int i;
|
|
// calculate vector directions
|
|
for ( i = 0 ; i < 4 ; i++ ) {
|
|
ac[i].xyz = tri->verts[ indexes[i] ].xyz;
|
|
ac[i].st[0] =
|
|
ac[i].st[1] = 0.5;
|
|
|
|
idVec3 toEye = tri->verts[ indexes[i] ].xyz - localViewer;
|
|
toEye.Normalize();
|
|
|
|
idVec3 d1 = tri->verts[ indexes[(i+1)%4] ].xyz - localViewer;
|
|
d1.Normalize();
|
|
edgeDir[i][1].Cross( toEye, d1 );
|
|
edgeDir[i][1].Normalize();
|
|
edgeDir[i][1] = vec3_origin - edgeDir[i][1];
|
|
|
|
idVec3 d2 = tri->verts[ indexes[(i+3)%4] ].xyz - localViewer;
|
|
d2.Normalize();
|
|
edgeDir[i][0].Cross( toEye, d2 );
|
|
edgeDir[i][0].Normalize();
|
|
|
|
edgeDir[i][2] = edgeDir[i][0] + edgeDir[i][1];
|
|
edgeDir[i][2].Normalize();
|
|
}
|
|
|
|
// build all the points
|
|
ac[4].xyz = tri->verts[ indexes[0] ].xyz + spread * edgeDir[0][0];
|
|
ac[4].st[0] = 0;
|
|
ac[4].st[1] = 0.5;
|
|
|
|
ac[5].xyz = tri->verts[ indexes[0] ].xyz + spread * edgeDir[0][2];
|
|
ac[5].st[0] = 0;
|
|
ac[5].st[1] = 0;
|
|
|
|
ac[6].xyz = tri->verts[ indexes[0] ].xyz + spread * edgeDir[0][1];
|
|
ac[6].st[0] = 0.5;
|
|
ac[6].st[1] = 0;
|
|
|
|
|
|
ac[7].xyz = tri->verts[ indexes[1] ].xyz + spread * edgeDir[1][0];
|
|
ac[7].st[0] = 0.5;
|
|
ac[7].st[1] = 0;
|
|
|
|
ac[8].xyz = tri->verts[ indexes[1] ].xyz + spread * edgeDir[1][2];
|
|
ac[8].st[0] = 1;
|
|
ac[8].st[1] = 0;
|
|
|
|
ac[9].xyz = tri->verts[ indexes[1] ].xyz + spread * edgeDir[1][1];
|
|
ac[9].st[0] = 1;
|
|
ac[9].st[1] = 0.5;
|
|
|
|
|
|
ac[10].xyz = tri->verts[ indexes[2] ].xyz + spread * edgeDir[2][0];
|
|
ac[10].st[0] = 1;
|
|
ac[10].st[1] = 0.5;
|
|
|
|
ac[11].xyz = tri->verts[ indexes[2] ].xyz + spread * edgeDir[2][2];
|
|
ac[11].st[0] = 1;
|
|
ac[11].st[1] = 1;
|
|
|
|
ac[12].xyz = tri->verts[ indexes[2] ].xyz + spread * edgeDir[2][1];
|
|
ac[12].st[0] = 0.5;
|
|
ac[12].st[1] = 1;
|
|
|
|
|
|
ac[13].xyz = tri->verts[ indexes[3] ].xyz + spread * edgeDir[3][0];
|
|
ac[13].st[0] = 0.5;
|
|
ac[13].st[1] = 1;
|
|
|
|
ac[14].xyz = tri->verts[ indexes[3] ].xyz + spread * edgeDir[3][2];
|
|
ac[14].st[0] = 0;
|
|
ac[14].st[1] = 1;
|
|
|
|
ac[15].xyz = tri->verts[ indexes[3] ].xyz + spread * edgeDir[3][1];
|
|
ac[15].st[0] = 0;
|
|
ac[15].st[1] = 0.5;
|
|
|
|
for ( i = 4 ; i < 16 ; i++ ) {
|
|
idVec3 dir = ac[i].xyz - localViewer;
|
|
float len = dir.Normalize();
|
|
|
|
float ang = dir * plane.Normal();
|
|
|
|
// ac[i].xyz -= dir * spread * 2;
|
|
float newLen = -( distFromPlane / ang );
|
|
|
|
if ( newLen > 0 && newLen < len ) {
|
|
ac[i].xyz = localViewer + dir * newLen;
|
|
}
|
|
|
|
ac[i].st[0] = 0;
|
|
ac[i].st[1] = 0.5;
|
|
}
|
|
|
|
#if 1
|
|
static glIndex_t triIndexes[18*3] = {
|
|
0,4,5, 0,5,6, 0,6,7, 0,7,1, 1,7,8, 1,8,9,
|
|
15,4,0, 15,0,3, 3,0,1, 3,1,2, 2,1,9, 2,9,10,
|
|
14,15,3, 14,3,13, 13,3,2, 13,2,12, 12,2,11, 11,2,10
|
|
};
|
|
#else
|
|
newTri->numIndexes = 12;
|
|
static glIndex_t triIndexes[4*3] = {
|
|
0,1,2, 0,2,3, 0,4,5,0,5,6
|
|
};
|
|
#endif
|
|
|
|
memcpy( newTri->indexes, triIndexes, sizeof( triIndexes ) );
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=====================
|
|
R_ExpandDeform
|
|
|
|
Expands the surface along it's normals by a shader amount
|
|
=====================
|
|
*/
|
|
static void R_ExpandDeform( drawSurf_t *surf ) {
|
|
int i;
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
|
|
tri = surf->geo;
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
newTri->indexes = tri->indexes;
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
float dist = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ];
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
ac[i] = *(idDrawVert *)&tri->verts[i];
|
|
ac[i].xyz = tri->verts[i].xyz + tri->verts[i].normal * dist;
|
|
}
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_MoveDeform
|
|
|
|
Moves the surface along the X axis, mostly just for demoing the deforms
|
|
=====================
|
|
*/
|
|
static void R_MoveDeform( drawSurf_t *surf ) {
|
|
int i;
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
|
|
tri = surf->geo;
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
newTri->indexes = tri->indexes;
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
float dist = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ];
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
ac[i] = *(idDrawVert *)&tri->verts[i];
|
|
ac[i].xyz[0] += dist;
|
|
}
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
//=====================================================================================
|
|
|
|
/*
|
|
=====================
|
|
R_TurbulentDeform
|
|
|
|
Turbulently deforms the XYZ, S, and T values
|
|
=====================
|
|
*/
|
|
static void R_TurbulentDeform( drawSurf_t *surf ) {
|
|
int i;
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
|
|
tri = surf->geo;
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
newTri->indexes = tri->indexes;
|
|
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( newTri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
idDeclTable *table = (idDeclTable *)surf->material->GetDeformDecl();
|
|
float range = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ];
|
|
float timeOfs = surf->shaderRegisters[ surf->material->GetDeformRegister(1) ];
|
|
float domain = surf->shaderRegisters[ surf->material->GetDeformRegister(2) ];
|
|
float tOfs = 0.5;
|
|
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
float f = tri->verts[i].xyz[0] * 0.003 + tri->verts[i].xyz[1] * 0.007 + tri->verts[i].xyz[2] * 0.011;
|
|
|
|
f = timeOfs + domain * f;
|
|
f += timeOfs;
|
|
|
|
ac[i] = *(idDrawVert *)&tri->verts[i];
|
|
|
|
ac[i].st[0] += range * table->TableLookup( f );
|
|
ac[i].st[1] += range * table->TableLookup( f + tOfs );
|
|
}
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
//=====================================================================================
|
|
|
|
/*
|
|
=====================
|
|
AddTriangleToIsland_r
|
|
|
|
=====================
|
|
*/
|
|
#define MAX_EYEBALL_TRIS 10
|
|
#define MAX_EYEBALL_ISLANDS 6
|
|
|
|
typedef struct {
|
|
int tris[MAX_EYEBALL_TRIS];
|
|
int numTris;
|
|
idBounds bounds;
|
|
idVec3 mid;
|
|
} eyeIsland_t;
|
|
|
|
static void AddTriangleToIsland_r( const srfTriangles_t *tri, int triangleNum, bool *usedList, eyeIsland_t *island ) {
|
|
int a, b, c;
|
|
|
|
usedList[triangleNum] = true;
|
|
|
|
// add to the current island
|
|
if ( island->numTris == MAX_EYEBALL_TRIS ) {
|
|
common->Error( "MAX_EYEBALL_TRIS" );
|
|
}
|
|
island->tris[island->numTris] = triangleNum;
|
|
island->numTris++;
|
|
|
|
// recurse into all neighbors
|
|
a = tri->indexes[triangleNum*3];
|
|
b = tri->indexes[triangleNum*3+1];
|
|
c = tri->indexes[triangleNum*3+2];
|
|
|
|
island->bounds.AddPoint( tri->verts[a].xyz );
|
|
island->bounds.AddPoint( tri->verts[b].xyz );
|
|
island->bounds.AddPoint( tri->verts[c].xyz );
|
|
|
|
int numTri = tri->numIndexes / 3;
|
|
for ( int i = 0 ; i < numTri ; i++ ) {
|
|
if ( usedList[i] ) {
|
|
continue;
|
|
}
|
|
if ( tri->indexes[i*3+0] == a
|
|
|| tri->indexes[i*3+1] == a
|
|
|| tri->indexes[i*3+2] == a
|
|
|| tri->indexes[i*3+0] == b
|
|
|| tri->indexes[i*3+1] == b
|
|
|| tri->indexes[i*3+2] == b
|
|
|| tri->indexes[i*3+0] == c
|
|
|| tri->indexes[i*3+1] == c
|
|
|| tri->indexes[i*3+2] == c ) {
|
|
AddTriangleToIsland_r( tri, i, usedList, island );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_EyeballDeform
|
|
|
|
Each eyeball surface should have an separate upright triangle behind it, long end
|
|
pointing out the eye, and another single triangle in front of the eye for the focus point.
|
|
=====================
|
|
*/
|
|
static void R_EyeballDeform( drawSurf_t *surf ) {
|
|
int i, j, k;
|
|
const srfTriangles_t *tri;
|
|
srfTriangles_t *newTri;
|
|
eyeIsland_t islands[MAX_EYEBALL_ISLANDS];
|
|
int numIslands;
|
|
bool triUsed[MAX_EYEBALL_ISLANDS*MAX_EYEBALL_TRIS];
|
|
|
|
tri = surf->geo;
|
|
|
|
// separate all the triangles into islands
|
|
int numTri = tri->numIndexes / 3;
|
|
if ( numTri > MAX_EYEBALL_ISLANDS*MAX_EYEBALL_TRIS ) {
|
|
common->Printf( "R_EyeballDeform: too many triangles in surface" );
|
|
return;
|
|
}
|
|
memset( triUsed, 0, sizeof( triUsed ) );
|
|
|
|
for ( numIslands = 0 ; numIslands < MAX_EYEBALL_ISLANDS ; numIslands++ ) {
|
|
islands[numIslands].numTris = 0;
|
|
islands[numIslands].bounds.Clear();
|
|
for ( i = 0 ; i < numTri ; i++ ) {
|
|
if ( !triUsed[i] ) {
|
|
AddTriangleToIsland_r( tri, i, triUsed, &islands[numIslands] );
|
|
break;
|
|
}
|
|
}
|
|
if ( i == numTri ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// assume we always have two eyes, two origins, and two targets
|
|
if ( numIslands != 3 ) {
|
|
common->Printf( "R_EyeballDeform: %i triangle islands\n", numIslands );
|
|
return;
|
|
}
|
|
|
|
// this srfTriangles_t and all its indexes and caches are in frame
|
|
// memory, and will be automatically disposed of
|
|
|
|
// the surface cannot have more indexes or verts than the original
|
|
newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ) );
|
|
memset( newTri, 0, sizeof( *newTri ) );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
newTri->indexes = (glIndex_t *)R_FrameAlloc( tri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
idDrawVert *ac = (idDrawVert *)_alloca16( tri->numVerts * sizeof( idDrawVert ) );
|
|
|
|
newTri->numIndexes = 0;
|
|
|
|
// decide which islands are the eyes and points
|
|
for ( i = 0 ; i < numIslands ; i++ ) {
|
|
islands[i].mid = islands[i].bounds.GetCenter();
|
|
}
|
|
|
|
for ( i = 0 ; i < numIslands ; i++ ) {
|
|
eyeIsland_t *island = &islands[i];
|
|
|
|
if ( island->numTris == 1 ) {
|
|
continue;
|
|
}
|
|
|
|
// the closest single triangle point will be the eye origin
|
|
// and the next-to-farthest will be the focal point
|
|
idVec3 origin, focus;
|
|
int originIsland = 0;
|
|
float dist[MAX_EYEBALL_ISLANDS];
|
|
int sortOrder[MAX_EYEBALL_ISLANDS];
|
|
|
|
for ( j = 0 ; j < numIslands ; j++ ) {
|
|
idVec3 dir = islands[j].mid - island->mid;
|
|
dist[j] = dir.Length();
|
|
sortOrder[j] = j;
|
|
for ( k = j-1 ; k >= 0 ; k-- ) {
|
|
if ( dist[k] > dist[k+1] ) {
|
|
int temp = sortOrder[k];
|
|
sortOrder[k] = sortOrder[k+1];
|
|
sortOrder[k+1] = temp;
|
|
float ftemp = dist[k];
|
|
dist[k] = dist[k+1];
|
|
dist[k+1] = ftemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
originIsland = sortOrder[1];
|
|
origin = islands[originIsland].mid;
|
|
|
|
focus = islands[sortOrder[2]].mid;
|
|
|
|
// determine the projection directions based on the origin island triangle
|
|
idVec3 dir = focus - origin;
|
|
dir.Normalize();
|
|
|
|
const idVec3 &p1 = tri->verts[tri->indexes[islands[originIsland].tris[0]+0]].xyz;
|
|
const idVec3 &p2 = tri->verts[tri->indexes[islands[originIsland].tris[0]+1]].xyz;
|
|
const idVec3 &p3 = tri->verts[tri->indexes[islands[originIsland].tris[0]+2]].xyz;
|
|
|
|
idVec3 v1 = p2 - p1;
|
|
v1.Normalize();
|
|
idVec3 v2 = p3 - p1;
|
|
v2.Normalize();
|
|
|
|
// texVec[0] will be the normal to the origin triangle
|
|
idVec3 texVec[2];
|
|
|
|
texVec[0].Cross( v1, v2 );
|
|
|
|
texVec[1].Cross( texVec[0], dir );
|
|
|
|
for ( j = 0 ; j < 2 ; j++ ) {
|
|
texVec[j] -= dir * ( texVec[j] * dir );
|
|
texVec[j].Normalize();
|
|
}
|
|
|
|
// emit these triangles, generating the projected texcoords
|
|
|
|
for ( j = 0 ; j < islands[i].numTris ; j++ ) {
|
|
for ( k = 0 ; k < 3 ; k++ ) {
|
|
int index = islands[i].tris[j] * 3;
|
|
|
|
index = tri->indexes[index+k];
|
|
newTri->indexes[newTri->numIndexes++] = index;
|
|
|
|
ac[index].xyz = tri->verts[index].xyz;
|
|
|
|
idVec3 local = tri->verts[index].xyz - origin;
|
|
|
|
ac[index].st[0] = 0.5 + local * texVec[0];
|
|
ac[index].st[1] = 0.5 + local * texVec[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
R_FinishDeform( surf, newTri, ac );
|
|
}
|
|
|
|
//==========================================================================================
|
|
|
|
|
|
/*
|
|
=====================
|
|
R_ParticleDeform
|
|
|
|
Emit particles from the surface instead of drawing it
|
|
=====================
|
|
*/
|
|
static void R_ParticleDeform( drawSurf_t *surf, bool useArea ) {
|
|
const struct renderEntity_s *renderEntity = &surf->space->entityDef->parms;
|
|
const struct viewDef_s *viewDef = tr.viewDef;
|
|
const idDeclParticle *particleSystem = (idDeclParticle *)surf->material->GetDeformDecl();
|
|
|
|
if ( r_skipParticles.GetBool() ) {
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] &&
|
|
viewDef->renderView.time*0.001 >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] ) {
|
|
// the entire system has faded out
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// calculate the area of all the triangles
|
|
//
|
|
int numSourceTris = surf->geo->numIndexes / 3;
|
|
float totalArea = 0;
|
|
float *sourceTriAreas = NULL;
|
|
const srfTriangles_t *srcTri = surf->geo;
|
|
|
|
if ( useArea ) {
|
|
sourceTriAreas = (float *)_alloca( sizeof( *sourceTriAreas ) * numSourceTris );
|
|
int triNum = 0;
|
|
for ( int i = 0 ; i < srcTri->numIndexes ; i += 3, triNum++ ) {
|
|
float area;
|
|
area = idWinding::TriangleArea( srcTri->verts[srcTri->indexes[i]].xyz, srcTri->verts[srcTri->indexes[i+1]].xyz, srcTri->verts[srcTri->indexes[i+2]].xyz );
|
|
sourceTriAreas[triNum] = totalArea;
|
|
totalArea += area;
|
|
}
|
|
}
|
|
|
|
//
|
|
// create the particles almost exactly the way idRenderModelPrt does
|
|
//
|
|
particleGen_t g;
|
|
|
|
g.renderEnt = renderEntity;
|
|
g.renderView = &viewDef->renderView;
|
|
g.origin.Zero();
|
|
g.axis = mat3_identity;
|
|
|
|
for ( int currentTri = 0; currentTri < ( ( useArea ) ? 1 : numSourceTris ); currentTri++ ) {
|
|
|
|
for ( int stageNum = 0 ; stageNum < particleSystem->stages.Num() ; stageNum++ ) {
|
|
idParticleStage *stage = particleSystem->stages[stageNum];
|
|
|
|
if ( !stage->material ) {
|
|
continue;
|
|
}
|
|
if ( !stage->cycleMsec ) {
|
|
continue;
|
|
}
|
|
if ( stage->hidden ) { // just for gui particle editor use
|
|
continue;
|
|
}
|
|
|
|
// we interpret stage->totalParticles as "particles per map square area"
|
|
// so the systems look the same on different size surfaces
|
|
int totalParticles = ( useArea ) ? stage->totalParticles * totalArea / 4096.0 : ( stage->totalParticles );
|
|
|
|
int count = totalParticles * stage->NumQuadsPerParticle();
|
|
|
|
// allocate a srfTriangles in temp memory that can hold all the particles
|
|
srfTriangles_t *tri;
|
|
|
|
tri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *tri ) );
|
|
tri->numVerts = 4 * count;
|
|
tri->numIndexes = 6 * count;
|
|
tri->verts = (idDrawVert *)R_FrameAlloc( tri->numVerts * sizeof( tri->verts[0] ) );
|
|
tri->indexes = (glIndex_t *)R_FrameAlloc( tri->numIndexes * sizeof( tri->indexes[0] ) );
|
|
|
|
// just always draw the particles
|
|
tri->bounds = stage->bounds;
|
|
|
|
tri->numVerts = 0;
|
|
|
|
idRandom steppingRandom, steppingRandom2;
|
|
|
|
int stageAge = g.renderView->time + renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000 - stage->timeOffset * 1000;
|
|
int stageCycle = stageAge / stage->cycleMsec;
|
|
|
|
// some particles will be in this cycle, some will be in the previous cycle
|
|
steppingRandom.SetSeed( (( stageCycle << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
|
|
steppingRandom2.SetSeed( (( (stageCycle-1) << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
|
|
|
|
for ( int index = 0 ; index < totalParticles ; index++ ) {
|
|
g.index = index;
|
|
|
|
// bump the random
|
|
steppingRandom.RandomInt();
|
|
steppingRandom2.RandomInt();
|
|
|
|
// calculate local age for this index
|
|
int bunchOffset = stage->particleLife * 1000 * stage->spawnBunching * index / totalParticles;
|
|
|
|
int particleAge = stageAge - bunchOffset;
|
|
int particleCycle = particleAge / stage->cycleMsec;
|
|
if ( particleCycle < 0 ) {
|
|
// before the particleSystem spawned
|
|
continue;
|
|
}
|
|
if ( stage->cycles && particleCycle >= stage->cycles ) {
|
|
// cycled systems will only run cycle times
|
|
continue;
|
|
}
|
|
|
|
if ( particleCycle == stageCycle ) {
|
|
g.random = steppingRandom;
|
|
} else {
|
|
g.random = steppingRandom2;
|
|
}
|
|
|
|
int inCycleTime = particleAge - particleCycle * stage->cycleMsec;
|
|
|
|
if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] &&
|
|
g.renderView->time - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME]*1000 ) {
|
|
// don't fire any more particles
|
|
continue;
|
|
}
|
|
|
|
// supress particles before or after the age clamp
|
|
g.frac = (float)inCycleTime / ( stage->particleLife * 1000 );
|
|
if ( g.frac < 0 ) {
|
|
// yet to be spawned
|
|
continue;
|
|
}
|
|
if ( g.frac > 1.0 ) {
|
|
// this particle is in the deadTime band
|
|
continue;
|
|
}
|
|
|
|
//---------------
|
|
// locate the particle origin and axis somewhere on the surface
|
|
//---------------
|
|
|
|
int pointTri = currentTri;
|
|
|
|
if ( useArea ) {
|
|
// select a triangle based on an even area distribution
|
|
pointTri = idBinSearch_LessEqual<float>( sourceTriAreas, numSourceTris, g.random.RandomFloat() * totalArea );
|
|
}
|
|
|
|
// now pick a random point inside pointTri
|
|
const idDrawVert *v1 = &srcTri->verts[ srcTri->indexes[ pointTri * 3 + 0 ] ];
|
|
const idDrawVert *v2 = &srcTri->verts[ srcTri->indexes[ pointTri * 3 + 1 ] ];
|
|
const idDrawVert *v3 = &srcTri->verts[ srcTri->indexes[ pointTri * 3 + 2 ] ];
|
|
|
|
float f1 = g.random.RandomFloat();
|
|
float f2 = g.random.RandomFloat();
|
|
float f3 = g.random.RandomFloat();
|
|
|
|
float ft = 1.0f / ( f1 + f2 + f3 + 0.0001f );
|
|
|
|
f1 *= ft;
|
|
f2 *= ft;
|
|
f3 *= ft;
|
|
|
|
g.origin = v1->xyz * f1 + v2->xyz * f2 + v3->xyz * f3;
|
|
g.axis[0] = v1->tangents[0] * f1 + v2->tangents[0] * f2 + v3->tangents[0] * f3;
|
|
g.axis[1] = v1->tangents[1] * f1 + v2->tangents[1] * f2 + v3->tangents[1] * f3;
|
|
g.axis[2] = v1->normal * f1 + v2->normal * f2 + v3->normal * f3;
|
|
|
|
//-----------------------
|
|
|
|
// this is needed so aimed particles can calculate origins at different times
|
|
g.originalRandom = g.random;
|
|
|
|
g.age = g.frac * stage->particleLife;
|
|
|
|
// if the particle doesn't get drawn because it is faded out or beyond a kill region,
|
|
// don't increment the verts
|
|
tri->numVerts += stage->CreateParticle( &g, tri->verts + tri->numVerts );
|
|
}
|
|
|
|
if ( tri->numVerts > 0 ) {
|
|
// build the index list
|
|
int indexes = 0;
|
|
for ( int i = 0 ; i < tri->numVerts ; i += 4 ) {
|
|
tri->indexes[indexes+0] = i;
|
|
tri->indexes[indexes+1] = i+2;
|
|
tri->indexes[indexes+2] = i+3;
|
|
tri->indexes[indexes+3] = i;
|
|
tri->indexes[indexes+4] = i+3;
|
|
tri->indexes[indexes+5] = i+1;
|
|
indexes += 6;
|
|
}
|
|
tri->numIndexes = indexes;
|
|
tri->ambientCache = vertexCache.AllocFrameTemp( tri->verts, tri->numVerts * sizeof( idDrawVert ) );
|
|
if ( tri->ambientCache ) {
|
|
// add the drawsurf
|
|
R_AddDrawSurf( tri, surf->space, renderEntity, stage->material, surf->scissorRect );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//========================================================================================
|
|
|
|
/*
|
|
=================
|
|
R_DeformDrawSurf
|
|
=================
|
|
*/
|
|
void R_DeformDrawSurf( drawSurf_t *drawSurf ) {
|
|
if ( !drawSurf->material ) {
|
|
return;
|
|
}
|
|
|
|
if ( r_skipDeforms.GetBool() ) {
|
|
return;
|
|
}
|
|
switch ( drawSurf->material->Deform() ) {
|
|
case DFRM_NONE:
|
|
return;
|
|
case DFRM_SPRITE:
|
|
R_AutospriteDeform( drawSurf );
|
|
break;
|
|
case DFRM_TUBE:
|
|
R_TubeDeform( drawSurf );
|
|
break;
|
|
case DFRM_FLARE:
|
|
R_FlareDeform( drawSurf );
|
|
break;
|
|
case DFRM_EXPAND:
|
|
R_ExpandDeform( drawSurf );
|
|
break;
|
|
case DFRM_MOVE:
|
|
R_MoveDeform( drawSurf );
|
|
break;
|
|
case DFRM_TURB:
|
|
R_TurbulentDeform( drawSurf );
|
|
break;
|
|
case DFRM_EYEBALL:
|
|
R_EyeballDeform( drawSurf );
|
|
break;
|
|
case DFRM_PARTICLE:
|
|
R_ParticleDeform( drawSurf, true );
|
|
break;
|
|
case DFRM_PARTICLE2:
|
|
R_ParticleDeform( drawSurf, false );
|
|
break;
|
|
}
|
|
}
|