/* ============= R_PlaneForSurface ============= */ void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { srfTriangles_t *tri; srfPoly_t *poly; drawVert_t *v1, *v2, *v3; vec4_t plane4; if (!surfType) { Com_Memset (plane, 0, sizeof(*plane)); plane->normal[0] = 1; return; } switch (*surfType) { case SF_TRIANGLES: tri = (srfTriangles_t *)surfType; v1 = tri->verts + tri->indexes[0]; v2 = tri->verts + tri->indexes[1]; v3 = tri->verts + tri->indexes[2]; PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz ); VectorCopy( plane4, plane->normal ); plane->dist = plane4[3]; return; case SF_POLY: poly = (srfPoly_t *)surfType; PlaneFromPoints( plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz ); VectorCopy( plane4, plane->normal ); plane->dist = plane4[3]; return; default: Com_Memset (plane, 0, sizeof(*plane)); plane->normal[0] = 1; return; } }
qboolean CM_GenerateFacetFor4Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c, drawVert_t *d ) { float dist; int i; vec4_t plane; // if we can't generate a valid plane for the points, ignore the facet if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) { f->numBoundaries = 0; return qfalse; } // if the fourth point is also on the plane, we can make a quad facet dist = DotProduct( d->xyz, f->surface ) - f->surface[3]; if ( fabs( dist ) > PLANAR_EPSILON ) { f->numBoundaries = 0; return qfalse; } // make boundaries f->numBoundaries = 4; CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz ); CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz ); CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, d->xyz ); CM_GenerateBoundaryForPoints( f->boundaries[3], f->surface, d->xyz, a->xyz ); VectorCopy( a->xyz, f->points[0] ); VectorCopy( b->xyz, f->points[1] ); VectorCopy( c->xyz, f->points[2] ); VectorCopy( d->xyz, f->points[3] ); for (i = 1; i < 4; i++) { if ( !PlaneFromPoints( plane, f->points[i], f->points[(i+1) % 4], f->points[(i+2) % 4]) ) { f->numBoundaries = 0; return qfalse; } if (DotProduct(f->surface, plane) < 0.9) { f->numBoundaries = 0; return qfalse; } } TextureMatrixFromPoints( f, a, b, c ); return qtrue; }
void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { srfTriangles_t *tri; srfPoly_t *poly; drawVert_t *v1, *v2, *v3; vector4 plane4; if (!surfType) { memset (plane, 0, sizeof(*plane)); plane->normal.x = 1; return; } switch (*surfType) { case SF_FACE: *plane = ((srfSurfaceFace_t *)surfType)->plane; return; case SF_TRIANGLES: tri = (srfTriangles_t *)surfType; v1 = tri->verts + tri->indexes[0]; v2 = tri->verts + tri->indexes[1]; v3 = tri->verts + tri->indexes[2]; PlaneFromPoints( &plane4, &v1->xyz, &v2->xyz, &v3->xyz ); VectorCopy( (vector3 *)&plane4, &plane->normal ); plane->dist = plane4.w; return; case SF_POLY: poly = (srfPoly_t *)surfType; PlaneFromPoints( &plane4, &poly->verts[0].xyz, &poly->verts[1].xyz, &poly->verts[2].xyz ); VectorCopy( (vector3 *)&plane4, &plane->normal ); plane->dist = plane4.w; return; default: memset (plane, 0, sizeof(*plane)); plane->normal.x = 1; return; } }
/* ===================== CM_GenerateFacetFor3Points ===================== */ qboolean CM_GenerateFacetFor3Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) { // if we can't generate a valid plane for the points, ignore the facet if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) { f->numBoundaries = 0; return qfalse; } // make boundaries f->numBoundaries = 3; CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz ); CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz ); CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, a->xyz ); VectorCopy( a->xyz, f->points[0] ); VectorCopy( b->xyz, f->points[1] ); VectorCopy( c->xyz, f->points[2] ); TextureMatrixFromPoints( f, a, b, c ); return qtrue; }
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; vec4_t plane; /* dummy check */ if( w->numpoints < 3 ) { FreeWinding( w ); return; } /* offset by entity origin */ for( i = 0; i < w->numpoints; i++ ) VectorAdd( w->p[ i ], entityOrigin, w->p[ i ] ); /* make a plane from the winding */ if( !PlaneFromPoints( plane, w->p[ 0 ], w->p[ 1 ], w->p[ 2 ] ) ) { FreeWinding( w ); return; } /* backface check */ d = DotProduct( dp->planes[ 0 ], plane ); if( d < -0.0001f ) { FreeWinding( w ); return; } /* walk list of planes */ for( i = 0; i < dp->numPlanes; i++ ) { /* chop winding by the plane */ ClipWindingEpsilon( w, dp->planes[ i ], dp->planes[ i ][ 3 ], 0.0625f, &front, &back ); 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( SURFACE_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->numVerts = w->numpoints; ds2->verts = Malloc( ds2->numVerts * sizeof( *ds2->verts ) ); Mem_Set( ds2->verts, 0, ds2->numVerts * sizeof( *ds2->verts ) ); /* set vertexes */ for( i = 0; i < ds2->numVerts; i++ ) { /* get vertex */ dv = &ds2->verts[ i ]; /* set alpha */ d = DotProduct( w->p[ i ], dp->planes[ 0 ] ) - dp->planes[ 0 ][ 3 ]; d2 = DotProduct( w->p[ i ], dp->planes[ 1 ] ) - dp->planes[ 1 ][ 3 ]; alpha = 255.0f * d2 / (d + d2); if( alpha > 255 ) alpha = 255; else if( alpha < 0 ) alpha = 0; /* set misc */ VectorSubtract( w->p[ i ], entityOrigin, dv->xyz ); VectorCopy( plane, dv->normal ); dv->st[ 0 ] = DotProduct( dv->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ]; dv->st[ 1 ] = DotProduct( dv->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ]; /* set color */ for( j = 0; j < MAX_LIGHTMAPS; j++ ) { dv->color[ j ][ 0 ] = 255; dv->color[ j ][ 1 ] = 255; dv->color[ j ][ 2 ] = 255; dv->color[ j ][ 3 ] = alpha; } } }
/* * @brief */ static void ParseBrush(entity_t *mapent) { map_brush_t *b; int32_t i, j, k; side_t *side, *s2; int32_t plane_num; map_brush_texture_t td; vec3_t planepts[3]; if (num_map_brushes == MAX_BSP_BRUSHES) Com_Error(ERR_FATAL, "MAX_BSP_BRUSHES\n"); b = &map_brushes[num_map_brushes]; b->original_sides = &map_brush_sides[num_map_brush_sides]; b->entity_num = num_entities - 1; b->brush_num = num_map_brushes - mapent->first_brush; do { if (!GetToken(true)) break; if (!g_strcmp0(token, "}")) break; if (num_map_brush_sides == MAX_BSP_BRUSH_SIDES) Com_Error(ERR_FATAL, "MAX_BSP_BRUSH_SIDES\n"); side = &map_brush_sides[num_map_brush_sides]; // read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) GetToken(true); if (g_strcmp0(token, "(")) Com_Error(ERR_FATAL, "Parsing brush\n"); for (j = 0; j < 3; j++) { GetToken(false); planepts[i][j] = atof(token); } GetToken(false); if (g_strcmp0(token, ")")) Com_Error(ERR_FATAL, "Parsing brush\n"); } memset(&td, 0, sizeof(td)); // read the texturedef GetToken(false); if (strlen(token) > sizeof(td.name) - 1) Com_Error(ERR_FATAL, "Texture name \"%s\" is too long.\n", token); g_strlcpy(td.name, token, sizeof(td.name)); GetToken(false); td.shift[0] = atoi(token); GetToken(false); td.shift[1] = atoi(token); GetToken(false); td.rotate = atoi(token); GetToken(false); td.scale[0] = atof(token); GetToken(false); td.scale[1] = atof(token); if (TokenAvailable()) { GetToken(false); side->contents = atoi(token); GetToken(false); side->surf = td.flags = atoi(token); GetToken(false); td.value = atoi(token); } else { side->contents = CONTENTS_SOLID; side->surf = td.flags = 0; td.value = 0; } // resolve implicit surface and contents flags SetImpliedFlags(side, td.name); // translucent objects are automatically classified as detail if (side->surf & (SURF_ALPHA_TEST | SURF_BLEND_33 | SURF_BLEND_66)) side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP)) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP | CONTENTS_MIST))) side->contents |= CONTENTS_SOLID; // hints and skips are never detail, and have no content if (side->surf & (SURF_HINT | SURF_SKIP)) { side->contents = 0; side->surf &= ~CONTENTS_DETAIL; } // find the plane number plane_num = PlaneFromPoints(planepts[0], planepts[1], planepts[2]); if (plane_num == -1) { Com_Verbose("Entity %i, Brush %i: plane with no normal\n", b->entity_num, b->brush_num); continue; } // see if the plane has been used already for (k = 0; k < b->num_sides; k++) { s2 = b->original_sides + k; if (s2->plane_num == plane_num) { Com_Verbose("Entity %i, Brush %i: duplicate plane\n", b->entity_num, b->brush_num); break; } if (s2->plane_num == (plane_num ^ 1)) { Com_Verbose("Entity %i, Brush %i: mirrored plane\n", b->entity_num, b->brush_num); break; } } if (k != b->num_sides) continue; // duplicated // keep this side side = b->original_sides + b->num_sides; side->plane_num = plane_num; side->texinfo = TexinfoForBrushTexture(&map_planes[plane_num], &td, vec3_origin); // save the td off in case there is an origin brush and we // have to recalculate the texinfo map_brush_textures[num_map_brush_sides] = td; num_map_brush_sides++; b->num_sides++; } while (true); // get the content for the entire brush b->contents = BrushContents(b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL)) { b->num_sides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & MASK_LIQUID)) { b->num_sides = 0; return; } // create windings for sides and bounds for brush MakeBrushWindings(b); // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP)) { c_clip_brushes++; for (i = 0; i < b->num_sides; i++) b->original_sides[i].texinfo = TEXINFO_NODE; } // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity. After the entire entity is parsed, // the plane_nums and texinfos will be adjusted for // the origin brush if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Com_Error(ERR_FATAL, "Entity %i, Brush %i: origin brushes not allowed in world\n", b->entity_num, b->brush_num); return; } VectorAdd(b->mins, b->maxs, origin); VectorScale(origin, 0.5, origin); sprintf(string, "%i %i %i", (int32_t)origin[0], (int32_t)origin[1], (int32_t)origin[2]); SetKeyValue(&entities[b->entity_num], "origin", string); VectorCopy(origin, entities[b->entity_num].origin); // don't keep this brush b->num_sides = 0; return; } AddBrushBevels(b); num_map_brushes++; mapent->num_brushes++; }
void ProcessDecals( void ) { int i, j, x, y, pw[ 5 ], r, iterations, smoothNormals; float distance, lightmapScale, backfaceAngle; vec4_t projection, plane; vec3_t origin, target, delta, lightmapAxis; vec3_t minlight, minvertexlight, ambient, colormod; entity_t *e, *e2; parseMesh_t *p; mesh_t *mesh, *subdivided; bspDrawVert_t *dv[ 4 ]; const char *value; /* note it */ Sys_FPrintf( SYS_VRB, "--- ProcessDecals ---\n" ); /* no decals */ if (nodecals) { for( i = 0; i < numEntities; i++ ) { /* get entity */ e = &entities[ i ]; value = ValueForKey( e, "classname" ); if( Q_stricmp( value, "_decal" ) && Q_stricmp( value, "misc_decal" ) ) continue; /* clear entity patches */ e->patches = NULL; // fixme: LEAK! } return; } /* walk entity list */ for( i = 0; i < numEntities; i++ ) { /* get entity */ e = &entities[ i ]; value = ValueForKey( e, "classname" ); if( Q_stricmp( value, "_decal" ) && Q_stricmp( value, "misc_decal" ) ) continue; /* any patches? */ if( e->patches == NULL ) { Sys_Warning( e->mapEntityNum, "Decal entity without any patch meshes, ignoring." ); e->epairs = NULL; /* fixme: leak! */ continue; } /* find target */ value = ValueForKey( e, "target" ); e2 = FindTargetEntity( value ); /* no target? */ if( e2 == NULL ) { Sys_Warning( e->mapEntityNum, "Decal entity without a valid target, ignoring." ); continue; } /* vortex: get lightmap scaling value for this entity */ GetEntityLightmapScale( e, &lightmapScale, 0); /* vortex: get lightmap axis for this entity */ GetEntityLightmapAxis( e, lightmapAxis, NULL ); /* vortex: per-entity normal smoothing */ GetEntityNormalSmoothing( e, &smoothNormals, 0); /* vortex: per-entity _minlight, _ambient, _color, _colormod */ GetEntityMinlightAmbientColor( e, NULL, minlight, minvertexlight, ambient, colormod, qtrue ); /* vortex: _backfacecull */ if ( KeyExists(e, "_backfacecull") ) backfaceAngle = FloatForKey(e, "_backfacecull"); else if ( KeyExists(e, "_bfc") ) backfaceAngle = FloatForKey(e, "_bfc"); else backfaceAngle = 90.0f; /* walk entity patches */ for( p = e->patches; p != NULL; p = e->patches ) { /* setup projector */ if( VectorCompare( e->origin, vec3_origin ) ) { VectorAdd( p->eMins, p->eMaxs, origin ); VectorScale( origin, 0.5f, origin ); } else VectorCopy( e->origin, origin ); VectorCopy( e2->origin, target ); VectorSubtract( target, origin, delta ); /* setup projection plane */ distance = VectorNormalize( delta, projection ); projection[ 3 ] = DotProduct( origin, projection ); /* 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++ ) VectorAdd( mesh->verts[ j ].xyz, e->origin, mesh->verts[ j ].xyz ); /* 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) */ plane[ 0 ] = 0.0f; /* stupid msvc */ if( 0 && PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) && fabs( DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ] ) <= PLANAR_EPSILON ) { /* make a quad projector */ MakeDecalProjector( i, p->shaderInfo, projection, distance, 4, dv, cos(backfaceAngle / 180.0f * Q_PI), lightmapScale, lightmapAxis, minlight, minvertexlight, ambient, colormod, smoothNormals); } else { /* make first triangle */ MakeDecalProjector( i, p->shaderInfo, projection, distance, 3, dv, cos(backfaceAngle / 180.0f * Q_PI), lightmapScale, lightmapAxis, minlight, minvertexlight, ambient, colormod, smoothNormals); /* make second triangle */ dv[ 1 ] = dv[ 2 ]; dv[ 2 ] = dv[ 3 ]; MakeDecalProjector( i, p->shaderInfo, projection, distance, 3, dv, cos(backfaceAngle / 180.0f * Q_PI), lightmapScale, lightmapAxis, minlight, minvertexlight, ambient, colormod, smoothNormals); } } } /* 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 ); }
void ProcessDecals( void ) { int i, j, x, y, pw[ 5 ], r, iterations; float distance; vec4_t projection, plane; vec3_t origin, target, delta; entity_t *e, *e2; parseMesh_t *p; mesh_t *mesh, *subdivided; bspDrawVert_t *dv[4]; const char *value; /* note it */ MsgDev( D_NOTE, "--- ProcessDecals ---\n" ); /* walk entity list */ for( i = 0; i < numEntities; i++ ) { /* get entity */ e = &entities[ i ]; value = ValueForKey( e, "classname" ); if( com.stricmp( value, "_decal" ) ) continue; /* any patches? */ if( e->patches == NULL ) { MsgDev( D_WARN, "Decal entity without any patch meshes, ignoring.\n" ); e->epairs = NULL; /* fixme: leak! */ continue; } /* find target */ value = ValueForKey( e, "target" ); e2 = FindTargetEntity( value ); /* no target? */ if( e2 == NULL ) { MsgDev( D_WARN, "Decal entity without a valid target, ignoring.\n" ); continue; } /* walk entity patches */ for( p = e->patches; p != NULL; p = e->patches ) { /* setup projector */ if( VectorCompare( e->origin, vec3_origin ) ) { VectorAdd( p->eMins, p->eMaxs, origin ); VectorScale( origin, 0.5f, origin ); } else VectorCopy( e->origin, origin ); VectorCopy( e2->origin, target ); VectorSubtract( target, origin, delta ); /* setup projection plane */ distance = VectorNormalizeLength2( delta, projection ); projection[ 3 ] = DotProduct( origin, projection ); /* create projectors */ if( distance > 0.125f ) { /* tesselate the patch */ iterations = IterationsForCurve( p->longestCurve, patch_subdivide->integer ); 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++ ) VectorAdd( mesh->verts[ j ].xyz, e->origin, mesh->verts[ j ].xyz ); /* 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) */ plane[ 0 ] = 0.0f; /* stupid msvc */ if( 0 && PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) && fabs( DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ] ) <= 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 */ Mem_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 */ MsgDev( D_NOTE, "%9d decal projectors\n", numProjectors ); }
void RE_ProjectDecal( qhandle_t hShader, int numPoints, vec3_t *points, vec4_t projection, vec4_t color, int lifeTime, int fadeTime ) { int i; float radius, iDist; vec3_t xyz; vec4_t omniProjection; decalVert_t dv[ 4 ]; decalProjector_t *dp, temp; /* first frame rendered does not have a valid decals list */ if ( tr.refdef.decalProjectors == NULL ) { return; } /* dummy check */ if ( numPoints != 1 && numPoints != 3 && numPoints != 4 ) { ri.Printf( PRINT_WARNING, "WARNING: Invalid number of decal points (%d)\n", numPoints ); return; } /* early outs */ if ( lifeTime == 0 ) { return; } if ( projection[ 3 ] <= 0.0f ) { return; } /* set times properly */ if ( lifeTime < 0 || fadeTime < 0 ) { lifeTime = 0; fadeTime = 0; } /* basic setup */ temp.shader = R_GetShaderByHandle( hShader ); /* debug code */ temp.numPlanes = temp.shader->entityMergable; temp.color[ 0 ] = color[ 0 ] * 255; temp.color[ 1 ] = color[ 1 ] * 255; temp.color[ 2 ] = color[ 2 ] * 255; temp.color[ 3 ] = color[ 3 ] * 255; temp.numPlanes = numPoints + 2; temp.fadeStartTime = tr.refdef.time + lifeTime - fadeTime; temp.fadeEndTime = temp.fadeStartTime + fadeTime; /* set up decal texcoords (fixme: support arbitrary projector st coordinates in trapcall) */ dv[ 0 ].st[ 0 ] = 0.0f; dv[ 0 ].st[ 1 ] = 0.0f; dv[ 1 ].st[ 0 ] = 0.0f; dv[ 1 ].st[ 1 ] = 1.0f; dv[ 2 ].st[ 0 ] = 1.0f; dv[ 2 ].st[ 1 ] = 1.0f; dv[ 3 ].st[ 0 ] = 1.0f; dv[ 3 ].st[ 1 ] = 0.0f; /* omnidirectional? */ if ( numPoints == 1 ) { /* set up omnidirectional */ numPoints = 4; temp.numPlanes = 6; temp.omnidirectional = qtrue; radius = projection[ 3 ]; Vector4Set( omniProjection, 0.0f, 0.0f, -1.0f, radius * 2.0f ); projection = omniProjection; iDist = 1.0f / ( radius * 2.0f ); /* set corner */ VectorSet( xyz, points[ 0 ][ 0 ] - radius, points[ 0 ][ 1 ] - radius, points[ 0 ][ 2 ] + radius ); /* make x axis texture matrix (yz) */ VectorSet( temp.texMat[ 0 ][ 0 ], 0.0f, iDist, 0.0f ); temp.texMat[ 0 ][ 0 ][ 3 ] = -DotProduct( temp.texMat[ 0 ][ 0 ], xyz ); VectorSet( temp.texMat[ 0 ][ 1 ], 0.0f, 0.0f, iDist ); temp.texMat[ 0 ][ 1 ][ 3 ] = -DotProduct( temp.texMat[ 0 ][ 1 ], xyz ); /* make y axis texture matrix (xz) */ VectorSet( temp.texMat[ 1 ][ 0 ], iDist, 0.0f, 0.0f ); temp.texMat[ 1 ][ 0 ][ 3 ] = -DotProduct( temp.texMat[ 1 ][ 0 ], xyz ); VectorSet( temp.texMat[ 1 ][ 1 ], 0.0f, 0.0f, iDist ); temp.texMat[ 1 ][ 1 ][ 3 ] = -DotProduct( temp.texMat[ 1 ][ 1 ], xyz ); /* make z axis texture matrix (xy) */ VectorSet( temp.texMat[ 2 ][ 0 ], iDist, 0.0f, 0.0f ); temp.texMat[ 2 ][ 0 ][ 3 ] = -DotProduct( temp.texMat[ 2 ][ 0 ], xyz ); VectorSet( temp.texMat[ 2 ][ 1 ], 0.0f, iDist, 0.0f ); temp.texMat[ 2 ][ 1 ][ 3 ] = -DotProduct( temp.texMat[ 2 ][ 1 ], xyz ); /* setup decal points */ VectorSet( dv[ 0 ].xyz, points[ 0 ][ 0 ] - radius, points[ 0 ][ 1 ] - radius, points[ 0 ][ 2 ] + radius ); VectorSet( dv[ 1 ].xyz, points[ 0 ][ 0 ] - radius, points[ 0 ][ 1 ] + radius, points[ 0 ][ 2 ] + radius ); VectorSet( dv[ 2 ].xyz, points[ 0 ][ 0 ] + radius, points[ 0 ][ 1 ] + radius, points[ 0 ][ 2 ] + radius ); VectorSet( dv[ 3 ].xyz, points[ 0 ][ 0 ] + radius, points[ 0 ][ 1 ] - radius, points[ 0 ][ 2 ] + radius ); } else { /* set up unidirectional */ temp.omnidirectional = qfalse; /* set up decal points */ VectorCopy( points[ 0 ], dv[ 0 ].xyz ); VectorCopy( points[ 1 ], dv[ 1 ].xyz ); VectorCopy( points[ 2 ], dv[ 2 ].xyz ); VectorCopy( points[ 3 ], dv[ 3 ].xyz ); /* make texture matrix */ if ( !MakeTextureMatrix( temp.texMat[ 0 ], projection, &dv[ 0 ], &dv[ 1 ], &dv[ 2 ] ) ) { return; } } /* bound the projector */ ClearBounds( temp.mins, temp.maxs ); for ( i = 0; i < numPoints; i++ ) { AddPointToBounds( dv[ i ].xyz, temp.mins, temp.maxs ); VectorMA( dv[ i ].xyz, projection[ 3 ], projection, xyz ); AddPointToBounds( xyz, temp.mins, temp.maxs ); } /* make bounding sphere */ VectorAdd( temp.mins, temp.maxs, temp.center ); VectorScale( temp.center, 0.5f, temp.center ); VectorSubtract( temp.maxs, temp.center, xyz ); temp.radius = VectorLength( xyz ); temp.radius2 = temp.radius * temp.radius; /* frustum cull the projector (fixme: this uses a stale frustum!) */ if ( R_CullPointAndRadius( temp.center, temp.radius ) == CULL_OUT ) { return; } /* make the front plane */ if ( !PlaneFromPoints( temp.planes[ 0 ], dv[ 0 ].xyz, dv[ 1 ].xyz, dv[ 2 ].xyz ) ) { return; } /* make the back plane */ VectorSubtract( vec3_origin, temp.planes[ 0 ], temp.planes[ 1 ] ); VectorMA( dv[ 0 ].xyz, projection[ 3 ], projection, xyz ); temp.planes[ 1 ][ 3 ] = DotProduct( xyz, temp.planes[ 1 ] ); /* make the side planes */ for ( i = 0; i < numPoints; i++ ) { VectorMA( dv[ i ].xyz, projection[ 3 ], projection, xyz ); if ( !PlaneFromPoints( temp.planes[ i + 2 ], dv[( i + 1 ) % numPoints ].xyz, dv[ i ].xyz, xyz ) ) { return; } } /* create a new projector */ dp = &tr.refdef.decalProjectors[ r_numDecalProjectors & DECAL_PROJECTOR_MASK ]; Com_Memcpy( dp, &temp, sizeof( *dp ) ); /* we have a winner */ r_numDecalProjectors++; }
void MakeDecimatedMap(int *NumNodes, int *NumTris, NODE **pNode, TRI **pTri) { int compare(TRITABLE *, TRITABLE *); int Bisect(NODE *, int, int, int); void CalcAngles(NODE *, int *, float *); void EdgeOnSide(int *, int *, int *); int tricall(int, NODE *, int *, TRI **, TRI **, const char *); int CheckBorders(int *,int,NODE *,int *,TRI **); float biggesterror; int i, j, N; int j0, j1, j2; int NumNodesToSave; int NumNodesUsed; NODE *Node; TRI *Tri; TRITABLE *TriTable; if(Decimate <= 0) return; /* ghCursorCurrent = LoadCursor(NULL,IDC_WAIT); SetCursor(ghCursorCurrent); */ dh = (Hur-Hll)/NH; dv = (Vur-Vll)/NV; NVP1 = NV+1; NumNodes[0] = (NH+1)*(NVP1); *pNode = (NODE *) malloc(NumNodes[0] * sizeof(NODE)); Node = *pNode; memset(Node,0,NumNodes[0]*sizeof(NODE)); // Copy [NH][NV] vertex array to our working node array for(i=0,N=0; i<=NH; i++) { for(j=0; j<=NV; j++, N++) { Node[N].p[0] = (float)xyz[i][j].p[0]; Node[N].p[1] = (float)xyz[i][j].p[1]; Node[N].p[2] = (float)xyz[i][j].p[2]; Node[N].fixed = xyz[i][j].fixed; } } // Start things off with the corner values Node[ 0].used = 1; Node[NV].used = 1; Node[NH*NVP1].used = 1; Node[NH*NVP1+NV].used = 1; NumNodesUsed = 4; tricall(NumNodes[0], Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; // Which coordinates are we triangulating on? switch(Plane) { case PLANE_XZ0: case PLANE_XZ1: j0 = 1; j1 = 0; j2 = 2; break; case PLANE_YZ0: case PLANE_YZ1: j0 = 0; j1 = 1; j2 = 2; break; default: j0 = 2; j1 = 0; j2 = 1; } // TriTable stores the largest error in a triangle and the node where that // error occurs TriTable = (TRITABLE *) malloc(NH*NV*2 * sizeof(TRITABLE)); NumNodesToSave = min(NumNodes[0], (int)(0.01*(100-Decimate)*(NumNodes[0]-NumNodesUsed)+NumNodesUsed)); while(NumNodesUsed < NumNodesToSave) { for(i=0; i<NumTris[0]; i++) Tri[i].flag = 0; // For every node that's not currently used, find what triangle it // lies on, and the error at this node for(i=0, biggesterror=0; i<NumNodes[0]; i++) { if(Node[i].used) continue; for(j=0, Node[i].tri=-1; (j<NumTris[0]) && (Node[i].tri==-1); j++) { if( side(Node[i].p[j1], Node[i].p[j2], Node[Tri[j].v[0]].p[j1],Node[Tri[j].v[0]].p[j2], Node[Tri[j].v[1]].p[j1],Node[Tri[j].v[1]].p[j2]) < 0. ) continue; if( side(Node[i].p[j1], Node[i].p[j2], Node[Tri[j].v[1]].p[j1],Node[Tri[j].v[1]].p[j2], Node[Tri[j].v[2]].p[j1],Node[Tri[j].v[2]].p[j2]) < 0. ) continue; if( side(Node[i].p[j1], Node[i].p[j2], Node[Tri[j].v[2]].p[j1],Node[Tri[j].v[2]].p[j2], Node[Tri[j].v[0]].p[j1],Node[Tri[j].v[0]].p[j2]) < 0. ) continue; Node[i].tri = j; } if(Node[i].tri < 0) { /* ghCursorCurrent = ghCursorDefault; SetCursor(ghCursorCurrent); */ g_FuncTable.m_pfnMessageBox(g_pRadiantWnd, "Error: Couldn't find the triangle bounding a point.", "Decimation Error",MB_ICONEXCLAMATION, NULL); return; } if(!Tri[Node[i].tri].flag) { PlaneFromPoints(Node[Tri[Node[i].tri].v[0]].p, Node[Tri[Node[i].tri].v[1]].p, Node[Tri[Node[i].tri].v[2]].p, &Tri[Node[i].tri].plane); Tri[Node[i].tri].flag = 1; } Node[i].error = Node[i].p[j0] - (Tri[Node[i].tri].plane.dist - Tri[Node[i].tri].plane.normal[j1]*Node[i].p[j1] - Tri[Node[i].tri].plane.normal[j2]*Node[i].p[j2] )/ Tri[Node[i].tri].plane.normal[j0]; biggesterror = max(biggesterror,Absolute(Node[i].error)); } if(biggesterror == 0) NumNodesToSave = NumNodesUsed; else { // For all current triangles, build a list of worst-case nodes memset(TriTable,0,NH*NV*2*sizeof(TRITABLE)); for(i=0; i<NumNodes[0]; i++) { if(Node[i].used) continue; if(Absolute(Node[i].error) > TriTable[Node[i].tri].error) { TriTable[Node[i].tri].error = (float)(Absolute(Node[i].error)); TriTable[Node[i].tri].node = i; } } qsort( (void *)TriTable, (size_t)(NumTris[0]), sizeof(TRITABLE), (int (*)(const void *, const void *))compare ); for(i=0; i<NumTris[0] && NumNodesUsed < NumNodesToSave && TriTable[i].error > 0.5*biggesterror; i++) { if(Node[TriTable[i].node].used) continue; // shouldn't happen NumNodesUsed++; Node[TriTable[i].node].used++; } free(Tri); tricall(NumNodes[0], Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; // Sliver-check along borders. Since borders are often linear, the errors // along borders will often be zero, so no new points will be added. This // tends to produce long, thin brushes. For all border triangles, check // that minimum angle isn't less than SLIVER_ANGLE. If it is, add another // vertex. while(CheckBorders(&NumNodesUsed,NumNodes[0],Node,NumTris,pTri) > 0) { } Tri = *pTri; } } free(TriTable); // One last time (because we're pessimistic), check border triangles // CheckBorders(&NumNodesUsed,NumNodes[0],Node,NumTris,pTri); // Tri = *pTri; // Check that all fixed points are exact. If not, add them to the mix. // First check to see if we have any fixed points that aren't already used. for(i=0, N=0; i<NumNodes[0] && !N; i++) { if(Node[i].used) continue; if(Node[i].fixed) N++; } if(N) { // Zero out the flag member of all triangles, indicating that // the plane equation has not been found. for(i=0; i<NumTris[0]; i++) Tri[i].flag = 0; for(i=0; i<NumNodes[0]; i++) { if(Node[i].used) continue; if(!Node[i].fixed) continue; Node[i].tri = -1; for(j=0; j<NumTris[0] && Node[i].tri==-1; j++) { if( side(Node[i].p[j1], Node[i].p[j2], Node[Tri[j].v[0]].p[j1],Node[Tri[j].v[0]].p[j2], Node[Tri[j].v[1]].p[j1],Node[Tri[j].v[1]].p[j2]) < 0. ) continue; if( side(Node[i].p[j1], Node[i].p[j2], Node[Tri[j].v[1]].p[j1],Node[Tri[j].v[1]].p[j2], Node[Tri[j].v[2]].p[j1],Node[Tri[j].v[2]].p[j2]) < 0. ) continue; if( side(Node[i].p[j1], Node[i].p[j2], Node[Tri[j].v[2]].p[j1],Node[Tri[j].v[2]].p[j2], Node[Tri[j].v[0]].p[j1],Node[Tri[j].v[0]].p[j2]) < 0. ) continue; Node[i].tri = j; } if(Node[i].tri < 0) { /* ghCursorCurrent = ghCursorDefault; SetCursor(ghCursorCurrent); */ g_FuncTable.m_pfnMessageBox(g_pRadiantWnd, "Error: Couldn't find the triangle bounding a point.", "Decimation Error",MB_ICONEXCLAMATION, NULL); return; } if(!Tri[Node[i].tri].flag) { PlaneFromPoints(Node[Tri[Node[i].tri].v[0]].p, Node[Tri[Node[i].tri].v[1]].p, Node[Tri[Node[i].tri].v[2]].p, &Tri[Node[i].tri].plane); Tri[Node[i].tri].flag = 1; } Node[i].error = Node[i].p[j0] - (Tri[Node[i].tri].plane.dist - Tri[Node[i].tri].plane.normal[j1]*Node[i].p[j1] - Tri[Node[i].tri].plane.normal[j2]*Node[i].p[j2] )/ Tri[Node[i].tri].plane.normal[j0]; if(Absolute(Node[i].error) > 0.5) { NumNodesUsed++; Node[i].used++; free(Tri); tricall(NumNodes[0], Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; } } } // Swap node orders for surfaces facing down, north or west so that // they are counterclockwise when facing the surface if((Plane == PLANE_XY1) || (Plane == PLANE_XZ0) || (Plane == PLANE_YZ1) ) { for(i=0; i<NumTris[0]; i++) { j = Tri[i].v[1]; Tri[i].v[1] = Tri[i].v[2]; Tri[i].v[2] = j; } } // Store bounding box coords for(i=0; i<NumTris[0]; i++) { Tri[i].min[0] = Node[Tri[i].v[0]].p[0]; Tri[i].min[0] = min(Tri[i].min[0],Node[Tri[i].v[1]].p[0]); Tri[i].min[0] = min(Tri[i].min[0],Node[Tri[i].v[2]].p[0]); Tri[i].min[1] = Node[Tri[i].v[0]].p[1]; Tri[i].min[1] = min(Tri[i].min[1],Node[Tri[i].v[1]].p[1]); Tri[i].min[1] = min(Tri[i].min[1],Node[Tri[i].v[2]].p[1]); Tri[i].min[2] = Node[Tri[i].v[0]].p[2]; Tri[i].min[2] = min(Tri[i].min[2],Node[Tri[i].v[1]].p[2]); Tri[i].min[2] = min(Tri[i].min[2],Node[Tri[i].v[2]].p[2]); Tri[i].max[0] = Node[Tri[i].v[0]].p[0]; Tri[i].max[0] = max(Tri[i].max[0],Node[Tri[i].v[1]].p[0]); Tri[i].max[0] = max(Tri[i].max[0],Node[Tri[i].v[2]].p[0]); Tri[i].max[1] = Node[Tri[i].v[0]].p[1]; Tri[i].max[1] = max(Tri[i].max[1],Node[Tri[i].v[1]].p[1]); Tri[i].max[1] = max(Tri[i].max[1],Node[Tri[i].v[2]].p[1]); Tri[i].max[2] = Node[Tri[i].v[0]].p[2]; Tri[i].max[2] = max(Tri[i].max[2],Node[Tri[i].v[1]].p[2]); Tri[i].max[2] = max(Tri[i].max[2],Node[Tri[i].v[2]].p[2]); } /* ghCursorCurrent = ghCursorDefault; SetCursor(ghCursorCurrent); */ }
/* * R_AddPortalSurface */ portalSurface_t *R_AddPortalSurface( const entity_t *ent, const mesh_t *mesh, const vec3_t mins, const vec3_t maxs, const shader_t *shader ) { unsigned int i; float dist; cplane_t plane, untransformed_plane; vec3_t v[3]; portalSurface_t *portalSurface; if( !mesh ) { return NULL; } if( R_FASTSKY() && !( shader->flags & (SHADER_PORTAL_CAPTURE|SHADER_PORTAL_CAPTURE2) ) ) { // r_fastsky doesn't affect portalmaps return NULL; } for( i = 0; i < 3; i++ ) { VectorCopy( mesh->xyzArray[mesh->elems[i]], v[i] ); } PlaneFromPoints( v, &untransformed_plane ); untransformed_plane.dist += DotProduct( ent->origin, untransformed_plane.normal ); CategorizePlane( &untransformed_plane ); if( shader->flags & SHADER_AUTOSPRITE ) { vec3_t centre; // autosprites are quads, facing the viewer if( mesh->numVerts < 4 ) { return NULL; } // compute centre as average of 4 vertices VectorCopy( mesh->xyzArray[mesh->elems[3]], centre ); for( i = 0; i < 3; i++ ) VectorAdd( centre, v[i], centre ); VectorMA( ent->origin, 0.25, centre, centre ); VectorNegate( &rn.viewAxis[AXIS_FORWARD], plane.normal ); plane.dist = DotProduct( plane.normal, centre ); CategorizePlane( &plane ); } else { vec3_t temp; mat3_t entity_rotation; // regular surfaces if( !Matrix3_Compare( ent->axis, axis_identity ) ) { Matrix3_Transpose( ent->axis, entity_rotation ); for( i = 0; i < 3; i++ ) { VectorCopy( v[i], temp ); Matrix3_TransformVector( entity_rotation, temp, v[i] ); VectorMA( ent->origin, ent->scale, v[i], v[i] ); } PlaneFromPoints( v, &plane ); CategorizePlane( &plane ); } else { plane = untransformed_plane; } } if( ( dist = PlaneDiff( rn.viewOrigin, &plane ) ) <= BACKFACE_EPSILON ) { // behind the portal plane if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { return NULL; } // we need to render the backplane view } // check if portal view is opaque due to alphagen portal if( shader->portalDistance && dist > shader->portalDistance ) { return NULL; } // find the matching portal plane for( i = 0; i < rn.numPortalSurfaces; i++ ) { portalSurface = &rn.portalSurfaces[i]; if( portalSurface->entity == ent && portalSurface->shader == shader && DotProduct( portalSurface->plane.normal, plane.normal ) > 0.99f && fabs( portalSurface->plane.dist - plane.dist ) < 0.1f ) { goto addsurface; } } if( i == MAX_PORTAL_SURFACES ) { // not enough space return NULL; } portalSurface = &rn.portalSurfaces[rn.numPortalSurfaces++]; portalSurface->entity = ent; portalSurface->plane = plane; portalSurface->shader = shader; portalSurface->untransformed_plane = untransformed_plane; ClearBounds( portalSurface->mins, portalSurface->maxs ); memset( portalSurface->texures, 0, sizeof( portalSurface->texures ) ); addsurface: AddPointToBounds( mins, portalSurface->mins, portalSurface->maxs ); AddPointToBounds( maxs, portalSurface->mins, portalSurface->maxs ); VectorAdd( portalSurface->mins, portalSurface->maxs, portalSurface->centre ); VectorScale( portalSurface->centre, 0.5, portalSurface->centre ); return portalSurface; }
static void FilterTraceWindingIntoNodes_r(traceWinding_t * tw, int nodeNum) { int num; vec4_t plane1, plane2, reverse; traceNode_t *node; traceWinding_t front, back; /* don't filter if passed a bogus node (solid, etc) */ if(nodeNum < 0 || nodeNum >= numTraceNodes) return; /* get node */ node = &traceNodes[nodeNum]; /* is this a decision node? */ if(node->type >= 0) { /* create winding plane if necessary, filtering out bogus windings as well */ if(nodeNum == headNodeNum) { if(!PlaneFromPoints(tw->plane, tw->v[0].xyz, tw->v[1].xyz, tw->v[2].xyz, qtrue)) return; } /* validate the node */ if(node->children[0] == 0 || node->children[1] == 0) Error("Invalid tracenode: %d", nodeNum); /* get node plane */ Vector4Copy(node->plane, plane1); /* get winding plane */ Vector4Copy(tw->plane, plane2); /* invert surface plane */ VectorSubtract(vec3_origin, plane2, reverse); reverse[3] = -plane2[3]; /* front only */ if(DotProduct(plane1, plane2) > 0.999f && fabs(plane1[3] - plane2[3]) < 0.001f) { FilterTraceWindingIntoNodes_r(tw, node->children[0]); return; } /* back only */ if(DotProduct(plane1, reverse) > 0.999f && fabs(plane1[3] - reverse[3]) < 0.001f) { FilterTraceWindingIntoNodes_r(tw, node->children[1]); return; } /* clip the winding by node plane */ ClipTraceWinding(tw, plane1, &front, &back); /* filter by node plane */ if(front.numVerts >= 3) FilterTraceWindingIntoNodes_r(&front, node->children[0]); if(back.numVerts >= 3) FilterTraceWindingIntoNodes_r(&back, node->children[1]); /* return to caller */ return; } /* add winding to leaf node */ num = AddTraceWinding(tw); AddItemToTraceNode(node, num); }
int FindMetaTriangle( metaTriangle_t *src, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c, int planeNum ){ int triIndex; vec3_t dir; /* detect degenerate triangles fixme: do something proper here */ VectorSubtract( a->xyz, b->xyz, dir ); if ( VectorLength( dir ) < 0.125f ) { return -1; } VectorSubtract( b->xyz, c->xyz, dir ); if ( VectorLength( dir ) < 0.125f ) { return -1; } VectorSubtract( c->xyz, a->xyz, dir ); if ( VectorLength( dir ) < 0.125f ) { return -1; } /* find plane */ if ( planeNum >= 0 ) { /* because of precision issues with small triangles, try to use the specified plane */ src->planeNum = planeNum; VectorCopy( mapplanes[ planeNum ].normal, src->plane ); src->plane[ 3 ] = mapplanes[ planeNum ].dist; } else { /* calculate a plane from the triangle's points (and bail if a plane can't be constructed) */ src->planeNum = -1; if ( PlaneFromPoints( src->plane, a->xyz, b->xyz, c->xyz ) == qfalse ) { return -1; } } /* ydnar 2002-10-03: repair any bogus normals (busted ase import kludge) */ if ( VectorLength( a->normal ) <= 0.0f ) { VectorCopy( src->plane, a->normal ); } if ( VectorLength( b->normal ) <= 0.0f ) { VectorCopy( src->plane, b->normal ); } if ( VectorLength( c->normal ) <= 0.0f ) { VectorCopy( src->plane, c->normal ); } /* ydnar 2002-10-04: set lightmap axis if not already set */ if ( !( src->si->compileFlags & C_VERTEXLIT ) && src->lightmapAxis[ 0 ] == 0.0f && src->lightmapAxis[ 1 ] == 0.0f && src->lightmapAxis[ 2 ] == 0.0f ) { /* the shader can specify an explicit lightmap axis */ if ( src->si->lightmapAxis[ 0 ] || src->si->lightmapAxis[ 1 ] || src->si->lightmapAxis[ 2 ] ) { VectorCopy( src->si->lightmapAxis, src->lightmapAxis ); } /* new axis-finding code */ else{ CalcLightmapAxis( src->plane, src->lightmapAxis ); } } /* fill out the src triangle */ src->indexes[ 0 ] = FindMetaVertex( a ); src->indexes[ 1 ] = FindMetaVertex( b ); src->indexes[ 2 ] = FindMetaVertex( c ); /* try to find an existing triangle */ #ifdef USE_EXHAUSTIVE_SEARCH { int i; metaTriangle_t *tri; for ( i = 0, tri = metaTriangles; i < numMetaTriangles; i++, tri++ ) { if ( memcmp( src, tri, sizeof( metaTriangle_t ) ) == 0 ) { return i; } } } #endif /* get a new triangle */ triIndex = AddMetaTriangle(); /* add the triangle */ memcpy( &metaTriangles[ triIndex ], src, sizeof( metaTriangle_t ) ); /* return the triangle index */ return triIndex; }
/* =========== MakeBrushPlanes =========== */ qboolean MakeBrushPlanes (brush_t *b) { int i, j; int planenum; side_t *s; int contents; bface_t *f; vec3_t origin; // // if the origin key is set (by an origin brush), offset all of the values // GetVectorForKey (&entities[b->entitynum], "origin", origin); // // convert to mapplanes // for (i=0 ; i<b->numsides ; i++) { s = &brushsides[b->firstside + i]; for (j=0 ; j<3 ; j++) { VectorSubtract (s->planepts[j], origin, s->planepts[j]); } planenum = PlaneFromPoints (s->planepts[0], s->planepts[1], s->planepts[2]); if (planenum == -1) { printf ("Entity %i, Brush %i: plane with no normal\n" , b->entitynum, b->brushnum); continue; } // // see if the plane has been used already // for (f=b->hulls[0].faces ; f ; f=f->next) { if (f->planenum == planenum || f->planenum == (planenum^1) ) { char *pszClass = "Unknown Class"; if ( b->entitynum ) { entity_t *e = entities + b->entitynum; pszClass = ValueForKey(e, "classname" ); } printf( "Entity %i, Brush %i: A '%s' @(%.0f,%.0f,%.0f) has a coplanar plane at (%.0f, %.0f, %.0f), texture %s\n", b->entitynum, b->brushnum, pszClass, origin[0], origin[1], origin[2], s->planepts[0][0]+origin[0], s->planepts[0][1]+origin[1], s->planepts[0][2]+origin[2], s->td.name ); return false; } } f = malloc(sizeof(*f)); memset (f, 0, sizeof(*f)); f->planenum = planenum; f->plane = &mapplanes[planenum]; f->next = b->hulls[0].faces; b->hulls[0].faces = f; f->texinfo = onlyents ? 0 : TexinfoForBrushTexture (f->plane, &s->td, origin); } return true; }
// projects decal onto a polygon void ProjectDecalOntoWinding( decalProjector_t* dp, int numPoints, vec3_t points[ 2 ][ MAX_DECAL_VERTS ], const idWorldSurface* surf, mbrush46_model_t* bmodel ) { // make a plane from the winding vec4_t plane; if ( !PlaneFromPoints( plane, points[ 0 ][ 0 ], points[ 0 ][ 1 ], points[ 0 ][ 2 ] ) ) { return; } // omnidirectional projectors need plane type float pd; int axis; float alpha = 1.f; if ( dp->omnidirectional ) { pd = 1.0f; // fade by distance from plane float d = DotProduct( dp->center, plane ) - plane[ 3 ]; alpha = 1.0f - ( fabs( d ) / dp->radius ); if ( alpha < 0.0f ) { return; } if ( alpha > 1.0f ) { alpha = 1.0f; } // set projection axis vec3_t absNormal; absNormal[ 0 ] = fabs( plane[ 0 ] ); absNormal[ 1 ] = fabs( plane[ 1 ] ); absNormal[ 2 ] = fabs( plane[ 2 ] ); if ( absNormal[ 2 ] >= absNormal[ 0 ] && absNormal[ 2 ] >= absNormal[ 1 ] ) { axis = 2; } else if ( absNormal[ 0 ] >= absNormal[ 1 ] && absNormal[ 0 ] >= absNormal[ 2 ] ) { axis = 0; } else { axis = 1; } } else { // backface check pd = DotProduct( dp->planes[ 0 ], plane ); if ( pd < -0.0001f ) { return; } // directional decals use first texture matrix axis = 0; } // chop the winding by all the projector planes int pingPong = 0; for ( int i = 0; i < dp->numPlanes; i++ ) { ChopWindingBehindPlane( numPoints, points[ pingPong ], &numPoints, points[ !pingPong ], dp->planes[ i ], 0.0f ); pingPong ^= 1; if ( numPoints < 3 ) { return; } if ( numPoints == MAX_DECAL_VERTS ) { break; } } // find first free decal (fixme: optimize this) int count = ( bmodel == tr.world->bmodels ? MAX_WORLD_DECALS : MAX_ENTITY_DECALS ); mbrush46_decal_t* oldest = &bmodel->decals[ 0 ]; mbrush46_decal_t* decal = bmodel->decals; int i; for ( i = 0; i < count; i++, decal++ ) { // try to find an empty decal slot if ( decal->shader == NULL ) { break; } // find oldest decal if ( decal->fadeEndTime < oldest->fadeEndTime ) { oldest = decal; } } // guess we have to use the oldest decal if ( i >= count ) { decal = oldest; } // r_speeds useful info tr.pc.c_decalSurfacesCreated++; // set it up (fixme: get the shader before this happens) decal->parent = surf; decal->shader = dp->shader; decal->fadeStartTime = dp->fadeStartTime; decal->fadeEndTime = dp->fadeEndTime; decal->fogIndex = surf->fogIndex; // add points decal->numVerts = numPoints; polyVert_t* vert = decal->verts; for ( i = 0; i < numPoints; i++, vert++ ) { // set xyz VectorCopy( points[ pingPong ][ i ], vert->xyz ); // set st vert->st[ 0 ] = DotProduct( vert->xyz, dp->texMat[ axis ][ 0 ] ) + dp->texMat[ axis ][ 0 ][ 3 ]; vert->st[ 1 ] = DotProduct( vert->xyz, dp->texMat[ axis ][ 1 ] ) + dp->texMat[ axis ][ 1 ][ 3 ]; // unidirectional decals fade by half distance from front->back planes if ( !dp->omnidirectional ) { // set alpha float d = DotProduct( vert->xyz, dp->planes[ 0 ] ) - dp->planes[ 0 ][ 3 ]; float d2 = DotProduct( vert->xyz, dp->planes[ 1 ] ) - dp->planes[ 1 ][ 3 ]; alpha = 2.0f * d2 / ( d + d2 ); if ( alpha > 1.0f ) { alpha = 1.0f; } else if ( alpha < 0.0f ) { alpha = 0.0f; } } // set color vert->modulate[ 0 ] = idMath::FtoiFast( pd * alpha * dp->color[ 0 ] ); vert->modulate[ 1 ] = idMath::FtoiFast( pd * alpha * dp->color[ 1 ] ); vert->modulate[ 2 ] = idMath::FtoiFast( pd * alpha * dp->color[ 2 ] ); vert->modulate[ 3 ] = idMath::FtoiFast( alpha * dp->color[ 3 ] ); } }
/* * R_UpdatePortalSurface */ void R_UpdatePortalSurface( portalSurface_t *portalSurface, const mesh_t *mesh, const vec3_t mins, const vec3_t maxs, const shader_t *shader ) { unsigned int i; float dist; cplane_t plane, untransformed_plane; vec3_t v[3]; const entity_t *ent; if( !mesh || !portalSurface ) { return; } ent = portalSurface->entity; for( i = 0; i < 3; i++ ) { VectorCopy( mesh->xyzArray[mesh->elems[i]], v[i] ); } PlaneFromPoints( v, &untransformed_plane ); untransformed_plane.dist += DotProduct( ent->origin, untransformed_plane.normal ); untransformed_plane.dist += 1; // nudge along the normal a bit CategorizePlane( &untransformed_plane ); if( shader->flags & SHADER_AUTOSPRITE ) { vec3_t centre; // autosprites are quads, facing the viewer if( mesh->numVerts < 4 ) { return; } // compute centre as average of 4 vertices VectorCopy( mesh->xyzArray[mesh->elems[3]], centre ); for( i = 0; i < 3; i++ ) VectorAdd( centre, v[i], centre ); VectorMA( ent->origin, 0.25, centre, centre ); VectorNegate( &rn.viewAxis[AXIS_FORWARD], plane.normal ); plane.dist = DotProduct( plane.normal, centre ); CategorizePlane( &plane ); } else { vec3_t temp; mat3_t entity_rotation; // regular surfaces if( !Matrix3_Compare( ent->axis, axis_identity ) ) { Matrix3_Transpose( ent->axis, entity_rotation ); for( i = 0; i < 3; i++ ) { VectorCopy( v[i], temp ); Matrix3_TransformVector( entity_rotation, temp, v[i] ); VectorMA( ent->origin, ent->scale, v[i], v[i] ); } PlaneFromPoints( v, &plane ); CategorizePlane( &plane ); } else { plane = untransformed_plane; } } if( ( dist = PlaneDiff( rn.viewOrigin, &plane ) ) <= BACKFACE_EPSILON ) { // behind the portal plane if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { return; } // we need to render the backplane view } // check if portal view is opaque due to alphagen portal if( shader->portalDistance && dist > shader->portalDistance ) { return; } portalSurface->plane = plane; portalSurface->untransformed_plane = untransformed_plane; AddPointToBounds( mins, portalSurface->mins, portalSurface->maxs ); AddPointToBounds( maxs, portalSurface->mins, portalSurface->maxs ); VectorAdd( portalSurface->mins, portalSurface->maxs, portalSurface->centre ); VectorScale( portalSurface->centre, 0.5, portalSurface->centre ); }
void InsertModel(char *name, int frame, matrix_t transform, matrix_t nTransform, remap_t * remap, shaderInfo_t * celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle) { int i, j, k, s, numSurfaces; matrix_t identity; picoModel_t *model; picoShader_t *shader; picoSurface_t *surface; shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; char *picoShaderName; char shaderName[MAX_QPATH]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; remap_t *rm, *glob; double normalEpsilon_save; double distanceEpsilon_save; /* get model */ model = LoadModel(name, frame); if(model == NULL) return; /* handle null matrix */ if(transform == NULL) { MatrixIdentity(identity); transform = identity; } /* create transform matrix for normals */ #if 0 MatrixCopy(transform, nTransform); if(MatrixInverse(nTransform)) { Sys_FPrintf(SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n"); MatrixTranspose(transform, nTransform); } #endif /* fix bogus lightmap scale */ if(lightmapScale <= 0.0f) lightmapScale = 1.0f; /* fix bogus shade angle */ if(shadeAngle <= 0.0f) shadeAngle = 0.0f; /* each surface on the model will become a new map drawsurface */ numSurfaces = PicoGetModelNumSurfaces(model); //% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces ); for(s = 0; s < numSurfaces; s++) { /* get surface */ surface = PicoGetModelSurface(model, s); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* fix the surface's normals */ PicoFixSurfaceNormals(surface); /* allocate a surface (ydnar: gs mods) */ ds = AllocDrawSurface(SURFACE_TRIANGLES); ds->entityNum = eNum; ds->castShadows = castShadows; ds->recvShadows = recvShadows; /* get shader name */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) picoShaderName = ""; else picoShaderName = PicoGetShaderName(shader); /* handle shader remapping */ glob = NULL; for(rm = remap; rm != NULL; rm = rm->next) { if(rm->from[0] == '*' && rm->from[1] == '\0') glob = rm; else if(!Q_stricmp(picoShaderName, rm->from)) { Sys_FPrintf(SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to); picoShaderName = rm->to; glob = NULL; break; } } if(glob != NULL) { Sys_FPrintf(SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to); picoShaderName = glob->to; } /* shader renaming for sof2 */ if(renameModelShaders) { strcpy(shaderName, picoShaderName); StripExtension(shaderName); if(spawnFlags & 1) strcat(shaderName, "_RMG_BSP"); else strcat(shaderName, "_BSP"); si = ShaderInfoForShader(shaderName); } else { si = ShaderInfoForShader(picoShaderName); // Tr3B: HACK to support the messy Doom 3 materials provided by .ASE files if(!si->explicitDef) { picoShaderName = PicoGetShaderMapName(shader); Q_strncpyz(shaderName, picoShaderName, sizeof(shaderName)); StripExtension(shaderName); i = 0; while(shaderName[i]) { if(shaderName[i] == '\\') shaderName[i] = '/'; i++; } if(strstr(shaderName, "base/")) { si = ShaderInfoForShader(strstr(shaderName, "base/") + strlen("base/")); Sys_FPrintf(SYS_WRN, "WARNING: Applied .ASE material loader HACK to '%s' -> '%s'\n", picoShaderName, si->shader); } } } /* set shader */ ds->shaderInfo = si; /* force to meta? */ if((si != NULL && si->forceMeta) || (spawnFlags & 4)) /* 3rd bit */ ds->type = SURFACE_FORCED_META; /* fix the surface's normals (jal: conditioned by shader info) */ //if(!(spawnFlags & 64) && (shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META)) // PicoFixSurfaceNormals(surface); /* set sample size */ if(lightmapSampleSize > 0.0f) ds->sampleSize = lightmapSampleSize; /* set lightmap scale */ if(lightmapScale > 0.0f) ds->lightmapScale = lightmapScale; /* set shading angle */ if(shadeAngle > 0.0f) ds->shadeAngleDegrees = shadeAngle; /* set particulars */ ds->numVerts = PicoGetSurfaceNumVertexes(surface); ds->verts = safe_malloc(ds->numVerts * sizeof(ds->verts[0])); memset(ds->verts, 0, ds->numVerts * sizeof(ds->verts[0])); ds->numIndexes = PicoGetSurfaceNumIndexes(surface); ds->indexes = safe_malloc(ds->numIndexes * sizeof(ds->indexes[0])); memset(ds->indexes, 0, ds->numIndexes * sizeof(ds->indexes[0])); /* copy vertexes */ for(i = 0; i < ds->numVerts; i++) { /* get vertex */ dv = &ds->verts[i]; /* xyz and normal */ xyz = PicoGetSurfaceXYZ(surface, i); VectorCopy(xyz, dv->xyz); MatrixTransformPoint2(transform, dv->xyz); normal = PicoGetSurfaceNormal(surface, i); VectorCopy(normal, dv->normal); MatrixTransformNormal2(nTransform, dv->normal); VectorNormalize2(dv->normal, dv->normal); /* ydnar: tek-fu celshading support for flat shaded shit */ if(flat) { dv->st[0] = si->stFlat[0]; dv->st[1] = si->stFlat[1]; } /* ydnar: gs mods: added support for explicit shader texcoord generation */ else if(si->tcGen) { /* project the texture */ dv->st[0] = DotProduct(si->vecs[0], dv->xyz); dv->st[1] = DotProduct(si->vecs[1], dv->xyz); } /* normal texture coordinates */ else { st = PicoGetSurfaceST(surface, 0, i); dv->st[0] = st[0]; dv->st[1] = st[1]; } /* set lightmap/color bits */ color = PicoGetSurfaceColor(surface, 0, i); dv->paintColor[0] = color[0] / 255.0f; dv->paintColor[1] = color[1] / 255.0f; dv->paintColor[2] = color[2] / 255.0f; dv->paintColor[3] = color[3] / 255.0f; for(j = 0; j < MAX_LIGHTMAPS; j++) { dv->lightmap[j][0] = 0.0f; dv->lightmap[j][1] = 0.0f; dv->lightColor[j][0] = 255; dv->lightColor[j][1] = 255; dv->lightColor[j][2] = 255; dv->lightColor[j][3] = 255; } } /* copy indexes */ indexes = PicoGetSurfaceIndexes(surface, 0); for(i = 0; i < ds->numIndexes; i++) ds->indexes[i] = indexes[i]; /* set cel shader */ ds->celShader = celShader; /* ydnar: giant hack land: generate clipping brushes for model triangles */ if(si->clipModel || (spawnFlags & 2)) /* 2nd bit */ { vec3_t points[4], backs[3]; vec4_t plane, reverse, pa, pb, pc; /* temp hack */ if(!si->clipModel && (((si->compileFlags & C_TRANSLUCENT) && !(si->compileFlags & C_COLLISION)) || !(si->compileFlags & C_SOLID))) continue; /* walk triangle list */ for(i = 0; i < ds->numIndexes; i += 3) { /* overflow hack */ AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes + 64) << 1, allocatedmapplanes, 1024); /* make points and back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; /* copy xyz */ VectorCopy(dv->xyz, points[j]); VectorCopy(dv->xyz, backs[j]); /* find nearest axial to normal and push back points opposite */ /* note: this doesn't work as well as simply using the plane of the triangle, below */ for(k = 0; k < 3; k++) { if(fabs(dv->normal[k]) >= fabs(dv->normal[(k + 1) % 3]) && fabs(dv->normal[k]) >= fabs(dv->normal[(k + 2) % 3])) { backs[j][k] += dv->normal[k] < 0.0f ? 64.0f : -64.0f; break; } } } VectorCopy(points[0], points[3]); // for cyclic usage /* make plane for triangle */ // div0: add some extra spawnflags: // 0: snap normals to axial planes for extrusion // 8: extrude with the original normals // 16: extrude only with up/down normals (ideal for terrain) // 24: extrude by distance zero (may need engine changes) if(PlaneFromPoints(plane, points[0], points[1], points[2], qtrue)) { vec3_t bestNormal; float backPlaneDistance = 2; if(spawnFlags & 8) // use a DOWN normal { if(spawnFlags & 16) { // 24: normal as is, and zero width (broken) VectorCopy(plane, bestNormal); } else { // 8: normal as is VectorCopy(plane, bestNormal); } } else { if(spawnFlags & 16) { // 16: UP/DOWN normal VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } else { // 0: axial normal if(fabs(plane[0]) > fabs(plane[1])) // x>y if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // x>y, z>=y if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // z>=x, x>y VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); else // y>=x if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0); else // z>=y, y>=x VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } } /* build a brush */ buildBrush = AllocBrush(48); buildBrush->entityNum = mapEntityNum; buildBrush->original = buildBrush; buildBrush->contentShader = si; buildBrush->compileFlags = si->compileFlags; buildBrush->contentFlags = si->contentFlags; buildBrush->generatedClipBrush = qtrue; normalEpsilon_save = normalEpsilon; distanceEpsilon_save = distanceEpsilon; if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here { buildBrush->detail = qfalse; // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model) if(normalEpsilon > 0) normalEpsilon = 0; if(distanceEpsilon > 0) distanceEpsilon = 0; } else buildBrush->detail = qtrue; /* regenerate back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; // shift by some units VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit } /* make back plane */ VectorScale(plane, -1.0f, reverse); reverse[3] = -plane[3]; if((spawnFlags & 24) != 24) reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance; // that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane) if(PlaneFromPoints(pa, points[2], points[1], backs[1], qtrue) && PlaneFromPoints(pb, points[1], points[0], backs[0], qtrue) && PlaneFromPoints(pc, points[0], points[2], backs[2], qtrue)) { /* set up brush sides */ buildBrush->numsides = 5; buildBrush->sides[0].shaderInfo = si; for(j = 1; j < buildBrush->numsides; j++) buildBrush->sides[j].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works buildBrush->sides[0].planenum = FindFloatPlane(plane, plane[3], 3, points); buildBrush->sides[1].planenum = FindFloatPlane(pa, pa[3], 2, &points[1]); // pa contains points[1] and points[2] buildBrush->sides[2].planenum = FindFloatPlane(pb, pb[3], 2, &points[0]); // pb contains points[0] and points[1] buildBrush->sides[3].planenum = FindFloatPlane(pc, pc[3], 2, &points[2]); // pc contains points[2] and points[0] (copied to points[3] buildBrush->sides[4].planenum = FindFloatPlane(reverse, reverse[3], 3, backs); } else { free(buildBrush); continue; } normalEpsilon = normalEpsilon_save; distanceEpsilon = distanceEpsilon_save; /* add to entity */ if(CreateBrushWindings(buildBrush)) { AddBrushBevels(); //% EmitBrushes( buildBrush, NULL, NULL ); buildBrush->next = entities[mapEntityNum].brushes; entities[mapEntityNum].brushes = buildBrush; entities[mapEntityNum].numBrushes++; } else free(buildBrush); } } } } }
void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw ) { int i, x, y, v, t, pw[ 5 ], r; bspDrawSurface_t *ds; surfaceInfo_t *info; bspDrawVert_t *bogus; bspDrawVert_t *dv[ 4 ]; mesh_t src, *subdivided, *mesh; float *radVertexLuxel; float dist; vec4_t plane; qboolean planar; radWinding_t rw; /* get surface */ ds = &bspDrawSurfaces[ num ]; info = &surfaceInfos[ num ]; /* construct a bogus vert list with color index stuffed into color[ 0 ] */ bogus = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) ); memcpy( bogus, &yDrawVerts[ ds->firstVert ], ds->numVerts * sizeof( bspDrawVert_t ) ); for( i = 0; i < ds->numVerts; i++ ) bogus[ i ].color[ 0 ][ 0 ] = i; /* build a subdivided mesh identical to shadow facets for this patch */ /* this MUST MATCH FacetsForPatch() identically! */ src.width = ds->patchWidth; src.height = ds->patchHeight; src.verts = bogus; //% subdivided = SubdivideMesh( src, 8, 512 ); subdivided = SubdivideMesh2( src, info->patchIterations ); PutMeshOnCurve( *subdivided ); //% MakeMeshNormals( *subdivided ); mesh = RemoveLinearMeshColumnsRows( subdivided ); FreeMesh( subdivided ); free( bogus ); /* FIXME: build interpolation table into color[ 1 ] */ /* fix up color indexes */ for( i = 0; i < (mesh->width * mesh->height); i++ ) { dv[ 0 ] = &mesh->verts[ i ]; if( dv[ 0 ]->color[ 0 ][ 0 ] >= ds->numVerts ) dv[ 0 ]->color[ 0 ][ 0 ] = ds->numVerts - 1; } /* 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? */ planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ); if( planar ) { dist = DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ]; if( fabs( dist ) > PLANAR_EPSILON ) planar = qfalse; } /* generate a quad */ if( planar ) { rw.numVerts = 4; for( v = 0; v < 4; v++ ) { /* get most everything */ memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) ); /* fix colors */ for( i = 0; i < MAX_LIGHTMAPS; i++ ) { radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] ); VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] ); rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ]; } } /* subdivide into area lights */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw ); } /* generate 2 tris */ else { rw.numVerts = 3; for( t = 0; t < 2; t++ ) { for( v = 0; v < 3 + t; v++ ) { /* get "other" triangle (stupid hacky logic, but whatevah) */ if( v == 1 && t == 1 ) v++; /* get most everything */ memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) ); /* fix colors */ for( i = 0; i < MAX_LIGHTMAPS; i++ ) { radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] ); VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] ); rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ]; } } /* subdivide into area lights */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw ); } } } } /* free the mesh */ FreeMesh( mesh ); }
/** * @brief Parses a brush from the map file * @sa FindMiptex * @param[in] mapent The entity the brush to parse belongs to * @param[in] filename The map filename, used to derive the name for the footsteps file */ static void ParseBrush (entity_t* mapent, const char* filename) { int j, k; brush_texture_t td; vec3_t planepts[3]; const int checkOrFix = config.performMapCheck || config.fixMap ; if (nummapbrushes == MAX_MAP_BRUSHES) Sys_Error("nummapbrushes == MAX_MAP_BRUSHES (%i)", nummapbrushes); mapbrush_t* b = &mapbrushes[nummapbrushes]; OBJZERO(*b); b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities - 1; b->brushnum = nummapbrushes - mapent->firstbrush; do { if (Q_strnull(GetToken())) break; if (*parsedToken == '}') break; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Sys_Error("nummapbrushsides == MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides); side_t* side = &brushsides[nummapbrushsides]; /* read the three point plane definition */ for (int i = 0; i < 3; i++) { if (i != 0) GetToken(); if (*parsedToken != '(') Sys_Error("parsing brush"); for (j = 0; j < 3; j++) { GetToken(); planepts[i][j] = atof(parsedToken); } GetToken(); if (*parsedToken != ')') Sys_Error("parsing brush"); } /* read the texturedef */ GetToken(); if (strlen(parsedToken) >= MAX_TEXPATH) { if (config.performMapCheck || config.fixMap) Com_Printf(" ");/* hack to make this look like output from Check_Printf() */ Com_Printf("ParseBrush: texture name too long (limit %i): %s\n", MAX_TEXPATH, parsedToken); if (config.fixMap) Sys_Error("Aborting, as -fix is active and saving might corrupt *.map by truncating texture name"); } Q_strncpyz(td.name, parsedToken, sizeof(td.name)); td.shift[0] = atof(GetToken()); td.shift[1] = atof(GetToken()); td.rotate = atof(GetToken()); td.scale[0] = atof(GetToken()); td.scale[1] = atof(GetToken()); /* find default flags and values */ const int mt = FindMiptex(td.name); side->surfaceFlags = td.surfaceFlags = side->contentFlags = td.value = 0; if (TokenAvailable()) { side->contentFlags = atoi(GetToken()); side->surfaceFlags = td.surfaceFlags = atoi(GetToken()); td.value = atoi(GetToken()); } /* if in check or fix mode, let them choose to do this (with command line options), * and then call is made elsewhere */ if (!checkOrFix) { SetImpliedFlags(side, &td, b); /* if no other content flags are set - make this solid */ if (!checkOrFix && side->contentFlags == 0) side->contentFlags = CONTENTS_SOLID; } /* translucent objects are automatically classified as detail and window */ if (side->surfaceFlags & (SURF_BLEND33 | SURF_BLEND66 | SURF_ALPHATEST)) { side->contentFlags |= CONTENTS_DETAIL; side->contentFlags |= CONTENTS_TRANSLUCENT; side->contentFlags |= CONTENTS_WINDOW; side->contentFlags &= ~CONTENTS_SOLID; } if (config.fulldetail) side->contentFlags &= ~CONTENTS_DETAIL; if (!checkOrFix) { if (!(side->contentFlags & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_ACTORCLIP | CONTENTS_WEAPONCLIP | CONTENTS_LIGHTCLIP))) side->contentFlags |= CONTENTS_SOLID; /* hints and skips are never detail, and have no content */ if (side->surfaceFlags & (SURF_HINT | SURF_SKIP)) { side->contentFlags = 0; side->surfaceFlags &= ~CONTENTS_DETAIL; } } /* check whether the flags are ok */ CheckFlags(side, b); /* generate a list of textures that should have footsteps when walking on them */ if (mt > 0 && (side->surfaceFlags & SURF_FOOTSTEP)) GenerateFootstepList(filename, mt); GenerateMaterialFile(filename, mt, side); /* find the plane number */ int planenum = PlaneFromPoints(b, planepts[0], planepts[1], planepts[2]); if (planenum == PLANENUM_LEAF) { Com_Printf("Entity %i, Brush %i: plane with no normal\n", b->entitynum, b->brushnum); continue; } for (j = 0; j < 3; j++) VectorCopy(planepts[j], mapplanes[planenum].planeVector[j]); /* see if the plane has been used already */ for (k = 0; k < b->numsides; k++) { const side_t* s2 = b->original_sides + k; if (s2->planenum == planenum) { Com_Printf("Entity %i, Brush %i: duplicate plane\n", b->entitynum, b->brushnum); break; } if (s2->planenum == (planenum ^ 1)) { Com_Printf("Entity %i, Brush %i: mirrored plane\n", b->entitynum, b->brushnum); break; } } if (k != b->numsides) continue; /* duplicated */ /* keep this side */ side = b->original_sides + b->numsides; side->planenum = planenum; side->texinfo = TexinfoForBrushTexture(&mapplanes[planenum], &td, vec3_origin, side->contentFlags & CONTENTS_TERRAIN); side->brush = b; /* save the td off in case there is an origin brush and we * have to recalculate the texinfo */ side_brushtextures[nummapbrushsides] = td; Verb_Printf(VERB_DUMP, "Brush %i Side %i (%f %f %f) (%f %f %f) (%f %f %f) texinfo:%i[%s] plane:%i\n", nummapbrushes, nummapbrushsides, planepts[0][0], planepts[0][1], planepts[0][2], planepts[1][0], planepts[1][1], planepts[1][2], planepts[2][0], planepts[2][1], planepts[2][2], side->texinfo, td.name, planenum); nummapbrushsides++; b->numsides++; } while (1); /* get the content for the entire brush */ b->contentFlags = BrushContents(b); /* copy all set face contentflags to the brush contentflags */ for (int m = 0; m < b->numsides; m++) b->contentFlags |= b->original_sides[m].contentFlags; /* set DETAIL, TRANSLUCENT contentflags on all faces, if they have been set on any. * called separately, if in check/fix mode */ if (!checkOrFix) CheckPropagateParserContentFlags(b); /* allow detail brushes to be removed */ if (config.nodetail && (b->contentFlags & CONTENTS_DETAIL)) { b->numsides = 0; return; } /* allow water brushes to be removed */ if (config.nowater && (b->contentFlags & CONTENTS_WATER)) { b->numsides = 0; return; } /* create windings for sides and bounds for brush */ MakeBrushWindings(b); Verb_Printf(VERB_DUMP, "Brush %i mins (%f %f %f) maxs (%f %f %f)\n", nummapbrushes, b->mbBox.mins[0], b->mbBox.mins[1], b->mbBox.mins[2], b->mbBox.maxs[0], b->mbBox.maxs[1], b->mbBox.maxs[2]); /* origin brushes are removed, but they set * the rotation origin for the rest of the brushes (like func_door) * in the entity. After the entire entity is parsed, the planenums * and texinfos will be adjusted for the origin brush */ if (!checkOrFix && (b->contentFlags & CONTENTS_ORIGIN)) { char string[32]; vec3_t origin; if (num_entities == 1) { Sys_Error("Entity %i, Brush %i: origin brushes not allowed in world" , b->entitynum, b->brushnum); return; } b->mbBox.getCenter(origin); Com_sprintf(string, sizeof(string), "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue(&entities[b->entitynum], "origin", string); Verb_Printf(VERB_EXTRA, "Entity %i, Brush %i: set origin to %s\n", b->entitynum, b->brushnum, string); VectorCopy(origin, entities[b->entitynum].origin); /* don't keep this brush */ b->numsides = 0; return; } if (!checkOrFix) AddBrushBevels(b); nummapbrushes++; mapent->numbrushes++; }
/* ================= ParseBrush ================= */ void ParseBrush (entity_t *mapent) { mapbrush_t *b; int i,j, k; int mt; side_t *side, *s2; int planenum; brush_texture_t td; int planepts[3][3]; if (nummapbrushes == MAX_MAP_BRUSHES) Error ("nummapbrushes == MAX_MAP_BRUSHES"); b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities-1; b->brushnum = nummapbrushes - mapent->firstbrush; do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Error ("MAX_MAP_BRUSHSIDES"); side = &brushsides[nummapbrushsides]; // read the three point plane definition for (i=0 ; i<3 ; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) Error ("parsing brush"); for (j=0 ; j<3 ; j++) { GetToken (false); planepts[i][j] = atoi(token); } GetToken (false); if (strcmp (token, ")") ) Error ("parsing brush"); } // // read the texturedef // GetToken (false); strcpy (td.name, token); GetToken (false); td.shift[0] = atoi(token); GetToken (false); td.shift[1] = atoi(token); GetToken (false); td.rotate = atoi(token); GetToken (false); td.scale[0] = atof(token); GetToken (false); td.scale[1] = atof(token); // find default flags and values mt = FindMiptex (td.name); td.flags = textureref[mt].flags; td.value = textureref[mt].value; side->contents = textureref[mt].contents; side->surf = td.flags = textureref[mt].flags; if (TokenAvailable()) { GetToken (false); side->contents = atoi(token); GetToken (false); side->surf = td.flags = atoi(token); GetToken (false); td.value = atoi(token); } // translucent objects are automatically classified as detail if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) side->contents |= CONTENTS_SOLID; // hints and skips are never detail, and have no content if (side->surf & (SURF_HINT|SURF_SKIP) ) { side->contents = 0; side->surf &= ~CONTENTS_DETAIL; } // // find the plane number // planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); if (planenum == -1) { printf ("Entity %i, Brush %i: plane with no normal\n" , b->entitynum, b->brushnum); continue; } // // see if the plane has been used already // for (k=0 ; k<b->numsides ; k++) { s2 = b->original_sides + k; if (s2->planenum == planenum) { printf ("Entity %i, Brush %i: duplicate plane\n" , b->entitynum, b->brushnum); break; } if ( s2->planenum == (planenum^1) ) { printf ("Entity %i, Brush %i: mirrored plane\n" , b->entitynum, b->brushnum); break; } } if (k != b->numsides) continue; // duplicated // // keep this side // side = b->original_sides + b->numsides; side->planenum = planenum; side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &td, vec3_origin); // save the td off in case there is an origin brush and we // have to recalculate the texinfo side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } while (1); // get the content for the entire brush b->contents = BrushContents (b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL) ) { b->numsides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) { b->numsides = 0; return; } // create windings for sides and bounds for brush MakeBrushWindings (b); // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) { c_clipbrushes++; for (i=0 ; i<b->numsides ; i++) b->original_sides[i].texinfo = TEXINFO_NODE; } // // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity. After the entire entity is parsed, // the planenums and texinfos will be adjusted for // the origin brush // if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Error ("Entity %i, Brush %i: origin brushes not allowed in world" , b->entitynum, b->brushnum); return; } VectorAdd (b->mins, b->maxs, origin); VectorScale (origin, 0.5, origin); sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue (&entities[b->entitynum], "origin", string); VectorCopy (origin, entities[b->entitynum].origin); // don't keep this brush b->numsides = 0; return; } AddBrushBevels (b); nummapbrushes++; mapent->numbrushes++; }
/* ================= ParseBrush ================= */ void ParseBrush (entity_t *mapent) { char *tl; mapbrush_t *b; int i,j, k; int mt; side_t *side, *s2; int planenum; brush_texture_t td; float planepts[3][3]; qboolean phongShading; MarkBrushBegin(); if (nummapbrushes == MAX_MAP_BRUSHES) Error ("nummapbrushes == MAX_MAP_BRUSHES"); b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities-1; b->brushnum = nummapbrushes - mapent->firstbrush; // Knightmare added b->optimizedDetail = false; b->isTerrain = (!strcmp("func_group", ValueForKey (&entities[b->entitynum], "classname")) && strlen(ValueForKey (&entities[b->entitynum], "terrain")) > 0); b->isGenSurf = (!strcmp("func_group", ValueForKey (&entities[b->entitynum], "classname")) && strlen(ValueForKey (&entities[b->entitynum], "gensurf")) > 0); phongShading = (!strcmp("func_group", ValueForKey (&entities[b->entitynum], "classname")) && strlen(ValueForKey (&entities[b->entitynum], "phongshading")) > 0); //if (b->isTerrain) // printf ("Brush number %i in enitity number %i has terrain flag set.\n", b->brushnum, b->entitynum); //if (b->phongShading) // printf ("Brush number %i in enitity number %i has phong shading flag set.\n", b->brushnum, b->entitynum); if (verbosebrushes) printf ("enitity %i, brush %i\n", b->entitynum, b->brushnum); // end Knightmare do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Error ("MAX_MAP_BRUSHSIDES"); side = &brushsides[nummapbrushsides]; // read the three point plane definition for (i=0 ; i<3 ; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) Error ("parsing brush\n Brush: %s", i+1, brush_info); for (j=0 ; j<3 ; j++) { GetToken (false); planepts[i][j] = atof(token); } GetToken (false); if (strcmp (token, ")") ) Error ("parsing brush\n Brush: %s", i+1, brush_info); } // // read the texturedef // GetToken (false); strcpy (td.name, token); tl = td.name; for(tl = td.name; *tl != 0; tl++) *tl = tolower(*tl); GetToken (false); td.shift[0] = atoi(token); GetToken (false); td.shift[1] = atoi(token); GetToken (false); td.rotate = atoi(token); GetToken (false); td.scale[0] = atof(token); GetToken (false); td.scale[1] = atof(token); // find default flags and values mt = FindMiptex (td.name); td.flags = textureref[mt].flags; td.value = textureref[mt].value; side->contents = textureref[mt].contents; side->surf = td.flags = textureref[mt].flags; if (TokenAvailable()) { GetToken (false); side->contents = atoi(token); GetToken (false); side->surf = td.flags = atoi(token); GetToken (false); td.value = atoi(token); } // translucent objects are automatically classified as detail if (side->surf & (SURF_TRANS33|SURF_TRANS66|SURF_ALPHATEST) ) // Knightmare- added alphatest side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) side->contents |= CONTENTS_SOLID; // hints and skips are never detail, and have no content if (side->surf & (SURF_HINT|SURF_SKIP) ) { //side->contents = 0; //side->surf &= ~CONTENTS_DETAIL; side->contents &= CONTENTS_DETAIL; // allow only detail content- Geoffery DeWan? } // // find the plane number // planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); if (planenum == -1) { printf ("Entity %i, Brush %i: plane with no normal\n Brush: %s\n" , b->entitynum, b->brushnum, brush_info); continue; } // // see if the plane has been used already // for (k=0 ; k<b->numsides ; k++) { s2 = b->original_sides + k; if (s2->planenum == planenum) { printf ("Entity %i, Brush %i: duplicate plane\n Brush: %s\n" , b->entitynum, b->brushnum, brush_info); break; } if ( s2->planenum == (planenum^1) ) { printf ("Entity %i, Brush %i: mirrored plane\n Brush: %s\n" , b->entitynum, b->brushnum, brush_info); break; } } if (k != b->numsides) continue; // duplicated // // keep this side // side = b->original_sides + b->numsides; side->planenum = planenum; side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &td, vec3_origin, b->isTerrain); // Knightmare added // save the td off in case there is an origin brush and we // have to recalculate the texinfo side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } while (1); // get the content for the entire brush b->contents = BrushContents (b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL) ) { b->numsides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) { b->numsides = 0; return; } // Knightmare- check if this is an optimized detail brush (has caulk faces) // also exclude trans brushes and terrain if ((b->contents & CONTENTS_DETAIL) && (b->contents & CONTENTS_SOLID) && !b->isTerrain && !b->isGenSurf) for (i=0; i<b->numsides; i++) { if ( !strcmp(texinfo[b->original_sides[i].texinfo].texture, "system/caulk") || !strcmp(texinfo[b->original_sides[i].texinfo].texture, "common/caulk") ) { b->optimizedDetail = true; //printf ("Entity %i, Brush %i: optimized detail\n", b->entitynum, b->brushnum); break; } } // Knightmare- special handling for terrain brushes if (b->isTerrain || b->isGenSurf || phongShading) for (i=0; i<b->numsides; i++) { s2 = &b->original_sides[i]; // set ArghRad phong shading value (because EasyGen/GTKGenSurf doesn't allow this) if (strcmp(texinfo[b->original_sides[i].texinfo].texture, "system/caulk") && strcmp(texinfo[b->original_sides[i].texinfo].texture, "common/caulk")) { texinfo[s2->texinfo].value = 777 + b->entitynum; // lucky 7's texinfo[s2->texinfo].flags &= ~SURF_LIGHT; // must not be light-emitting } } // create windings for sides and bounds for brush // MEM_LEAK MakeBrushWindings (b); t_w = b->original_sides[0].winding; // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) { c_clipbrushes++; for (i=0 ; i<b->numsides ; i++) b->original_sides[i].texinfo = TEXINFO_NODE; } // // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity. After the entire entity is parsed, // the planenums and texinfos will be adjusted for // the origin brush // if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Error ("Entity %i, Brush %i: origin brushes not allowed in world\n BrushL %s", b->entitynum, b->brushnum, brush_info); return; } VectorAdd (b->mins, b->maxs, origin); VectorScale (origin, 0.5, origin); sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue (&entities[b->entitynum], "origin", string); VectorCopy (origin, entities[b->entitynum].origin); // don't keep this brush b->numsides = 0; return; } AddBrushBevels (b); nummapbrushes++; mapent->numbrushes++; }
/** * @brief Creates a new decal projector from a triangle. * * Projected polygons should be 3 or 4 points. * * If a single point is passed in (numPoints == 1) then the decal will be omnidirectional * omnidirectional decals use points[ 0 ] as center and projection[ 3 ] as radius * pass in lifeTime < 0 for a temporary mark. * * @param[in] hShader * @param[in] numPoints * @param[in] points * @param[in] projection * @param[in] color * @param[in] lifeTime * @param[in] fadeTime */ void RE_ProjectDecal(qhandle_t hShader, int numPoints, vec3_t *points, vec4_t projection, vec4_t color, int lifeTime, int fadeTime) { static int totalProjectors = 0; vec3_t xyz; decalVert_t dv[4]; int i; decalProjector_t *dp, temp; if (r_numDecalProjectors >= MAX_DECAL_PROJECTORS) { Ren_Print("WARNING: RE_ProjectDecal() Max decal projectors reached (%d)\n", MAX_DECAL_PROJECTORS); return; } // dummy check if (numPoints != 1 && numPoints != 3 && numPoints != 4) { Ren_Print("WARNING: RE_ProjectDecal() Invalid number of decal points (%d)\n", numPoints); return; } // early outs if (lifeTime == 0) { Ren_Developer("WARNING: RE_ProjectDecal() lifeTime == 0\n"); // modders should have a look at this - vanilla does these calls return; } if (projection[3] <= 0.0f) { Ren_Print("WARNING: RE_ProjectDecal() projection[3] <= 0.0f\n"); return; } // set times properly if (lifeTime < 0 || fadeTime < 0) { lifeTime = 0; fadeTime = 0; } // basic setup temp.shader = R_GetShaderByHandle(hShader); temp.color[0] = (byte)(color[0] * 255); temp.color[1] = (byte)(color[1] * 255); temp.color[2] = (byte)(color[2] * 255); temp.color[3] = (byte)(color[3] * 255); temp.numPlanes = numPoints + 2; temp.fadeStartTime = tr.refdef.time + lifeTime - fadeTime; // FIXME: stale refdef time temp.fadeEndTime = temp.fadeStartTime + fadeTime; temp.projectorNum = 0; // set up decal texcoords (FIXME: support arbitrary projector st coordinates in trapcall) dv[0].st[0] = 0.0f; dv[0].st[1] = 0.0f; dv[1].st[0] = 0.0f; dv[1].st[1] = 1.0f; dv[2].st[0] = 1.0f; dv[2].st[1] = 1.0f; dv[3].st[0] = 1.0f; dv[3].st[1] = 0.0f; // omnidirectional? if (numPoints == 1) { float radius; float iDist; // set up omnidirectional numPoints = 4; temp.numPlanes = 6; temp.omnidirectional = qtrue; radius = projection[3]; Vector4Set(projection, 0.0f, 0.0f, -1.0f, radius * 2.0f); iDist = 1.0f / (radius * 2.0f); // set corner VectorSet(xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius); // make x axis texture matrix (yz) VectorSet(temp.texMat[0][0], 0.0f, iDist, 0.0f); temp.texMat[0][0][3] = -DotProduct(temp.texMat[0][0], xyz); VectorSet(temp.texMat[0][1], 0.0f, 0.0f, iDist); temp.texMat[0][1][3] = -DotProduct(temp.texMat[0][1], xyz); // make y axis texture matrix (xz) VectorSet(temp.texMat[1][0], iDist, 0.0f, 0.0f); temp.texMat[1][0][3] = -DotProduct(temp.texMat[1][0], xyz); VectorSet(temp.texMat[1][1], 0.0f, 0.0f, iDist); temp.texMat[1][1][3] = -DotProduct(temp.texMat[1][1], xyz); // make z axis texture matrix (xy) VectorSet(temp.texMat[2][0], iDist, 0.0f, 0.0f); temp.texMat[2][0][3] = -DotProduct(temp.texMat[2][0], xyz); VectorSet(temp.texMat[2][1], 0.0f, iDist, 0.0f); temp.texMat[2][1][3] = -DotProduct(temp.texMat[2][1], xyz); // setup decal points VectorSet(dv[0].xyz, points[0][0] - radius, points[0][1] - radius, points[0][2] + radius); VectorSet(dv[1].xyz, points[0][0] - radius, points[0][1] + radius, points[0][2] + radius); VectorSet(dv[2].xyz, points[0][0] + radius, points[0][1] + radius, points[0][2] + radius); VectorSet(dv[3].xyz, points[0][0] + radius, points[0][1] - radius, points[0][2] + radius); } else { // set up unidirectional temp.omnidirectional = qfalse; // set up decal points VectorCopy(points[0], dv[0].xyz); VectorCopy(points[1], dv[1].xyz); VectorCopy(points[2], dv[2].xyz); VectorCopy(points[3], dv[3].xyz); // make texture matrix if (!MakeTextureMatrix(temp.texMat[0], projection, &dv[0], &dv[1], &dv[2])) { Ren_Print("WARNING: RE_ProjectDecal() MakeTextureMatrix returns NULL\n"); return; } } // bound the projector ClearBounds(temp.mins, temp.maxs); for (i = 0; i < numPoints; i++) { AddPointToBounds(dv[i].xyz, temp.mins, temp.maxs); VectorMA(dv[i].xyz, projection[3], projection, xyz); AddPointToBounds(xyz, temp.mins, temp.maxs); } // make bounding sphere VectorAdd(temp.mins, temp.maxs, temp.center); VectorScale(temp.center, 0.5f, temp.center); VectorSubtract(temp.maxs, temp.center, xyz); temp.radius = VectorLength(xyz); temp.radius2 = temp.radius * temp.radius; // make the front plane if (!PlaneFromPoints(temp.planes[0], dv[0].xyz, dv[1].xyz, dv[2].xyz)) { Ren_Developer("WARNING: RE_ProjectDecal() PlaneFromPoints is NULL\n"); // occurs on UJE_fueldump return; } // make the back plane VectorSubtract(vec3_origin, temp.planes[0], temp.planes[1]); VectorMA(dv[0].xyz, projection[3], projection, xyz); temp.planes[1][3] = DotProduct(xyz, temp.planes[1]); // make the side planes for (i = 0; i < numPoints; i++) { VectorMA(dv[i].xyz, projection[3], projection, xyz); if (!PlaneFromPoints(temp.planes[i + 2], dv[(i + 1) % numPoints].xyz, dv[i].xyz, xyz)) { Ren_Developer("WARNING: RE_ProjectDecal() a side plane is NULL\n"); // occurs on map venice return; } } // create a new projector dp = &backEndData->decalProjectors[r_numDecalProjectors]; Com_Memcpy(dp, &temp, sizeof(*dp)); dp->projectorNum = totalProjectors++; // we have a winner r_numDecalProjectors++; }
/* ================= Q2_ParseBrush ================= */ void Q2_ParseBrush(script_t *script, entity_t *mapent) { mapbrush_t *b; int i, j, k; int mt; side_t *side, *s2; int planenum; brush_texture_t td; int planepts[3][3]; token_t token; if (nummapbrushes >= MAX_MAPFILE_BRUSHES) { Error("nummapbrushes == MAX_MAPFILE_BRUSHES"); } b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities - 1; b->brushnum = nummapbrushes - mapent->firstbrush; b->leafnum = -1; do { if (!PS_ReadToken(script, &token)) { break; } if (!strcmp(token.string, "}")) { break; } //IDBUG: mixed use of MAX_MAPFILE_? and MAX_MAP_? this could // lead to out of bound indexing of the arrays if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) { Error("MAX_MAPFILE_BRUSHSIDES"); } side = &brushsides[nummapbrushsides]; //read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) { PS_ExpectTokenString(script, "("); } for (j = 0; j < 3; j++) { PS_ExpectAnyToken(script, &token); planepts[i][j] = atof(token.string); } //end for PS_ExpectTokenString(script, ")"); } //end for // //read the texturedef // PS_ExpectAnyToken(script, &token); strcpy(td.name, token.string); PS_ExpectAnyToken(script, &token); td.shift[0] = atol(token.string); PS_ExpectAnyToken(script, &token); td.shift[1] = atol(token.string); PS_ExpectAnyToken(script, &token); td.rotate = atol(token.string); PS_ExpectAnyToken(script, &token); td.scale[0] = atof(token.string); PS_ExpectAnyToken(script, &token); td.scale[1] = atof(token.string); //find default flags and values mt = FindMiptex(td.name); td.flags = textureref[mt].flags; td.value = textureref[mt].value; side->contents = textureref[mt].contents; side->surf = td.flags = textureref[mt].flags; //check if there's a number available if (PS_CheckTokenType(script, TT_NUMBER, 0, &token)) { side->contents = token.intvalue; PS_ExpectTokenType(script, TT_NUMBER, 0, &token); side->surf = td.flags = token.intvalue; PS_ExpectTokenType(script, TT_NUMBER, 0, &token); td.value = token.intvalue; } // translucent objects are automatically classified as detail // if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) // side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP)) { side->contents |= CONTENTS_DETAIL; } if (fulldetail) { side->contents &= ~CONTENTS_DETAIL; } if (!(side->contents & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST))) { side->contents |= CONTENTS_SOLID; } // hints and skips are never detail, and have no content // if (side->surf & (SURF_HINT|SURF_SKIP) ) // { // side->contents = 0; // side->surf &= ~CONTENTS_DETAIL; // } #ifdef ME //for creating AAS... this side is textured side->flags |= SFL_TEXTURED; #endif //ME // // find the plane number // planenum = PlaneFromPoints(planepts[0], planepts[1], planepts[2]); if (planenum == -1) { Log_Print("Entity %i, Brush %i: plane with no normal\n" , b->entitynum, b->brushnum); continue; } // // see if the plane has been used already // for (k = 0 ; k < b->numsides ; k++) { s2 = b->original_sides + k; if (s2->planenum == planenum) { Log_Print("Entity %i, Brush %i: duplicate plane\n" , b->entitynum, b->brushnum); break; } if (s2->planenum == (planenum ^ 1)) { Log_Print("Entity %i, Brush %i: mirrored plane\n" , b->entitynum, b->brushnum); break; } } if (k != b->numsides) { continue; // duplicated } // // keep this side // side = b->original_sides + b->numsides; side->planenum = planenum; side->texinfo = TexinfoForBrushTexture(&mapplanes[planenum], &td, vec3_origin); // save the td off in case there is an origin brush and we // have to recalculate the texinfo side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } while (1); // get the content for the entire brush b->contents = Q2_BrushContents(b); #ifdef ME if (BrushExists(b)) { c_squattbrushes++; b->numsides = 0; return; } //end if if (create_aas) { //create AAS brushes, and add brush bevels AAS_CreateMapBrushes(b, mapent, true); //NOTE: if we return here then duplicate plane errors occur for the non world entities return; } //end if #endif //ME // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL)) { b->numsides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER))) { b->numsides = 0; return; } // create windings for sides and bounds for brush MakeBrushWindings(b); // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP)) { c_clipbrushes++; for (i = 0 ; i < b->numsides ; i++) b->original_sides[i].texinfo = TEXINFO_NODE; } // // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity. After the entire entity is parsed, // the planenums and texinfos will be adjusted for // the origin brush // if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Error("Entity %i, Brush %i: origin brushes not allowed in world" , b->entitynum, b->brushnum); return; } VectorAdd(b->mins, b->maxs, origin); VectorScale(origin, 0.5, origin); sprintf(string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue(&entities[b->entitynum], "origin", string); VectorCopy(origin, entities[b->entitynum].origin); // don't keep this brush b->numsides = 0; return; } AddBrushBevels(b); nummapbrushes++; mapent->numbrushes++; }
static void ProjectDecalOntoWinding( decalProjector_t *dp, int numPoints, vec3_t points[ 2 ][ MAX_DECAL_VERTS ], bspSurface_t *surf, bspModel_t *bmodel ) { int i, pingPong, count, axis; float pd, d, d2, alpha = 1.f; vec4_t plane; vec3_t absNormal; decal_t *decal, *oldest; polyVert_t *vert; /* make a plane from the winding */ if ( !PlaneFromPoints( plane, points[ 0 ][ 0 ], points[ 0 ][ 1 ], points[ 0 ][ 2 ] ) ) { return; } /* omnidirectional projectors need plane type */ if ( dp->omnidirectional ) { /* compiler warnings be gone */ pd = 1.0f; /* fade by distance from plane */ d = DotProduct( dp->center, plane ) - plane[ 3 ]; alpha = 1.0f - ( fabs( d ) / dp->radius ); if ( alpha < 0.0f ) { return; } if ( alpha > 1.0f ) { alpha = 1.0f; } /* set projection axis */ absNormal[ 0 ] = fabs( plane[ 0 ] ); absNormal[ 1 ] = fabs( plane[ 1 ] ); absNormal[ 2 ] = fabs( plane[ 2 ] ); if ( absNormal[ 2 ] >= absNormal[ 0 ] && absNormal[ 2 ] >= absNormal[ 1 ] ) { axis = 2; } else if ( absNormal[ 0 ] >= absNormal[ 1 ] && absNormal[ 0 ] >= absNormal[ 2 ] ) { axis = 0; } else { axis = 1; } } else { /* backface check */ pd = DotProduct( dp->planes[ 0 ], plane ); if ( pd < -0.0001f ) { return; } /* directional decals use first texture matrix */ axis = 0; } /* chop the winding by all the projector planes */ pingPong = 0; for ( i = 0; i < dp->numPlanes; i++ ) //% dp->numPlanes { ChopWindingBehindPlane( numPoints, points[ pingPong ], &numPoints, points[ !pingPong ], dp->planes[ i ], 0.0f ); pingPong ^= 1; if ( numPoints < 3 ) { return; } if ( numPoints == MAX_DECAL_VERTS ) { break; } } /* find first free decal (fixme: optimize this) */ count = ( bmodel == tr.world->models ? MAX_WORLD_DECALS : MAX_ENTITY_DECALS ); oldest = &bmodel->decals[ 0 ]; decal = bmodel->decals; for ( i = 0; i < count; i++, decal++ ) { /* try to find an empty decal slot */ if ( decal->shader == NULL ) { break; } /* find oldest decal */ if ( decal->fadeEndTime < oldest->fadeEndTime ) { oldest = decal; } } /* guess we have to use the oldest decal */ if ( i >= count ) { decal = oldest; } /* r_speeds useful info */ tr.pc.c_decalSurfacesCreated++; /* set it up (fixme: get the shader before this happens) */ decal->parent = surf; decal->shader = dp->shader; decal->fadeStartTime = dp->fadeStartTime; decal->fadeEndTime = dp->fadeEndTime; decal->fogIndex = surf->fogIndex; /* add points */ decal->numVerts = numPoints; vert = decal->verts; for ( i = 0; i < numPoints; i++, vert++ ) { /* set xyz */ VectorCopy( points[ pingPong ][ i ], vert->xyz ); /* set st */ vert->st[ 0 ] = DotProduct( vert->xyz, dp->texMat[ axis ][ 0 ] ) + dp->texMat[ axis ][ 0 ][ 3 ]; vert->st[ 1 ] = DotProduct( vert->xyz, dp->texMat[ axis ][ 1 ] ) + dp->texMat[ axis ][ 1 ][ 3 ]; /* unidirectional decals fade by half distance from front->back planes */ if ( !dp->omnidirectional ) { /* set alpha */ d = DotProduct( vert->xyz, dp->planes[ 0 ] ) - dp->planes[ 0 ][ 3 ]; d2 = DotProduct( vert->xyz, dp->planes[ 1 ] ) - dp->planes[ 1 ][ 3 ]; alpha = 2.0f * d2 / ( d + d2 ); if ( alpha > 1.0f ) { alpha = 1.0f; } else if ( alpha < 0.0f ) { alpha = 0.0f; } } /* set color */ vert->modulate[ 0 ] = Q_ftol( pd * alpha * dp->color[ 0 ] ); vert->modulate[ 1 ] = Q_ftol( pd * alpha * dp->color[ 1 ] ); vert->modulate[ 2 ] = Q_ftol( pd * alpha * dp->color[ 2 ] ); vert->modulate[ 3 ] = Q_ftol( alpha * dp->color[ 3 ] ); } }
static int MakeDecalProjector( shaderInfo_t *si, vec4_t projection, float distance, int numVerts, bspDrawVert_t **dv ) { int i, j; decalProjector_t *dp; vec3_t xyz; /* dummy check */ if( numVerts != 3 && numVerts != 4 ) return -1; /* limit check */ if( numProjectors >= MAX_PROJECTORS ) { MsgDev( D_WARN, "MAX_PROJECTORS (%d) exceeded, no more decal projectors available.\n", MAX_PROJECTORS ); return -2; } /* create a new projector */ dp = &projectors[ numProjectors ]; Mem_Set( 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 */ ClearBounds( dp->mins, dp->maxs ); for( i = 0; i < numVerts; i++ ) { AddPointToBounds( dv[ i ]->xyz, dp->mins, dp->maxs ); VectorMA( dv[ i ]->xyz, distance, projection, xyz ); AddPointToBounds( xyz, dp->mins, dp->maxs ); } /* make bouding sphere */ VectorAdd( dp->mins, dp->maxs, dp->center ); VectorScale( dp->center, 0.5f, dp->center ); VectorSubtract( dp->maxs, dp->center, xyz ); dp->radius = VectorLength( xyz ); 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 */ VectorSubtract( vec3_origin, dp->planes[ 0 ], dp->planes[ 1 ] ); VectorMA( dv[ 0 ]->xyz, distance, projection, xyz ); dp->planes[ 1 ][ 3 ] = DotProduct( xyz, dp->planes[ 1 ] ); /* make the side planes */ for( i = 0; i < numVerts; i++ ) { j = (i + 1) % numVerts; VectorMA( dv[ i ]->xyz, distance, projection, xyz ); if( !PlaneFromPoints( dp->planes[ i + 2 ], dv[ j ]->xyz, dv[ i ]->xyz, xyz ) ) return -1; } /* return ok */ numProjectors++; return numProjectors - 1; }
/* * R_RenderMeshGLSL_Distortion */ static void R_RenderMeshGLSL_Distortion( r_glslfeat_t programFeatures ) { int i, last_slot; unsigned last_framenum; int state, tcgen; int width = 1, height = 1; int program, object; mat4x4_t unused; cplane_t plane; const char *key; shaderpass_t *pass = r_back.accumPasses[0]; image_t *portaltexture[2]; qboolean frontPlane; PlaneFromPoints( r_back.r_triangle0Copy, &plane ); plane.dist += DotProduct( ri.currententity->origin, plane.normal ); key = R_PortalKeyForPlane( &plane ); last_framenum = last_slot = 0; for( i = 0; i < 2; i++ ) { int slot; portaltexture[i] = NULL; slot = R_FindPortalTextureSlot( key, i+1 ); if( slot ) portaltexture[i] = r_portaltextures[slot-1]; if( portaltexture[i] == NULL ) { portaltexture[i] = r_blacktexture; } else { width = portaltexture[i]->upload_width; height = portaltexture[i]->upload_height; } // find the most recently updated texture if( portaltexture[i]->framenum > last_framenum ) { last_slot = i; last_framenum = i; } } // if textures were not updated sequentially, use the most recent one // and reset the remaining to black if( portaltexture[0]->framenum+1 != portaltexture[1]->framenum ) portaltexture[(last_slot+1)&1] = r_blacktexture; if( pass->anim_frames[0] != r_blankbumptexture ) programFeatures |= GLSL_DISTORTION_APPLY_DUDV; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; if( pass->flags & SHADERPASS_GRAYSCALE ) programFeatures |= GLSL_COMMON_APPLY_GRAYSCALE; if( portaltexture[0] != r_blacktexture ) programFeatures |= GLSL_DISTORTION_APPLY_REFLECTION; if( portaltexture[1] != r_blacktexture ) programFeatures |= GLSL_DISTORTION_APPLY_REFRACTION; frontPlane = (PlaneDiff( ri.viewOrigin, &ri.portalPlane ) > 0 ? qtrue : qfalse); if( frontPlane ) { if( pass->alphagen.type != ALPHA_GEN_IDENTITY ) programFeatures |= GLSL_DISTORTION_APPLY_DISTORTION_ALPHA; } tcgen = pass->tcgen; // store the original tcgen R_BindShaderpass( pass, pass->anim_frames[0], 0, NULL ); // dudvmap // calculate the fragment color R_ModifyColor( pass, programFeatures & GLSL_DISTORTION_APPLY_DISTORTION_ALPHA ? qtrue : qfalse, qfalse ); GL_TexEnv( GL_MODULATE ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); if( pass->anim_frames[1] ) { // eyeDot programFeatures |= GLSL_DISTORTION_APPLY_EYEDOT; pass->tcgen = TC_GEN_SVECTORS; GL_Bind( 1, pass->anim_frames[1] ); // normalmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, 1, unused, NULL ); } GL_Bind( 2, portaltexture[0] ); // reflection GL_Bind( 3, portaltexture[1] ); // refraction pass->tcgen = tcgen; // restore original tcgen // update uniforms program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, vec3_origin, NULL, NULL, NULL, frontPlane, width, height, 0, 0, 0, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); R_FlushArrays(); qglUseProgramObjectARB( 0 ); } }
static int MakeDecalProjector( int mapEntityNum, shaderInfo_t *si, vec4_t projection, float distance, int numVerts, bspDrawVert_t **dv, float backfacecull, float lightmapScale, vec3_t lightmapAxis, vec3_t minlight, vec3_t minvertexlight, vec3_t ambient, vec3_t colormod, int smoothNormals ) { int i, j; decalProjector_t *dp; vec3_t xyz; /* dummy check */ if( numVerts != 3 && numVerts != 4 ) return -1; /* limit check */ if( numProjectors >= MAX_PROJECTORS ) { Sys_Warning( mapEntityNum, "MAX_PROJECTORS (%d) exceeded, no more decal projectors available.", MAX_PROJECTORS ); return -2; } /* create a new projector */ dp = &projectors[ numProjectors ]; memset( dp, 0, sizeof( decalProjector_t ) ); /* basic setup */ dp->si = si; dp->numPlanes = numVerts + 2; dp->backfacecull = backfacecull; dp->lightmapScale = lightmapScale; VectorCopy( lightmapAxis, dp->lightmapAxis ); VectorCopy( minlight, dp->minlight ); VectorCopy( minvertexlight, dp->minvertexlight ); VectorCopy( ambient, dp->ambient ); VectorCopy( colormod, dp->colormod ); dp->smoothNormals = smoothNormals; dp->mapEntityNum = mapEntityNum; /* make texture matrix */ if( !MakeTextureMatrix( dp, projection, dv[ 0 ], dv[ 1 ], dv[ 2 ] ) ) return -1; /* bound the projector */ ClearBounds( dp->mins, dp->maxs ); for( i = 0; i < numVerts; i++ ) { AddPointToBounds( dv[ i ]->xyz, dp->mins, dp->maxs ); VectorMA( dv[ i ]->xyz, distance, projection, xyz ); AddPointToBounds( xyz, dp->mins, dp->maxs ); } /* make bouding sphere */ VectorAdd( dp->mins, dp->maxs, dp->center ); VectorScale( dp->center, 0.5f, dp->center ); VectorSubtract( dp->maxs, dp->center, xyz ); dp->radius = VectorLength( xyz ); 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 */ VectorSubtract( vec3_origin, dp->planes[ 0 ], dp->planes[ 1 ] ); VectorMA( dv[ 0 ]->xyz, distance, projection, xyz ); dp->planes[ 1 ][ 3 ] = DotProduct( xyz, dp->planes[ 1 ] ); /* make the side planes */ for( i = 0; i < numVerts; i++ ) { j = (i + 1) % numVerts; VectorMA( dv[ i ]->xyz, distance, projection, xyz ); if( !PlaneFromPoints( dp->planes[ i + 2 ], dv[ j ]->xyz, dv[ i ]->xyz, xyz ) ) return -1; } /* return ok */ numProjectors++; return numProjectors - 1; }