828 lines
23 KiB
C++
828 lines
23 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"
|
|
|
|
|
|
|
|
#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, bspDrawVert_t *a, bspDrawVert_t *b, 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 ( fabs( bb ) < 0.00000001 ) {
|
|
return false;
|
|
}
|
|
|
|
/* calculate texture origin */
|
|
#if 0
|
|
s = 0.0;
|
|
t = 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;
|
|
|
|
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.0 ? ( axis[ i ][ j ] / lengths[ i ] ) : 0.0;
|
|
//% dp->texMat[ i ][ j ] = fabs( vecs[ i ][ j ] ) > 0.0 ? (1.0 / vecs[ i ][ j ]) : 0.0;
|
|
//% dp->texMat[ i ][ j ] = axis[ i ][ j ] > 0.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.0;
|
|
texDelta = 0.0;
|
|
|
|
/* walk deltas */
|
|
for ( k = 0; k < 3; k++ )
|
|
{
|
|
if ( fabs( deltas[ k ][ j ] ) > delta &&
|
|
fabs( texDeltas[ k ][ i ] ) > texDelta ) {
|
|
delta = deltas[ k ][ j ];
|
|
texDelta = texDeltas[ k ][ i ];
|
|
}
|
|
}
|
|
|
|
/* set texture matrix component */
|
|
if ( fabs( delta ) > 0.0 ) {
|
|
dp->texMat[ i ][ j ] = texDelta / delta;
|
|
}
|
|
else{
|
|
dp->texMat[ i ][ j ] = 0.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): %f %f\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 ( fabs( s - a->st[ 0 ] ) > 0.01 || fabs( 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 ( fabs( s - b->st[ 0 ] ) > 0.01 || fabs( 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 ( fabs( s - c->st[ 0 ] ) > 0.01 || fabs( 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, Vector3 axis[ 3 ], const Vector3& origin, decalProjector_t *out ){
|
|
int i;
|
|
|
|
|
|
/* 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 ( 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 ( 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, 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 */
|
|
numProjectors++;
|
|
return numProjectors - 1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
ProcessDecals()
|
|
finds all decal entities and creates decal projectors
|
|
*/
|
|
|
|
#define PLANAR_EPSILON 0.5f
|
|
|
|
void ProcessDecals( void ){
|
|
int j, x, y, pw[ 5 ], r, iterations;
|
|
float distance;
|
|
Plane3f projection, plane;
|
|
entity_t *e2;
|
|
parseMesh_t *p;
|
|
mesh_t *mesh, *subdivided;
|
|
bspDrawVert_t *dv[ 4 ];
|
|
|
|
|
|
/* 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 == NULL ) {
|
|
Sys_Warning( "Decal entity without any patch meshes, ignoring.\n" );
|
|
e.epairs.clear();
|
|
continue;
|
|
}
|
|
|
|
/* find target */
|
|
e2 = FindTargetEntity( e.valueForKey( "target" ) );
|
|
|
|
/* no target? */
|
|
if ( e2 == NULL ) {
|
|
Sys_Warning( "Decal entity without a valid target, ignoring.\n" );
|
|
continue;
|
|
}
|
|
|
|
/* walk entity patches */
|
|
for ( p = e.patches; p != NULL; 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 */
|
|
iterations = IterationsForCurve( p->longestCurve, patchSubdivisions );
|
|
subdivided = SubdivideMesh2( p->mesh, iterations );
|
|
|
|
/* fit it to the curve and remove colinear verts on rows/columns */
|
|
PutMeshOnCurve( *subdivided );
|
|
mesh = RemoveLinearMeshColumnsRows( subdivided );
|
|
FreeMesh( subdivided );
|
|
|
|
/* offset by projector origin */
|
|
for ( j = 0; j < ( mesh->width * mesh->height ); j++ )
|
|
mesh->verts[ j ].xyz += e.origin;
|
|
|
|
/* iterate through the mesh quads */
|
|
for ( y = 0; y < ( mesh->height - 1 ); y++ )
|
|
{
|
|
for ( x = 0; x < ( mesh->width - 1 ); x++ )
|
|
{
|
|
/* set indexes */
|
|
pw[ 0 ] = x + ( y * mesh->width );
|
|
pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
|
|
pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
|
|
pw[ 3 ] = x + 1 + ( y * mesh->width );
|
|
pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
|
|
|
|
/* set radix */
|
|
r = ( x + y ) & 1;
|
|
|
|
/* get drawverts */
|
|
dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
|
|
dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
|
|
dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
|
|
dv[ 3 ] = &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 ) &&
|
|
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 */
|
|
free( mesh );
|
|
}
|
|
|
|
/* remove patch from entity (fixme: leak!) */
|
|
e.patches = p->next;
|
|
|
|
/* push patch to worldspawn (enable this to debug projectors) */
|
|
#if 0
|
|
p->next = entities[ 0 ].patches;
|
|
entities[ 0 ].patches = p;
|
|
#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, mapDrawSurface_t *ds, winding_t *w ){
|
|
int i, j;
|
|
float d, d2, alpha;
|
|
winding_t *front, *back;
|
|
mapDrawSurface_t *ds2;
|
|
bspDrawVert_t *dv;
|
|
Plane3f plane;
|
|
|
|
|
|
/* dummy check */
|
|
if ( w->numpoints < 3 ) {
|
|
FreeWinding( w );
|
|
return;
|
|
}
|
|
|
|
/* offset by entity origin */
|
|
for ( i = 0; i < w->numpoints; i++ )
|
|
w->p[ i ] += entityOrigin;
|
|
|
|
/* make a plane from the winding */
|
|
if ( !PlaneFromPoints( plane, w->p[ 0 ], w->p[ 1 ], w->p[ 2 ] ) ) {
|
|
FreeWinding( w );
|
|
return;
|
|
}
|
|
|
|
/* backface check */
|
|
if ( vector3_dot( dp->planes[ 0 ].normal(), plane.normal() ) < -0.0001f ) {
|
|
FreeWinding( w );
|
|
return;
|
|
}
|
|
|
|
/* walk list of planes */
|
|
for ( i = 0; i < dp->numPlanes; i++ )
|
|
{
|
|
/* chop winding by the plane */
|
|
ClipWindingEpsilonStrict( w, dp->planes[ i ], 0.0625f, &front, &back ); /* strict, if identical plane we don't want to keep it */
|
|
FreeWinding( w );
|
|
|
|
/* lose the front fragment */
|
|
if ( front != NULL ) {
|
|
FreeWinding( front );
|
|
}
|
|
|
|
/* if nothing left in back, then bail */
|
|
if ( back == NULL ) {
|
|
return;
|
|
}
|
|
|
|
/* reset winding */
|
|
w = back;
|
|
}
|
|
|
|
/* nothing left? */
|
|
if ( w == NULL || w->numpoints < 3 ) {
|
|
return;
|
|
}
|
|
|
|
/* add to counts */
|
|
numDecalSurfaces++;
|
|
|
|
/* make a new surface */
|
|
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->numVerts = w->numpoints;
|
|
ds2->verts = safe_calloc( ds2->numVerts * sizeof( *ds2->verts ) );
|
|
|
|
/* set vertexes */
|
|
for ( i = 0; i < ds2->numVerts; i++ )
|
|
{
|
|
/* get vertex */
|
|
dv = &ds2->verts[ i ];
|
|
|
|
/* set alpha */
|
|
d = plane3_distance_to_point( dp->planes[ 0 ], w->p[ i ] );
|
|
d2 = plane3_distance_to_point( dp->planes[ 1 ], w->p[ i ] );
|
|
alpha = 255.0f * d2 / ( d + d2 );
|
|
if ( alpha > 255 ) {
|
|
alpha = 255;
|
|
}
|
|
else if ( alpha < 0 ) {
|
|
alpha = 0;
|
|
}
|
|
|
|
/* set misc */
|
|
dv->xyz = w->p[ 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 ( j = 0; j < MAX_LIGHTMAPS; j++ )
|
|
{
|
|
dv->color[ j ] = { 255, 255, 255, alpha };
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
ProjectDecalOntoFace()
|
|
projects a decal onto a brushface surface
|
|
*/
|
|
|
|
static void ProjectDecalOntoFace( decalProjector_t *dp, mapDrawSurface_t *ds ){
|
|
/* dummy check */
|
|
if ( ds->sideRef == NULL || ds->sideRef->side == NULL ) {
|
|
return;
|
|
}
|
|
|
|
/* backface check */
|
|
if ( ds->planar ) {
|
|
Plane3f plane = mapplanes[ ds->planeNum ].plane;
|
|
plane.dist() += vector3_dot( plane.normal(), entityOrigin );
|
|
if ( vector3_dot( dp->planes[ 0 ].normal(), plane.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 ){
|
|
int x, y, pw[ 5 ], r, iterations;
|
|
mesh_t src, *mesh, *subdivided;
|
|
winding_t *w;
|
|
|
|
|
|
/* backface check */
|
|
if ( ds->planar ) {
|
|
Plane3f plane = mapplanes[ ds->planeNum ].plane;
|
|
plane.dist() += vector3_dot( plane.normal(), entityOrigin );
|
|
if ( vector3_dot( dp->planes[ 0 ].normal(), plane.normal() ) < -0.0001f ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* tesselate the patch */
|
|
src.width = ds->patchWidth;
|
|
src.height = ds->patchHeight;
|
|
src.verts = ds->verts;
|
|
iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
|
|
subdivided = SubdivideMesh2( src, iterations );
|
|
|
|
/* fit it to the curve and remove colinear verts on rows/columns */
|
|
PutMeshOnCurve( *subdivided );
|
|
mesh = RemoveLinearMeshColumnsRows( subdivided );
|
|
FreeMesh( subdivided );
|
|
|
|
/* iterate through the mesh quads */
|
|
for ( y = 0; y < ( mesh->height - 1 ); y++ )
|
|
{
|
|
for ( x = 0; x < ( mesh->width - 1 ); x++ )
|
|
{
|
|
/* set indexes */
|
|
pw[ 0 ] = x + ( y * mesh->width );
|
|
pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
|
|
pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
|
|
pw[ 3 ] = x + 1 + ( y * mesh->width );
|
|
pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
|
|
|
|
/* set radix */
|
|
r = ( x + y ) & 1;
|
|
|
|
/* generate decal for first triangle */
|
|
w = AllocWinding( 3 );
|
|
w->numpoints = 3;
|
|
w->p[ 0 ] = mesh->verts[ pw[ r + 0 ] ].xyz;
|
|
w->p[ 1 ] = mesh->verts[ pw[ r + 1 ] ].xyz;
|
|
w->p[ 2 ] = mesh->verts[ pw[ r + 2 ] ].xyz;
|
|
ProjectDecalOntoWinding( dp, ds, w );
|
|
|
|
/* generate decal for second triangle */
|
|
w = AllocWinding( 3 );
|
|
w->numpoints = 3;
|
|
w->p[ 0 ] = mesh->verts[ pw[ r + 0 ] ].xyz;
|
|
w->p[ 1 ] = mesh->verts[ pw[ r + 2 ] ].xyz;
|
|
w->p[ 2 ] = mesh->verts[ pw[ r + 3 ] ].xyz;
|
|
ProjectDecalOntoWinding( dp, ds, w );
|
|
}
|
|
}
|
|
|
|
/* clean up */
|
|
free( mesh );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
ProjectDecalOntoTriangles()
|
|
projects a decal onto a triangle surface
|
|
*/
|
|
|
|
static void ProjectDecalOntoTriangles( decalProjector_t *dp, mapDrawSurface_t *ds ){
|
|
|
|
/* triangle surfaces without shaders don't get marks by default */
|
|
if ( ds->type == ESurfaceType::Triangles && ds->shaderInfo->shaderText == NULL ) {
|
|
return;
|
|
}
|
|
|
|
/* backface check */
|
|
if ( ds->planar ) {
|
|
Plane3f plane = mapplanes[ ds->planeNum ].plane;
|
|
plane.dist() += vector3_dot( plane.normal(), entityOrigin );
|
|
if ( vector3_dot( dp->planes[ 0 ].normal(), plane.normal() ) < -0.0001f ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* iterate through triangles */
|
|
for ( int i = 0; i < ds->numIndexes; i += 3 )
|
|
{
|
|
/* generate decal */
|
|
winding_t *w = AllocWinding( 3 );
|
|
w->numpoints = 3;
|
|
w->p[ 0 ] = ds->verts[ ds->indexes[ i ] ].xyz;
|
|
w->p[ 1 ] = ds->verts[ ds->indexes[ i + 1 ] ].xyz;
|
|
w->p[ 2 ] = ds->verts[ ds->indexes[ i + 2 ] ].xyz;
|
|
ProjectDecalOntoWinding( dp, ds, w );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
MakeEntityDecals()
|
|
projects decals onto world surfaces
|
|
*/
|
|
|
|
void MakeEntityDecals( entity_t *e ){
|
|
int i, j, k, f, fOld, start;
|
|
decalProjector_t dp;
|
|
mapDrawSurface_t *ds;
|
|
Vector3 identityAxis[ 3 ] = { g_vector3_axis_x, g_vector3_axis_y, g_vector3_axis_z };
|
|
|
|
|
|
/* note it */
|
|
Sys_FPrintf( SYS_VRB, "--- MakeEntityDecals ---\n" );
|
|
|
|
/* transform projector instead of geometry */
|
|
entityOrigin.set( 0 );
|
|
|
|
/* init pacifier */
|
|
fOld = -1;
|
|
start = I_FloatTime();
|
|
|
|
/* walk the list of decal projectors */
|
|
for ( i = 0; i < numProjectors; i++ )
|
|
{
|
|
/* print pacifier */
|
|
f = 10 * i / numProjectors;
|
|
if ( f != fOld ) {
|
|
fOld = f;
|
|
Sys_FPrintf( SYS_VRB, "%d...", f );
|
|
}
|
|
|
|
/* get projector */
|
|
TransformDecalProjector( &projectors[ i ], identityAxis, e->origin, &dp );
|
|
|
|
/* walk the list of surfaces in the entity */
|
|
for ( j = e->firstDrawSurf; j < numMapDrawSurfs; j++ )
|
|
{
|
|
/* get surface */
|
|
ds = &mapDrawSurfs[ j ];
|
|
if ( ds->numVerts <= 0 ) {
|
|
continue;
|
|
}
|
|
|
|
/* ignore autosprite or nomarks */
|
|
if ( ds->shaderInfo->autosprite || ( ds->shaderInfo->compileFlags & C_NOMARKS ) ) {
|
|
continue;
|
|
}
|
|
|
|
/* bounds check */
|
|
for ( k = 0; k < 3; k++ )
|
|
if ( ds->minmax.mins[ k ] >= ( dp.center[ k ] + dp.radius ) ||
|
|
ds->minmax.maxs[ k ] <= ( dp.center[ k ] - dp.radius ) ) {
|
|
break;
|
|
}
|
|
if ( k < 3 ) {
|
|
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) ( I_FloatTime() - start ) );
|
|
|
|
/* emit some stats */
|
|
Sys_FPrintf( SYS_VRB, "%9d decal surfaces\n", numDecalSurfaces );
|
|
}
|