void ExportEntities( void ){ char filename[ 1024 ]; FILE *file; /* note it */ Sys_FPrintf( SYS_VRB, "--- ExportEntities ---\n" ); /* do some path mangling */ strcpy( filename, source ); StripExtension( filename ); strcat( filename, ".ent" ); /* sanity check */ if ( bspEntData == NULL || bspEntDataSize == 0 ) { Sys_Warning( "No BSP entity data. aborting...\n" ); return; } /* write it */ Sys_Printf( "Writing %s\n", filename ); Sys_FPrintf( SYS_VRB, "(%d bytes)\n", bspEntDataSize ); file = fopen( filename, "w" ); if ( file == NULL ) { Error( "Unable to open %s for writing", filename ); } fprintf( file, "%s\n", bspEntData ); fclose( file ); }
void FreeBrush( brush_t *b ) { int i; /* error check */ if( *((int*) b) == 0xFEFEFEFE ) { Sys_Warning( "Attempt to free an already freed brush!" ); return; } /* free brush sides */ for( i = 0; i < b->numsides; i++ ) if( b->sides[i].winding != NULL ) FreeWinding( b->sides[ i ].winding ); /* ydnar: overwrite it */ memset( b, 0xFE, (int) &(((brush_t*) 0)->sides[ b->numsides ]) ); *((int*) b) = 0xFEFEFEFE; /* free it */ free( b ); if( numthreads == 1 ) numActiveBrushes--; }
/* DESCRIPTION: COM_WriteFile // LOCATION: NOT the info_write thing that's in common.c // PATH: // // We make a file and write to it. The target is the game directory. // Until I rework the various systems and better know how HL should // signify which dir to write to, we use this cheap trick: // The first dir on the list should be the game dir (unless another was aded). // So just open the file regularly and it'll be in the right place. */ void COM_WriteFile(char *filename, void *filedata, int length) { hl_file_t *fp; char * c; c = strrchr(filename, '/'); if(c != NULL) { *c = '\0'; FS_CreateDirHierarchy(filename, NULL); *c = '/'; } fp = FS_Open(filename, "wb"); if (!fp) { Sys_Warning("COM_WriteFile: Couldn't open some file %s.\n", filename); } else { if(FS_Write(filedata, 1, length, fp) != length) { Sys_Warning("COM_WriteFile: Didn't manage to write all of file %s.\n", filename); } FS_Close(fp); } }
static void R_DefineEdge( int v1, int v2, int planeNum ) { int i; // check for degenerate edge if ( v1 == v2 ) { return; } // search for a matching other side for ( i = 0; i<numSilEdges; ++i) { if ( silEdges[i].v1 == v1 && silEdges[i].v2 == v2 ) { c_duplicatedEdges++; // allow it to still create a new edge continue; } if ( silEdges[i].v2 == v1 && silEdges[i].v1 == v2 ) { if ( silEdges[i].p2 != numPlanes ) { c_tripledEdges++; // allow it to still create a new edge continue; } // this is a matching back side silEdges[i].p2 = planeNum; return; } } // define the new edge if ( numSilEdges == MAX_SIL_EDGES ) { Sys_Warning( "MAX_SIL_EDGES" ); return; } silEdges[numSilEdges].p1 = planeNum; silEdges[numSilEdges].p2 = numPlanes; silEdges[numSilEdges].v1 = v1; silEdges[numSilEdges].v2 = v2; numSilEdges++; }
/* ============= GetThreadWork ============= */ int GetThreadWork( void ){ int r; int f; ThreadLock(); if ( dispatch == workcount ) { ThreadUnlock(); return -1; } f = 40 * dispatch / workcount; if ( f < oldf ) { Sys_Warning( "progress went backwards (should never happen)\n" ); oldf = f; } while ( f > oldf ) { ++oldf; if ( pacifier ) { if ( oldf % 4 == 0 ) { Sys_Printf( "%i", f / 4 ); } else{ Sys_Printf( "." ); } fflush( stdout ); /* ydnar */ } } r = dispatch; dispatch++; ThreadUnlock(); return r; }
void ParsePatch( qboolean onlyLights ) { vec_t info[ 5 ]; int i, j, k; parseMesh_t *pm; char texture[ MAX_QPATH ]; char shader[ MAX_QPATH ]; mesh_t m; bspDrawVert_t *verts; epair_t *ep; vec4_t delta, delta2, delta3; qboolean degenerate; float longestCurve; int maxIterations; MatchToken( "{" ); /* get texture */ GetToken( qtrue ); strcpy( texture, token ); Parse1DMatrix( 5, info ); m.width = info[0]; m.height = info[1]; m.verts = verts = (bspDrawVert_t *)safe_malloc( m.width * m.height * sizeof( m.verts[0] ) ); if( m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE ) Error( "ParsePatch: bad size" ); MatchToken( "(" ); for( j = 0; j < m.width ; j++ ) { MatchToken( "(" ); for( i = 0; i < m.height ; i++ ) { Parse1DMatrix( 5, verts[ i * m.width + j ].xyz ); /* ydnar: fix colors */ for( k = 0; k < MAX_LIGHTMAPS; k++ ) { verts[ i * m.width + j ].color[ k ][ 0 ] = 255; verts[ i * m.width + j ].color[ k ][ 1 ] = 255; verts[ i * m.width + j ].color[ k ][ 2 ] = 255; verts[ i * m.width + j ].color[ k ][ 3 ] = 255; } } MatchToken( ")" ); } MatchToken( ")" ); // if brush primitives format, we may have some epairs to ignore here GetToken(qtrue); if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}")) { // NOTE: we leak that! ep = ParseEPair(); } else UnGetToken(); MatchToken( "}" ); MatchToken( "}" ); /* short circuit */ if( noCurveBrushes || onlyLights ) return; /* ydnar: delete and warn about degenerate patches */ j = (m.width * m.height); VectorClear( delta ); delta[ 3 ] = 0; degenerate = qtrue; /* find first valid vector */ for( i = 1; i < j && delta[ 3 ] == 0; i++ ) { VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta ); delta[ 3 ] = VectorNormalize( delta, delta ); } /* secondary degenerate test */ if( delta[ 3 ] == 0 ) degenerate = qtrue; else { /* if all vectors match this or are zero, then this is a degenerate patch */ for( i = 1; i < j && degenerate == qtrue; i++ ) { VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta2 ); delta2[ 3 ] = VectorNormalize( delta2, delta2 ); if( delta2[ 3 ] != 0 ) { /* create inverse vector */ VectorCopy( delta2, delta3 ); delta3[ 3 ] = delta2[ 3 ]; VectorNegate( delta3, delta3 ); /* compare */ if( VectorCompare( delta, delta2 ) == qfalse && VectorCompare( delta, delta3 ) == qfalse ) degenerate = qfalse; } } } /* warn and select degenerate patch */ if( degenerate ) { Sys_Warning( mapEnt->mapEntityNum, entitySourceBrushes, "Degenerate patch" ); free( m.verts ); return; } /* find longest curve on the mesh */ longestCurve = 0.0f; maxIterations = 0; for( j = 0; j + 2 < m.width; j += 2 ) { for( i = 0; i + 2 < m.height; i += 2 ) { ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */ ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */ ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */ ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */ } } /* allocate patch mesh */ pm = (parseMesh_t *)safe_malloc( sizeof( *pm ) ); memset( pm, 0, sizeof( *pm ) ); /* ydnar: add entity/brush numbering */ pm->entityNum = mapEnt->mapEntityNum; pm->mapEntityNum = mapEnt->mapEntityNum; pm->brushNum = entitySourceBrushes; /* set shader */ sprintf( shader, "textures/%s", texture ); pm->shaderInfo = ShaderInfoForShader( shader ); /* set mesh */ pm->mesh = m; /* set longest curve */ pm->longestCurve = longestCurve; pm->maxIterations = maxIterations; /* link to the entity */ pm->next = mapEnt->patches; mapEnt->patches = pm; }
void R_IdentifySilEdges( srfTriangles_t *tri ) { int i; int numTris; int shared, single; numTris = tri->numIndices / 3; numSilEdges = 0; numPlanes = numTris; c_duplicatedEdges = 0; c_tripledEdges = 0; for ( i = 0 ; i < numTris ; i++ ) { int i1, i2, i3; i1 = tri->silIndices[ i*3 + 0 ]; i2 = tri->silIndices[ i*3 + 1 ]; i3 = tri->silIndices[ i*3 + 2 ]; // create the edges R_DefineEdge( i1, i2, i ); R_DefineEdge( i2, i3, i ); R_DefineEdge( i3, i1, i ); } if ( c_duplicatedEdges || c_tripledEdges ) { Sys_Warning( "%i duplicated edge directions, %i tripled edges", c_duplicatedEdges, c_tripledEdges ); } // if we know that the vertexes aren't going // to deform, we can remove interior triangulation edges // on otherwise planar polygons. // I earlier believed that I could also remove concave // edges, because they are never silhouettes in the conventional sense, // but they are still needed to balance out all the true sil edges // for the shadow algorithm to function int c_coplanarCulled; c_coplanarCulled = 0; c_totalSilEdges += numSilEdges; // sort the sil edges based on plane number qsort( silEdges, numSilEdges, sizeof( silEdges[0] ), SilEdgeSort ); // count up the distribution. // a perfectly built model should only have shared // edges, but most models will have some interpenetration // and dangling edges shared = 0; single = 0; for ( i = 0 ; i < numSilEdges ; i++ ) { if ( silEdges[i].p2 == numPlanes ) { sigIndice[single*2] = silEdges[i].v1; sigIndice[single*2 + 1] = silEdges[i].v2; single++; } else { shared++; } } if ( !single ) { tri->perfectHull = true; } else { tri->perfectHull = false; Sys_Warning("built model has single edges %d\n", single); } tri->numSilEdges = numSilEdges; tri->silEdges = (silEdge_t*)mem_alloc( numSilEdges * sizeof(silEdge_t) ); memcpy( tri->silEdges, silEdges, numSilEdges * sizeof( tri->silEdges[0] ) ); }
void LoadSurfaceExtraFile( const char *path ){ char srfPath[ 1024 ]; surfaceExtra_t *se; int surfaceNum, size; byte *buffer; /* dummy check */ if ( path == NULL || path[ 0 ] == '\0' ) { return; } /* load the file */ strcpy( srfPath, path ); StripExtension( srfPath ); strcat( srfPath, ".srf" ); Sys_Printf( "Loading %s\n", srfPath ); size = LoadFile( srfPath, (void**) &buffer ); if ( size <= 0 ) { Sys_Warning( "Unable to find surface file %s, using defaults.\n", srfPath ); return; } /* parse the file */ ParseFromMemory( (char *) buffer, size ); /* tokenize it */ while ( 1 ) { /* test for end of file */ if ( !GetToken( qtrue ) ) { break; } /* default? */ if ( !Q_stricmp( token, "default" ) ) { se = &seDefault; } /* surface number */ else { surfaceNum = atoi( token ); if ( surfaceNum < 0 || surfaceNum > MAX_MAP_DRAW_SURFS ) { Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath, scriptline, surfaceNum ); } while ( surfaceNum >= numSurfaceExtras ) se = AllocSurfaceExtra(); se = &surfaceExtras[ surfaceNum ]; } /* handle { } section */ if ( !GetToken( qtrue ) || strcmp( token, "{" ) ) { Error( "ReadSurfaceExtraFile(): %s, line %d: { not found", srfPath, scriptline ); } while ( 1 ) { if ( !GetToken( qtrue ) ) { break; } if ( !strcmp( token, "}" ) ) { break; } /* shader */ if ( !Q_stricmp( token, "shader" ) ) { GetToken( qfalse ); se->si = ShaderInfoForShader( token ); } /* parent surface number */ else if ( !Q_stricmp( token, "parent" ) ) { GetToken( qfalse ); se->parentSurfaceNum = atoi( token ); } /* entity number */ else if ( !Q_stricmp( token, "entity" ) ) { GetToken( qfalse ); se->entityNum = atoi( token ); } /* cast shadows */ else if ( !Q_stricmp( token, "castShadows" ) ) { GetToken( qfalse ); se->castShadows = atoi( token ); } /* recv shadows */ else if ( !Q_stricmp( token, "receiveShadows" ) ) { GetToken( qfalse ); se->recvShadows = atoi( token ); } /* lightmap sample size */ else if ( !Q_stricmp( token, "sampleSize" ) ) { GetToken( qfalse ); se->sampleSize = atoi( token ); } /* longest curve */ else if ( !Q_stricmp( token, "longestCurve" ) ) { GetToken( qfalse ); se->longestCurve = atof( token ); } /* lightmap axis vector */ else if ( !Q_stricmp( token, "lightmapAxis" ) ) { Parse1DMatrix( 3, se->lightmapAxis ); } /* ignore all other tokens on the line */ while ( TokenAvailable() ) GetToken( qfalse ); } } /* free the buffer */ free( buffer ); }
void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back ) { brush_t *b[2]; int i, j; winding_t *w, *cw[2], *midwinding; plane_t *plane, *plane2; side_t *s, *cs; float d, d_front, d_back; *front = NULL; *back = NULL; plane = &mapplanes[planenum]; // check all points d_front = d_back = 0; for (i=0 ; i<brush->numsides ; i++) { w = brush->sides[i].winding; if (!w) continue; for (j=0 ; j<w->numpoints ; j++) { d = DotProduct (w->p[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) d_front = d; if (d < 0 && d < d_back) d_back = d; } } if (d_front < 0.1) // PLANESIDE_EPSILON) { // only on back *back = CopyBrush( brush ); return; } if (d_back > -0.1) // PLANESIDE_EPSILON) { // only on front *front = CopyBrush( brush ); return; } // create a new winding from the split plane w = BaseWindingForPlane (plane->normal, plane->dist); for (i=0 ; i<brush->numsides && w ; i++) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); } if (!w || WindingIsTiny (w) ) { // the brush isn't really split int side; side = BrushMostlyOnSide (brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush (brush); if (side == PSIDE_BACK) *back = CopyBrush (brush); return; } if( WindingIsHuge( w ) ) Sys_Warning( w, "Huge winding" ); midwinding = w; // split it for real for (i=0 ; i<2 ; i++) { b[i] = AllocBrush (brush->numsides+1); memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) ); b[i]->numsides = 0; b[i]->next = NULL; b[i]->original = brush->original; } // split all the current windings for (i=0 ; i<brush->numsides ; i++) { s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon (w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j=0 ; j<2 ; j++) { if (!cw[j]) continue; cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; } } // see if we have valid polygons on both sides for (i=0 ; i<2 ; i++) { if (b[i]->numsides < 3 || !BoundBrush (b[i])) { if (b[i]->numsides >= 3) Sys_FPrintf (SYS_VRB,"bogus brush after clip\n"); FreeBrush (b[i]); b[i] = NULL; } } if ( !(b[0] && b[1]) ) { if (!b[0] && !b[1]) Sys_FPrintf (SYS_VRB,"split removed brush\n"); else Sys_FPrintf (SYS_VRB,"split not on both sides\n"); if (b[0]) { FreeBrush (b[0]); *front = CopyBrush (brush); } if (b[1]) { FreeBrush (b[1]); *back = CopyBrush (brush); } return; } // add the midwinding to both sides for (i=0 ; i<2 ; i++) { cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum^i^1; cs->shaderInfo = NULL; if (i==0) cs->winding = CopyWinding (midwinding); else cs->winding = midwinding; } { vec_t v1; int i; for (i=0 ; i<2 ; i++) { v1 = BrushVolume (b[i]); if (v1 < 1.0) { FreeBrush (b[i]); b[i] = NULL; // Sys_FPrintf (SYS_VRB,"tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
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 ); }
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; }