qboolean R_LoadMDM(model_t *mod, void *buffer, const char *modName) { int i, j, k; mdmHeader_t *mdm; // mdmFrame_t *frame; mdmSurface_t *mdmSurf; mdmTriangle_t *mdmTri; mdmVertex_t *mdmVertex; mdmTag_t *mdmTag; int version; // int size; shader_t *sh; int32_t *collapseMap, *collapseMapOut, *boneref, *bonerefOut; mdmModel_t *mdmModel; mdmTagIntern_t *tag; mdmSurfaceIntern_t *surf; srfTriangle_t *tri; md5Vertex_t *v; mdm = ( mdmHeader_t * ) buffer; version = LittleLong(mdm->version); if (version != MDM_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadMDM: %s has wrong version (%i should be %i)\n", modName, version, MDM_VERSION); return qfalse; } mod->type = MOD_MDM; // size = LittleLong(mdm->ofsEnd); mod->dataSize += sizeof(mdmModel_t); //mdm = mod->mdm = ri.Hunk_Alloc(size, h_low); //memcpy(mdm, buffer, LittleLong(pinmodel->ofsEnd)); mdmModel = mod->mdm = ri.Hunk_Alloc(sizeof(mdmModel_t), h_low); LL(mdm->ident); LL(mdm->version); //LL(mdm->numFrames); LL(mdm->numTags); LL(mdm->numSurfaces); //LL(mdm->ofsFrames); LL(mdm->ofsTags); LL(mdm->ofsEnd); LL(mdm->ofsSurfaces); mdmModel->lodBias = LittleFloat(mdm->lodBias); mdmModel->lodScale = LittleFloat(mdm->lodScale); /* mdm->skel = RE_RegisterModel(mdm->bonesfile); if ( !mdm->skel ) { ri.Error (ERR_DROP, "R_LoadMDM: %s skeleton not found", mdm->bonesfile ); } if ( mdm->numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMDM: %s has no frames\n", modName ); return qfalse; }*/ // swap all the frames /*frameSize = (int) ( sizeof( mdmFrame_t ) ); for ( i = 0 ; i < mdm->numFrames ; i++, frame++) { frame = (mdmFrame_t *) ( (byte *)mdm + mdm->ofsFrames + i * frameSize ); frame->radius = LittleFloat( frame->radius ); for ( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); frame->parentOffset[j] = LittleFloat( frame->parentOffset[j] ); } } */ // swap all the tags mdmModel->numTags = mdm->numTags; mdmModel->tags = tag = ri.Hunk_Alloc(sizeof(*tag) * mdm->numTags, h_low); mdmTag = ( mdmTag_t * )(( byte * ) mdm + mdm->ofsTags); for (i = 0; i < mdm->numTags; i++, tag++) { int ii; Q_strncpyz(tag->name, mdmTag->name, sizeof(tag->name)); for (ii = 0; ii < 3; ii++) { tag->axis[ii][0] = LittleFloat(mdmTag->axis[ii][0]); tag->axis[ii][1] = LittleFloat(mdmTag->axis[ii][1]); tag->axis[ii][2] = LittleFloat(mdmTag->axis[ii][2]); } tag->boneIndex = LittleLong(mdmTag->boneIndex); //tag->torsoWeight = LittleFloat( tag->torsoWeight ); tag->offset[0] = LittleFloat(mdmTag->offset[0]); tag->offset[1] = LittleFloat(mdmTag->offset[1]); tag->offset[2] = LittleFloat(mdmTag->offset[2]); LL(mdmTag->numBoneReferences); LL(mdmTag->ofsBoneReferences); LL(mdmTag->ofsEnd); tag->numBoneReferences = mdmTag->numBoneReferences; tag->boneReferences = ri.Hunk_Alloc(sizeof(*bonerefOut) * mdmTag->numBoneReferences, h_low); // swap the bone references boneref = ( int32_t * )(( byte * ) mdmTag + mdmTag->ofsBoneReferences); for (j = 0, bonerefOut = tag->boneReferences; j < mdmTag->numBoneReferences; j++, boneref++, bonerefOut++) { *bonerefOut = LittleLong(*boneref); } // find the next tag mdmTag = ( mdmTag_t * )(( byte * ) mdmTag + mdmTag->ofsEnd); } // swap all the surfaces mdmModel->numSurfaces = mdm->numSurfaces; mdmModel->surfaces = ri.Hunk_Alloc(sizeof(*surf) * mdmModel->numSurfaces, h_low); mdmSurf = ( mdmSurface_t * )(( byte * ) mdm + mdm->ofsSurfaces); for (i = 0, surf = mdmModel->surfaces; i < mdm->numSurfaces; i++, surf++) { LL(mdmSurf->shaderIndex); LL(mdmSurf->ofsHeader); LL(mdmSurf->ofsCollapseMap); LL(mdmSurf->numTriangles); LL(mdmSurf->ofsTriangles); LL(mdmSurf->numVerts); LL(mdmSurf->ofsVerts); LL(mdmSurf->numBoneReferences); LL(mdmSurf->ofsBoneReferences); LL(mdmSurf->ofsEnd); surf->minLod = LittleLong(mdmSurf->minLod); // change to surface identifier surf->surfaceType = SF_MDM; surf->model = mdmModel; Q_strncpyz(surf->name, mdmSurf->name, sizeof(surf->name)); if (mdmSurf->numVerts > SHADER_MAX_VERTEXES) { ri.Error(ERR_DROP, "R_LoadMDM: %s has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, mdmSurf->numVerts); } if (mdmSurf->numTriangles > SHADER_MAX_TRIANGLES) { ri.Error(ERR_DROP, "R_LoadMDM: %s has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, mdmSurf->numTriangles); } // register the shaders if (mdmSurf->shader[0]) { Q_strncpyz(surf->shader, mdmSurf->shader, sizeof(surf->shader)); sh = R_FindShader(surf->shader, SHADER_3D_DYNAMIC, qtrue); if (sh->defaultShader) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } } else { surf->shaderIndex = 0; } // swap all the triangles surf->numTriangles = mdmSurf->numTriangles; surf->triangles = ri.Hunk_Alloc(sizeof(*tri) * surf->numTriangles, h_low); mdmTri = ( mdmTriangle_t * )(( byte * ) mdmSurf + mdmSurf->ofsTriangles); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, mdmTri++, tri++) { tri->indexes[0] = LittleLong(mdmTri->indexes[0]); tri->indexes[1] = LittleLong(mdmTri->indexes[1]); tri->indexes[2] = LittleLong(mdmTri->indexes[2]); } // swap all the vertexes surf->numVerts = mdmSurf->numVerts; surf->verts = ri.Hunk_Alloc(sizeof(*v) * surf->numVerts, h_low); mdmVertex = ( mdmVertex_t * )(( byte * ) mdmSurf + mdmSurf->ofsVerts); for (j = 0, v = surf->verts; j < mdmSurf->numVerts; j++, v++) { v->normal[0] = LittleFloat(mdmVertex->normal[0]); v->normal[1] = LittleFloat(mdmVertex->normal[1]); v->normal[2] = LittleFloat(mdmVertex->normal[2]); v->texCoords[0] = LittleFloat(mdmVertex->texCoords[0]); v->texCoords[1] = LittleFloat(mdmVertex->texCoords[1]); v->numWeights = LittleLong(mdmVertex->numWeights); if (v->numWeights > MAX_WEIGHTS) { #if 0 ri.Error(ERR_DROP, "R_LoadMDM: vertex %i requires %i instead of maximum %i weights on surface (%i) in model '%s'", j, v->numWeights, MAX_WEIGHTS, i, modName); #else ri.Printf(PRINT_WARNING, "WARNING: R_LoadMDM: vertex %i requires %i instead of maximum %i weights on surface (%i) in model '%s'\n", j, v->numWeights, MAX_WEIGHTS, i, modName); #endif } v->weights = ri.Hunk_Alloc(sizeof(*v->weights) * v->numWeights, h_low); for (k = 0; k < v->numWeights; k++) { md5Weight_t *weight = ri.Hunk_Alloc(sizeof(*weight), h_low); weight->boneIndex = LittleLong(mdmVertex->weights[k].boneIndex); weight->boneWeight = LittleFloat(mdmVertex->weights[k].boneWeight); weight->offset[0] = LittleFloat(mdmVertex->weights[k].offset[0]); weight->offset[1] = LittleFloat(mdmVertex->weights[k].offset[1]); weight->offset[2] = LittleFloat(mdmVertex->weights[k].offset[2]); v->weights[k] = weight; } mdmVertex = ( mdmVertex_t * ) &mdmVertex->weights[v->numWeights]; } // swap the collapse map surf->collapseMap = ri.Hunk_Alloc(sizeof(*collapseMapOut) * mdmSurf->numVerts, h_low); collapseMap = ( int32_t * )(( byte * ) mdmSurf + mdmSurf->ofsCollapseMap); //ri.Printf(PRINT_ALL, "collapse map for mdm surface '%s': ", surf->name); for (j = 0, collapseMapOut = surf->collapseMap; j < mdmSurf->numVerts; j++, collapseMap++, collapseMapOut++) { int32_t value = LittleLong(*collapseMap); //surf->collapseMap[j] = value; *collapseMapOut = value; //ri.Printf(PRINT_ALL, "(%i -> %i) ", j, value); } //ri.Printf(PRINT_ALL, "\n"); #if 0 ri.Printf(PRINT_ALL, "collapse map for mdm surface '%s': ", surf->name); for (j = 0, collapseMap = surf->collapseMap; j < mdmSurf->numVerts; j++, collapseMap++) { ri.Printf(PRINT_ALL, "(%i -> %i) ", j, *collapseMap); } ri.Printf(PRINT_ALL, "\n"); #endif // swap the bone references surf->numBoneReferences = mdmSurf->numBoneReferences; surf->boneReferences = ri.Hunk_Alloc(sizeof(*bonerefOut) * mdmSurf->numBoneReferences, h_low); boneref = ( int32_t * )(( byte * ) mdmSurf + mdmSurf->ofsBoneReferences); for (j = 0, bonerefOut = surf->boneReferences; j < surf->numBoneReferences; j++, boneref++, bonerefOut++) { *bonerefOut = LittleLong(*boneref); } // find the next surface mdmSurf = ( mdmSurface_t * )(( byte * ) mdmSurf + mdmSurf->ofsEnd); } // loading is done now calculate the bounding box and tangent spaces ClearBounds(mdmModel->bounds[0], mdmModel->bounds[1]); for (i = 0, surf = mdmModel->surfaces; i < mdmModel->numSurfaces; i++, surf++) { for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { vec3_t tmpVert; md5Weight_t *w; VectorClear(tmpVert); for (k = 0, w = v->weights[0]; k < v->numWeights; k++, w++) { //vec3_t offsetVec; //VectorClear(offsetVec); //bone = &md5->bones[w->boneIndex]; //QuatTransformVector(bone->rotation, w->offset, offsetVec); //VectorAdd(bone->origin, offsetVec, offsetVec); VectorMA(tmpVert, w->boneWeight, w->offset, tmpVert); } VectorCopy(tmpVert, v->position); AddPointToBounds(tmpVert, mdmModel->bounds[0], mdmModel->bounds[1]); } // calc tangent spaces #if 0 { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorClear(v->tangent); VectorClear(v->binormal); VectorClear(v->normal); } for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { v0 = surf->verts[tri->indexes[0]].position; v1 = surf->verts[tri->indexes[1]].position; v2 = surf->verts[tri->indexes[2]].position; t0 = surf->verts[tri->indexes[0]].texCoords; t1 = surf->verts[tri->indexes[1]].texCoords; t2 = surf->verts[tri->indexes[2]].texCoords; #if 1 R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, v0, v1, v2); R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2); #endif for (k = 0; k < 3; k++) { float *v; v = surf->verts[tri->indexes[k]].tangent; VectorAdd(v, tangent, v); v = surf->verts[tri->indexes[k]].binormal; VectorAdd(v, binormal, v); v = surf->verts[tri->indexes[k]].normal; VectorAdd(v, normal, v); } } for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorNormalize(v->tangent); VectorNormalize(v->binormal); VectorNormalize(v->normal); } } #else { int k; float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[3]; for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { dv[0] = &surf->verts[tri->indexes[0]]; dv[1] = &surf->verts[tri->indexes[1]]; dv[2] = &surf->verts[tri->indexes[2]]; R_CalcNormalForTriangle(faceNormal, dv[0]->position, dv[1]->position, dv[2]->position); // calculate barycentric basis for the triangle bb = (dv[1]->texCoords[0] - dv[0]->texCoords[0]) * (dv[2]->texCoords[1] - dv[0]->texCoords[1]) - (dv[2]->texCoords[0] - dv[0]->texCoords[0]) * (dv[1]->texCoords[1] - dv[0]->texCoords[1]); if (fabs(bb) < 0.00000001f) { continue; } // do each vertex for (k = 0; k < 3; k++) { // calculate s tangent vector s = dv[k]->texCoords[0] + 10.0f; t = dv[k]->texCoords[1]; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->tangent[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->tangent[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->tangent[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->tangent, dv[k]->position, dv[k]->tangent); VectorNormalize(dv[k]->tangent); // calculate t tangent vector (binormal) s = dv[k]->texCoords[0]; t = dv[k]->texCoords[1] + 10.0f; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->binormal[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->binormal[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->binormal[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->binormal, dv[k]->position, dv[k]->binormal); VectorNormalize(dv[k]->binormal); // calculate the normal as cross product N=TxB #if 0 CrossProduct(dv[k]->tangent, dv[k]->binormal, dv[k]->normal); VectorNormalize(dv[k]->normal); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct(dv[k]->normal, dv[k]->tangent, dv[k]->binormal); if (DotProduct(dv[k]->normal, faceNormal) < 0) { VectorInverse(dv[k]->normal); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else //VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal); #endif } } #if 1 for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { //VectorNormalize(v->tangent); //VectorNormalize(v->binormal); //VectorNormalize(v->normal); } #endif } #endif #if 0 // do another extra smoothing for normals to avoid flat shading for (j = 0; j < surf->numVerts; j++) { for (k = 0; k < surf->numVerts; k++) { if (j == k) { continue; } if (VectorCompare(surf->verts[j].position, surf->verts[k].position)) { VectorAdd(surf->verts[j].normal, surf->verts[k].normal, surf->verts[j].normal); } } VectorNormalize(surf->verts[j].normal); } #endif } // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones { int numRemaining; growList_t sortedTriangles; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[MAX_BONES]; Com_InitGrowList(&vboSurfaces, 10); for (i = 0, surf = mdmModel->surfaces; i < mdmModel->numSurfaces; i++, surf++) { // sort triangles Com_InitGrowList(&sortedTriangles, 1000); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { skelTriangle_t *sortTri = Com_Allocate(sizeof(*sortTri)); for (k = 0; k < 3; k++) { sortTri->indexes[k] = tri->indexes[k]; sortTri->vertexes[k] = &surf->verts[tri->indexes[k]]; } sortTri->referenced = qfalse; Com_AddToGrowList(&sortedTriangles, sortTri); } //qsort(sortedTriangles.elements, sortedTriangles.currentElements, sizeof(void *), CompareTrianglesByBoneReferences); #if 0 for (j = 0; j < sortedTriangles.currentElements; j++) { int b[MAX_WEIGHTS * 3]; skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); for (k = 0; k < 3; k++) { v = sortTri->vertexes[k]; for (l = 0; l < MAX_WEIGHTS; l++) { b[k * 3 + l] = (l < v->numWeights) ? v->weights[l]->boneIndex : 9999; } qsort(b, MAX_WEIGHTS * 3, sizeof(int), CompareBoneIndices); //ri.Printf(PRINT_ALL, "bone indices: %i %i %i %i\n", b[k * 3 + 0], b[k * 3 + 1], b[k * 3 + 2], b[k * 3 + 3]); } } #endif numRemaining = sortedTriangles.currentElements; while (numRemaining) { numBoneReferences = 0; Com_Memset(boneReferences, 0, sizeof(boneReferences)); Com_InitGrowList(&vboTriangles, 1000); for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); if (sortTri->referenced) { continue; } if (AddTriangleToVBOTriangleList(&vboTriangles, sortTri, &numBoneReferences, boneReferences)) { sortTri->referenced = qtrue; } } if (!vboTriangles.currentElements) { ri.Printf(PRINT_WARNING, "R_LoadMDM: could not add triangles to a remaining VBO surface for model '%s'\n", modName); break; } AddSurfaceToVBOSurfacesListMDM(&vboSurfaces, &vboTriangles, mdmModel, surf, i, numBoneReferences, boneReferences); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList(&vboTriangles); } for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); Com_Dealloc(sortTri); } Com_DestroyGrowList(&sortedTriangles); } // move VBO surfaces list to hunk mdmModel->numVBOSurfaces = vboSurfaces.currentElements; mdmModel->vboSurfaces = ri.Hunk_Alloc(mdmModel->numVBOSurfaces * sizeof(*mdmModel->vboSurfaces), h_low); for (i = 0; i < mdmModel->numVBOSurfaces; i++) { mdmModel->vboSurfaces[i] = ( srfVBOMDMMesh_t * ) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); } return qtrue; }
void FogDrawSurfaces(entity_t * e) { int i, j, k, fogNum; fog_t *fog; mapDrawSurface_t *ds; vec3_t mins, maxs; int fogged, numFogged; int numBaseDrawSurfs; /* note it */ Sys_FPrintf(SYS_VRB, "----- FogDrawSurfs -----\n"); /* reset counters */ numFogged = 0; numFogFragments = 0; /* walk fog list */ for(fogNum = 0; fogNum < numMapFogs; fogNum++) { /* get fog */ fog = &mapFogs[fogNum]; /* clip each surface into this, but don't clip any of the resulting fragments to the same brush */ numBaseDrawSurfs = numMapDrawSurfs; for(i = 0; i < numBaseDrawSurfs; i++) { /* get the drawsurface */ ds = &mapDrawSurfs[i]; /* no fog? */ if(ds->shaderInfo->noFog) continue; /* global fog doesn't have a brush */ if(fog->brush == NULL) { /* don't re-fog already fogged surfaces */ if(ds->fogNum >= 0) continue; fogged = 1; } else { /* find drawsurface bounds */ ClearBounds(mins, maxs); for(j = 0; j < ds->numVerts; j++) AddPointToBounds(ds->verts[j].xyz, mins, maxs); /* check against the fog brush */ for(k = 0; k < 3; k++) { if(mins[k] > fog->brush->maxs[k]) break; if(maxs[k] < fog->brush->mins[k]) break; } /* no intersection? */ if(k < 3) continue; /* ydnar: gs mods: handle the various types of surfaces */ switch (ds->type) { /* handle brush faces */ case SURFACE_FACE: fogged = ChopFaceSurfaceByBrush(e, ds, fog->brush); break; /* handle patches */ case SURFACE_PATCH: fogged = ChopPatchSurfaceByBrush(e, ds, fog->brush); break; /* handle triangle surfaces (fixme: split triangle surfaces) */ case SURFACE_TRIANGLES: case SURFACE_FORCED_META: case SURFACE_META: fogged = 1; break; /* no fogging */ default: fogged = 0; break; } } /* is this surface fogged? */ if(fogged) { numFogged += fogged; ds->fogNum = fogNum; } } } /* emit some statistics */ Sys_FPrintf(SYS_VRB, "%9d fog polygon fragments\n", numFogFragments); Sys_FPrintf(SYS_VRB, "%9d fog patch fragments\n", numFogPatchFragments); Sys_FPrintf(SYS_VRB, "%9d fogged drawsurfs\n", numFogged); }
/* ============ ExpandBrush ============= */ void ExpandBrush (int hullnum) { int i, x, s; vec3_t corner; winding_t *w; plane_t plane; int j, k, numwindings; vec_t r; winding_t **windings; plane_t clipplane, faceplane; mface_t *mf; vec3_t point; vec3_t mins, maxs; if (!numbrushfaces) return; num_hull_points = 0; num_hull_edges = 0; ClearBounds( mins, maxs ); // generate windings and bounds data numwindings = 0; windings = calloc(numbrushfaces, sizeof(*windings)); for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; windings[i] = NULL; faceplane = mf->plane; w = BaseWindingForPlane (&faceplane); for (j = 0;j < numbrushfaces && w;j++) { clipplane = faces[j].plane; if( j == i ) continue; // flip the plane, because we want to keep the back side VectorNegate(clipplane.normal, clipplane.normal); clipplane.dist *= -1; w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true); } if (!w) continue; // overcontrained plane for (j = 0;j < w->numpoints;j++) { for (k = 0;k < 3;k++) { point[k] = w->points[j][k]; r = Q_rint( point[k] ); if ( fabs( point[k] - r ) < ZERO_EPSILON) w->points[j][k] = r; else w->points[j][k] = point[k]; // check for incomplete brushes if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE ) return; } AddPointToBounds( w->points[j], mins, maxs ); } windings[i] = w; } // add all of the corner offsets for (i = 0;i < numwindings;i++) { w = windings[i]; for (j = 0;j < w->numpoints;j++) AddHullPoint(w->points[j], hullnum); } // expand the face planes for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; for (x=0 ; x<3 ; x++) { if (mf->plane.normal[x] > 0) corner[x] = -hullinfo.hullsizes[hullnum][0][x]; else if (mf->plane.normal[x] < 0) corner[x] = -hullinfo.hullsizes[hullnum][1][x]; } mf->plane.dist += DotProduct (corner, mf->plane.normal); } // add any axis planes not contained in the brush to bevel off corners for (x=0 ; x<3 ; x++) for (s=-1 ; s<=1 ; s+=2) { // add the plane VectorClear (plane.normal); plane.normal[x] = s; if (s == -1) plane.dist = -mins[x] + hullinfo.hullsizes[hullnum][1][x]; else plane.dist = maxs[x] + -hullinfo.hullsizes[hullnum][0][x]; AddBrushPlane (&plane); } // add all of the edge bevels for (i = 0;i < numwindings;i++) { w = windings[i]; for (j = 0;j < w->numpoints;j++) AddHullEdge(w->points[j], w->points[(j+1)%w->numpoints], hullnum); } // free the windings as we no longer need them for (i = 0;i < numwindings;i++) if (windings[i]) FreeWinding(windings[i]); free(windings); }
/* ================= R_MarkFragments ================= */ int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { int numsurfaces, numPlanes; int i, j, k, m, n; surfaceType_t *surfaces[ 64 ]; vec3_t mins, maxs; int returnedFragments; int returnedPoints; vec3_t normals[ MAX_VERTS_ON_POLY + 2 ]; float dists[ MAX_VERTS_ON_POLY + 2 ]; vec3_t clipPoints[ 2 ][ MAX_VERTS_ON_POLY ]; int numClipPoints; float *v; srfSurfaceFace_t *face; srfGridMesh_t *cv; srfTriangles_t *trisurf; srfVert_t *dv; srfTriangle_t *tri; vec3_t normal; vec3_t projectionDir; vec3_t v1, v2; //increment view count for double check prevention tr.viewCountNoReset++; // VectorNormalize2( projection, projectionDir ); // find all the brushes that are to be considered ClearBounds( mins, maxs ); for ( i = 0; i < numPoints; i++ ) { vec3_t temp; AddPointToBounds( points[ i ], mins, maxs ); VectorAdd( points[ i ], projection, temp ); AddPointToBounds( temp, mins, maxs ); // make sure we get all the leafs (also the one(s) in front of the hit surface) VectorMA( points[ i ], -20, projectionDir, temp ); AddPointToBounds( temp, mins, maxs ); } if ( numPoints > MAX_VERTS_ON_POLY ) { numPoints = MAX_VERTS_ON_POLY; } // create the bounding planes for the to be projected polygon for ( i = 0; i < numPoints; i++ ) { VectorSubtract( points[( i + 1 ) % numPoints ], points[ i ], v1 ); VectorAdd( points[ i ], projection, v2 ); VectorSubtract( points[ i ], v2, v2 ); CrossProduct( v1, v2, normals[ i ] ); VectorNormalizeFast( normals[ i ] ); dists[ i ] = DotProduct( normals[ i ], points[ i ] ); } // add near and far clipping planes for projection VectorCopy( projectionDir, normals[ numPoints ] ); dists[ numPoints ] = DotProduct( normals[ numPoints ], points[ 0 ] ) - 32; VectorCopy( projectionDir, normals[ numPoints + 1 ] ); VectorInverse( normals[ numPoints + 1 ] ); dists[ numPoints + 1 ] = DotProduct( normals[ numPoints + 1 ], points[ 0 ] ) - 20; numPlanes = numPoints + 2; numsurfaces = 0; R_BoxSurfaces_r( tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir ); returnedPoints = 0; returnedFragments = 0; for ( i = 0; i < numsurfaces; i++ ) { if ( *surfaces[ i ] == SF_GRID ) { cv = ( srfGridMesh_t * ) surfaces[ i ]; for ( m = 0; m < cv->height - 1; m++ ) { for ( n = 0; n < cv->width - 1; n++ ) { // We triangulate the grid and chop all triangles within // the bounding planes of the to be projected polygon. // LOD is not taken into account, not such a big deal though. // // It's probably much nicer to chop the grid itself and deal // with this chopped grid as an SF_GRID surface, so LOD will // be applied. However the LOD of that chopped grid must // be synced with the LOD of the original curve. // One way to do this; the chopped grid shares vertices with // the original curve. When LOD is applied to the original // curve the unused vertices are flagged. Now the chopped curve // should skip the flagged vertices. This still leaves the // problems with the vertices at the chopped grid edges. // // To avoid issues when LOD applied to "hollow curves" (like // the ones around many jump pads) we now just add a 2 unit // offset to the triangle vertices. // The offset is added in the vertex normal vector direction // so all triangles will still fit together. // The 2 unit offset should avoid pretty much all LOD problems. numClipPoints = 3; dv = cv->verts + m * cv->width + n; VectorCopy( dv[ 0 ].xyz, clipPoints[ 0 ][ 0 ] ); VectorMA( clipPoints[ 0 ][ 0 ], MARKER_OFFSET, dv[ 0 ].normal, clipPoints[ 0 ][ 0 ] ); VectorCopy( dv[ cv->width ].xyz, clipPoints[ 0 ][ 1 ] ); VectorMA( clipPoints[ 0 ][ 1 ], MARKER_OFFSET, dv[ cv->width ].normal, clipPoints[ 0 ][ 1 ] ); VectorCopy( dv[ 1 ].xyz, clipPoints[ 0 ][ 2 ] ); VectorMA( clipPoints[ 0 ][ 2 ], MARKER_OFFSET, dv[ 1 ].normal, clipPoints[ 0 ][ 2 ] ); // check the normal of this triangle VectorSubtract( clipPoints[ 0 ][ 0 ], clipPoints[ 0 ][ 1 ], v1 ); VectorSubtract( clipPoints[ 0 ][ 2 ], clipPoints[ 0 ][ 1 ], v2 ); CrossProduct( v1, v2, normal ); VectorNormalizeFast( normal ); if ( DotProduct( normal, projectionDir ) < -0.1 ) { // add the fragments of this triangle R_AddMarkFragments( numClipPoints, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } VectorCopy( dv[ 1 ].xyz, clipPoints[ 0 ][ 0 ] ); VectorMA( clipPoints[ 0 ][ 0 ], MARKER_OFFSET, dv[ 1 ].normal, clipPoints[ 0 ][ 0 ] ); VectorCopy( dv[ cv->width ].xyz, clipPoints[ 0 ][ 1 ] ); VectorMA( clipPoints[ 0 ][ 1 ], MARKER_OFFSET, dv[ cv->width ].normal, clipPoints[ 0 ][ 1 ] ); VectorCopy( dv[ cv->width + 1 ].xyz, clipPoints[ 0 ][ 2 ] ); VectorMA( clipPoints[ 0 ][ 2 ], MARKER_OFFSET, dv[ cv->width + 1 ].normal, clipPoints[ 0 ][ 2 ] ); // check the normal of this triangle VectorSubtract( clipPoints[ 0 ][ 0 ], clipPoints[ 0 ][ 1 ], v1 ); VectorSubtract( clipPoints[ 0 ][ 2 ], clipPoints[ 0 ][ 1 ], v2 ); CrossProduct( v1, v2, normal ); VectorNormalizeFast( normal ); if ( DotProduct( normal, projectionDir ) < -0.05 ) { // add the fragments of this triangle R_AddMarkFragments( numClipPoints, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } } } else if ( *surfaces[ i ] == SF_FACE ) { face = ( srfSurfaceFace_t * ) surfaces[ i ]; // check the normal of this face if ( DotProduct( face->plane.normal, projectionDir ) > -0.5 ) { continue; } for ( k = 0, tri = face->triangles; k < face->numTriangles; k++, tri++ ) { for ( j = 0; j < 3; j++ ) { v = face->verts[ tri->indexes[ j ] ].xyz; VectorMA( v, MARKER_OFFSET, face->plane.normal, clipPoints[ 0 ][ j ] ); } // add the fragments of this face R_AddMarkFragments( 3, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } else if ( *surfaces[ i ] == SF_TRIANGLES && !r_noMarksOnTrisurfs->integer ) { trisurf = ( srfTriangles_t * ) surfaces[ i ]; for ( k = 0, tri = trisurf->triangles; k < trisurf->numTriangles; k++, tri++ ) { for ( j = 0; j < 3; j++ ) { VectorCopy( trisurf->verts[ tri->indexes[ j ] ].xyz, clipPoints[ 0 ][ j ] ); } // add the fragments of this face R_AddMarkFragments( 3, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } } return returnedFragments; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_StoreArea( tmp_area_t *tmparea ) { int side, edgenum, i; plane_t *plane; tmp_face_t *tmpface; aas_area_t *aasarea; aas_edge_t *edge; aas_face_t *aasface; aas_faceindex_t aasfacenum; vec3_t facecenter; winding_t *w; //when the area is merged go to the merged area //FIXME: this isn't necessary anymore because the tree // is refreshed after area merging while ( tmparea->mergedarea ) tmparea = tmparea->mergedarea; // if ( tmparea->invalid ) { Error( "AAS_StoreArea: tried to store invalid area" ); } //if there is an aas area already stored for this tmp area if ( tmparea->aasareanum ) { return -tmparea->aasareanum; } // if ( ( *aasworld ).numareas >= max_aas.max_areas ) { Error( "AAS_MAX_AREAS = %d", max_aas.max_areas ); } //end if //area zero is a dummy if ( ( *aasworld ).numareas == 0 ) { ( *aasworld ).numareas = 1; } //create an area from this leaf aasarea = &( *aasworld ).areas[( *aasworld ).numareas]; aasarea->areanum = ( *aasworld ).numareas; aasarea->numfaces = 0; aasarea->firstface = ( *aasworld ).faceindexsize; ClearBounds( aasarea->mins, aasarea->maxs ); VectorClear( aasarea->center ); // // Log_Write("tmparea %d became aasarea %d\r\n", tmparea->areanum, aasarea->areanum); //store the aas area number at the tmp area tmparea->aasareanum = aasarea->areanum; // for ( tmpface = tmparea->tmpfaces; tmpface; tmpface = tmpface->next[side] ) { side = tmpface->frontarea != tmparea; //if there's an aas face created for the tmp face already if ( tmpface->aasfacenum ) { //we're at the back of the face so use a negative index aasfacenum = -tmpface->aasfacenum; #ifdef DEBUG if ( tmpface->aasfacenum < 0 || tmpface->aasfacenum > max_aas.max_faces ) { Error( "AAS_CreateTree_r: face number out of range" ); } //end if #endif //DEBUG aasface = &( *aasworld ).faces[tmpface->aasfacenum]; aasface->backarea = aasarea->areanum; } //end if else { plane = &mapplanes[tmpface->planenum ^ side]; if ( side ) { w = tmpface->winding; tmpface->winding = ReverseWinding( tmpface->winding ); } //end if if ( !AAS_GetFace( tmpface->winding, plane, 0, &aasfacenum ) ) { continue; } if ( side ) { FreeWinding( tmpface->winding ); tmpface->winding = w; } //end if aasface = &( *aasworld ).faces[aasfacenum]; aasface->frontarea = aasarea->areanum; aasface->backarea = 0; aasface->faceflags = tmpface->faceflags; //set the face number at the tmp face tmpface->aasfacenum = aasfacenum; } //end else //add face points to the area bounds and //calculate the face 'center' VectorClear( facecenter ); for ( edgenum = 0; edgenum < aasface->numedges; edgenum++ ) { edge = &( *aasworld ).edges[abs( ( *aasworld ).edgeindex[aasface->firstedge + edgenum] )]; for ( i = 0; i < 2; i++ ) { AddPointToBounds( ( *aasworld ).vertexes[edge->v[i]], aasarea->mins, aasarea->maxs ); VectorAdd( ( *aasworld ).vertexes[edge->v[i]], facecenter, facecenter ); } //end for } //end for VectorScale( facecenter, 1.0 / ( aasface->numedges * 2.0 ), facecenter ); //add the face 'center' to the area 'center' VectorAdd( aasarea->center, facecenter, aasarea->center ); // if ( ( *aasworld ).faceindexsize >= max_aas.max_faceindexsize ) { Error( "AAS_MAX_FACEINDEXSIZE = %d", max_aas.max_faceindexsize ); } //end if ( *aasworld ).faceindex[( *aasworld ).faceindexsize++] = aasfacenum; aasarea->numfaces++; } //end for //if the area has no faces at all (return 0, = solid leaf) if ( !aasarea->numfaces ) { return 0; } // VectorScale( aasarea->center, 1.0 / aasarea->numfaces, aasarea->center ); //Log_Write("area %d center %f %f %f\r\n", (*aasworld).numareas, // aasarea->center[0], aasarea->center[1], aasarea->center[2]); //store the area settings AAS_StoreAreaSettings( tmparea->settings ); // //Log_Write("tmp area %d became aas area %d\r\n", tmpareanum, aasarea->areanum); qprintf( "\r%6d", aasarea->areanum ); // if ( ( *aasworld ).areasettings[aasarea->areanum].contents & AREACONTENTS_CLUSTERPORTAL ) { static int num; Log_Write( "***** area %d is a cluster portal %d\n", aasarea->areanum, num++ ); } //end if // ( *aasworld ).numareas++; return -( ( *aasworld ).numareas - 1 ); } //end of the function AAS_StoreArea
qboolean R_LoadMD5(model_t *mod, void *buffer, int bufferSize, const char *modName) { int i, j, k; md5Model_t *md5; md5Bone_t *bone; md5Surface_t *surf; srfTriangle_t *tri; md5Vertex_t *v; md5Weight_t *weight; int version; shader_t *sh; char *buf_p; char *token; vec3_t boneOrigin; quat_t boneQuat; matrix_t boneMat; int numRemaining; growList_t sortedTriangles; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[MAX_BONES]; buf_p = ( char * ) buffer; // skip MD5Version indent string COM_ParseExt2(&buf_p, qfalse); // check version token = COM_ParseExt2(&buf_p, qfalse); version = atoi(token); if (version != MD5_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION); return qfalse; } mod->type = MOD_MD5; mod->dataSize += sizeof(md5Model_t); md5 = mod->md5 = ri.Hunk_Alloc(sizeof(md5Model_t), h_low); // skip commandline <arguments string> token = COM_ParseExt2(&buf_p, qtrue); token = COM_ParseExt2(&buf_p, qtrue); // ri.Printf(PRINT_ALL, "%s\n", token); // parse numJoints <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numJoints")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); md5->numBones = atoi(token); // parse numMeshes <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numMeshes")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); md5->numSurfaces = atoi(token); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); if (md5->numBones < 1) { ri.Printf(PRINT_WARNING, "R_LoadMD5: '%s' has no bones\n", modName); return qfalse; } if (md5->numBones > MAX_BONES) { ri.Printf(PRINT_WARNING, "R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones); // parse all the bones md5->bones = ri.Hunk_Alloc(sizeof(*bone) * md5->numBones, h_low); // parse joints { token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "joints")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "{")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName); return qfalse; } for (i = 0, bone = md5->bones; i < md5->numBones; i++, bone++) { token = COM_ParseExt2(&buf_p, qtrue); Q_strncpyz(bone->name, token, sizeof(bone->name)); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name); token = COM_ParseExt2(&buf_p, qfalse); bone->parentIndex = atoi(token); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex); if (bone->parentIndex >= md5->numBones) { ri.Error(ERR_DROP, "R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i", modName, bone->name, bone->parentIndex, md5->numBones); } // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (j = 0; j < 3; j++) { token = COM_ParseExt2(&buf_p, qfalse); boneOrigin[j] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (j = 0; j < 3; j++) { token = COM_ParseExt2(&buf_p, qfalse); boneQuat[j] = atof(token); } QuatCalcW(boneQuat); MatrixFromQuat(boneMat, boneQuat); VectorCopy(boneOrigin, bone->origin); QuatCopy(boneQuat, bone->rotation); MatrixSetupTransformFromQuat(bone->inverseTransform, boneQuat, boneOrigin); MatrixInverse(bone->inverseTransform); // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } } // parse } token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "}")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName); return qfalse; } // parse all the surfaces if (md5->numSurfaces < 1) { ri.Printf(PRINT_WARNING, "R_LoadMD5: '%s' has no surfaces\n", modName); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); md5->surfaces = ri.Hunk_Alloc(sizeof(*surf) * md5->numSurfaces, h_low); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { // parse mesh { token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "mesh")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "{")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName); return qfalse; } // change to surface identifier surf->surfaceType = SF_MD5; // give pointer to model for Tess_SurfaceMD5 surf->model = md5; // parse shader <name> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "shader")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'shader' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); Q_strncpyz(surf->shader, token, sizeof(surf->shader)); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader); // FIXME .md5mesh meshes don't have surface names // lowercase the surface name so skin compares are faster //Q_strlwr(surf->name); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name); // register the shaders sh = R_FindShader(surf->shader, SHADER_3D_DYNAMIC, qtrue); if (sh->defaultShader) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } // parse numVerts <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numVerts")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numVerts = atoi(token); if (surf->numVerts > SHADER_MAX_VERTEXES) { ri.Error(ERR_DROP, "R_LoadMD5: '%s' has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, surf->numVerts); } surf->verts = ri.Hunk_Alloc(sizeof(*v) * surf->numVerts, h_low); for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { // skip vert <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "vert")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (k = 0; k < 2; k++) { token = COM_ParseExt2(&buf_p, qfalse); v->texCoords[k] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); v->firstWeight = atoi(token); token = COM_ParseExt2(&buf_p, qfalse); v->numWeights = atoi(token); if (v->numWeights > MAX_WEIGHTS) { ri.Error(ERR_DROP, "R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'", j, MAX_WEIGHTS, i, modName); } } // parse numTris <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numTris")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numTriangles = atoi(token); if (surf->numTriangles > SHADER_MAX_TRIANGLES) { ri.Error(ERR_DROP, "R_LoadMD5: '%s' has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, surf->numTriangles); } surf->triangles = ri.Hunk_Alloc(sizeof(*tri) * surf->numTriangles, h_low); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { // skip tri <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "tri")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); for (k = 0; k < 3; k++) { token = COM_ParseExt2(&buf_p, qfalse); tri->indexes[k] = atoi(token); } } // parse numWeights <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "numWeights")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName); return qfalse; } token = COM_ParseExt2(&buf_p, qfalse); surf->numWeights = atoi(token); surf->weights = ri.Hunk_Alloc(sizeof(*weight) * surf->numWeights, h_low); for (j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++) { // skip weight <number> token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "weight")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName); return qfalse; } COM_ParseExt2(&buf_p, qfalse); token = COM_ParseExt2(&buf_p, qfalse); weight->boneIndex = atoi(token); token = COM_ParseExt2(&buf_p, qfalse); weight->boneWeight = atof(token); // skip ( token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, "(")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName); return qfalse; } for (k = 0; k < 3; k++) { token = COM_ParseExt2(&buf_p, qfalse); weight->offset[k] = atof(token); } // skip ) token = COM_ParseExt2(&buf_p, qfalse); if (Q_stricmp(token, ")")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName); return qfalse; } } // parse } token = COM_ParseExt2(&buf_p, qtrue); if (Q_stricmp(token, "}")) { ri.Printf(PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName); return qfalse; } // loop trough all vertices and set up the vertex weights for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { v->weights = ri.Hunk_Alloc(sizeof(*v->weights) * v->numWeights, h_low); for (k = 0; k < v->numWeights; k++) { v->weights[k] = surf->weights + (v->firstWeight + k); } } } // loading is done now calculate the bounding box and tangent spaces ClearBounds(md5->bounds[0], md5->bounds[1]); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { vec3_t tmpVert; md5Weight_t *w; VectorClear(tmpVert); for (k = 0, w = v->weights[0]; k < v->numWeights; k++, w++) { vec3_t offsetVec; bone = &md5->bones[w->boneIndex]; QuatTransformVector(bone->rotation, w->offset, offsetVec); VectorAdd(bone->origin, offsetVec, offsetVec); VectorMA(tmpVert, w->boneWeight, offsetVec, tmpVert); } VectorCopy(tmpVert, v->position); AddPointToBounds(tmpVert, md5->bounds[0], md5->bounds[1]); } // calc tangent spaces #if 1 { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorClear(v->tangent); VectorClear(v->binormal); VectorClear(v->normal); } for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { v0 = surf->verts[tri->indexes[0]].position; v1 = surf->verts[tri->indexes[1]].position; v2 = surf->verts[tri->indexes[2]].position; t0 = surf->verts[tri->indexes[0]].texCoords; t1 = surf->verts[tri->indexes[1]].texCoords; t2 = surf->verts[tri->indexes[2]].texCoords; #if 1 R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, v0, v1, v2); R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2); #endif for (k = 0; k < 3; k++) { float *v; v = surf->verts[tri->indexes[k]].tangent; VectorAdd(v, tangent, v); v = surf->verts[tri->indexes[k]].binormal; VectorAdd(v, binormal, v); v = surf->verts[tri->indexes[k]].normal; VectorAdd(v, normal, v); } } for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { VectorNormalize(v->tangent); VectorNormalize(v->binormal); VectorNormalize(v->normal); } } #else { int k; float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[3]; for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { dv[0] = &surf->verts[tri->indexes[0]]; dv[1] = &surf->verts[tri->indexes[1]]; dv[2] = &surf->verts[tri->indexes[2]]; R_CalcNormalForTriangle(faceNormal, dv[0]->position, dv[1]->position, dv[2]->position); // calculate barycentric basis for the triangle bb = (dv[1]->texCoords[0] - dv[0]->texCoords[0]) * (dv[2]->texCoords[1] - dv[0]->texCoords[1]) - (dv[2]->texCoords[0] - dv[0]->texCoords[0]) * (dv[1]->texCoords[1] - dv[0]->texCoords[1]); if (fabs(bb) < 0.00000001f) { continue; } // do each vertex for (k = 0; k < 3; k++) { // calculate s tangent vector s = dv[k]->texCoords[0] + 10.0f; t = dv[k]->texCoords[1]; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->tangent[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->tangent[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->tangent[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->tangent, dv[k]->position, dv[k]->tangent); VectorNormalize(dv[k]->tangent); // calculate t tangent vector (binormal) s = dv[k]->texCoords[0]; t = dv[k]->texCoords[1] + 10.0f; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->binormal[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->binormal[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->binormal[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->binormal, dv[k]->position, dv[k]->binormal); VectorNormalize(dv[k]->binormal); // calculate the normal as cross product N=TxB #if 0 CrossProduct(dv[k]->tangent, dv[k]->binormal, dv[k]->normal); VectorNormalize(dv[k]->normal); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct(dv[k]->normal, dv[k]->tangent, dv[k]->binormal); if (DotProduct(dv[k]->normal, faceNormal) < 0) { VectorInverse(dv[k]->normal); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal); #endif } } #if 1 for (j = 0, v = surf->verts; j < surf->numVerts; j++, v++) { //VectorNormalize(v->tangent); //VectorNormalize(v->binormal); VectorNormalize(v->normal); } #endif } #endif #if 0 // do another extra smoothing for normals to avoid flat shading for (j = 0; j < surf->numVerts; j++) { for (k = 0; k < surf->numVerts; k++) { if (j == k) { continue; } if (VectorCompare(surf->verts[j].position, surf->verts[k].position)) { VectorAdd(surf->verts[j].normal, surf->verts[k].normal, surf->verts[j].normal); } } VectorNormalize(surf->verts[j].normal); } #endif } // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones Com_InitGrowList(&vboSurfaces, 10); for (i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++) { // sort triangles Com_InitGrowList(&sortedTriangles, 1000); for (j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { skelTriangle_t *sortTri = Com_Allocate(sizeof(*sortTri)); for (k = 0; k < 3; k++) { sortTri->indexes[k] = tri->indexes[k]; sortTri->vertexes[k] = &surf->verts[tri->indexes[k]]; } sortTri->referenced = qfalse; Com_AddToGrowList(&sortedTriangles, sortTri); } //qsort(sortedTriangles.elements, sortedTriangles.currentElements, sizeof(void *), CompareTrianglesByBoneReferences); #if 0 for (j = 0; j < sortedTriangles.currentElements; j++) { int b[MAX_WEIGHTS * 3]; skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); for (k = 0; k < 3; k++) { v = sortTri->vertexes[k]; for (l = 0; l < MAX_WEIGHTS; l++) { b[k * 3 + l] = (l < v->numWeights) ? v->weights[l]->boneIndex : 9999; } qsort(b, MAX_WEIGHTS * 3, sizeof(int), CompareBoneIndices); //ri.Printf(PRINT_ALL, "bone indices: %i %i %i %i\n", b[k * 3 + 0], b[k * 3 + 1], b[k * 3 + 2], b[k * 3 + 3]); } } #endif numRemaining = sortedTriangles.currentElements; while (numRemaining) { numBoneReferences = 0; Com_Memset(boneReferences, 0, sizeof(boneReferences)); Com_InitGrowList(&vboTriangles, 1000); for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); if (sortTri->referenced) { continue; } if (AddTriangleToVBOTriangleList(&vboTriangles, sortTri, &numBoneReferences, boneReferences)) { sortTri->referenced = qtrue; } } if (!vboTriangles.currentElements) { ri.Printf(PRINT_WARNING, "R_LoadMD5: could not add triangles to a remaining VBO surfaces for model '%s'\n", modName); Com_DestroyGrowList(&vboTriangles); break; } AddSurfaceToVBOSurfacesList(&vboSurfaces, &vboTriangles, md5, surf, i, numBoneReferences, boneReferences); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList(&vboTriangles); } for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = Com_GrowListElement(&sortedTriangles, j); Com_Dealloc(sortTri); } Com_DestroyGrowList(&sortedTriangles); } // move VBO surfaces list to hunk md5->numVBOSurfaces = vboSurfaces.currentElements; md5->vboSurfaces = ri.Hunk_Alloc(md5->numVBOSurfaces * sizeof(*md5->vboSurfaces), h_low); for (i = 0; i < md5->numVBOSurfaces; i++) { md5->vboSurfaces[i] = ( srfVBOMD5Mesh_t * ) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); return qtrue; }
/* ============ BuildST Builds the triangle_st array for the base frame and model.skinwidth / model.skinheight FIXME: allow this to be loaded from a file for arbitrary mappings ============ */ void BuildST (triangle_t *ptri, int numtri) { int i, j; int width, height, iwidth, iheight, swidth; float basex, basey; float s_scale, t_scale; float scale; vec3_t mins, maxs; float *pbasevert; vec3_t vtemp1, vtemp2, normal; // // find bounds of all the verts on the base frame // ClearBounds (mins, maxs); for (i=0 ; i<numtri ; i++) for (j=0 ; j<3 ; j++) AddPointToBounds (ptri[i].verts[j], mins, maxs); for (i=0 ; i<3 ; i++) { mins[i] = floor(mins[i]); maxs[i] = ceil(maxs[i]); } width = maxs[0] - mins[0]; height = maxs[2] - mins[2]; if (!g_fixedwidth) { // old style scale = 8; if (width*scale >= 150) scale = 150.0 / width; if (height*scale >= 190) scale = 190.0 / height; s_scale = t_scale = scale; iwidth = ceil(width*s_scale); iheight = ceil(height*t_scale); iwidth += 4; iheight += 4; } else { // new style iwidth = g_fixedwidth / 2; iheight = g_fixedheight; s_scale = (float)(iwidth-4) / width; t_scale = (float)(iheight-4) / height; } // // determine which side of each triangle to map the texture to // for (i=0 ; i<numtri ; i++) { VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1); VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2); CrossProduct (vtemp1, vtemp2, normal); if (normal[1] > 0) { basex = iwidth + 2; } else { basex = 2; } basey = 2; for (j=0 ; j<3 ; j++) { pbasevert = ptri[i].verts[j]; triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex); triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey); } } // make the width a multiple of 4; some hardware requires this, and it ensures // dword alignment for each scan swidth = iwidth*2; model.skinwidth = (swidth + 3) & ~3; model.skinheight = iheight; }
/* ===================== RE_AddPolyToScene ===================== */ void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; int i, j; int fogIndex; fog_t *fog; vec3_t bounds[2]; if ( !tr.registered ) { return; } if ( !hShader ) { ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); return; } for ( j = 0; j < numPolys; j++ ) { if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) { /* NOTE TTimo this was initially a PRINT_WARNING but it happens a lot with high fighting scenes and particles since we don't plan on changing the const and making for room for those effects simply cut this message to developer only */ ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n"); return; } poly = &backEndData[tr.smpFrame]->polys[r_numpolys]; poly->surfaceType = SF_POLY; poly->hShader = hShader; poly->numVerts = numVerts; poly->verts = &backEndData[tr.smpFrame]->polyVerts[r_numpolyverts]; Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) ); if ( glConfig.hardwareType == GLHW_RAGEPRO ) { poly->verts->modulate[0] = 255; poly->verts->modulate[1] = 255; poly->verts->modulate[2] = 255; poly->verts->modulate[3] = 255; } // done. r_numpolys++; r_numpolyverts += numVerts; // if no world is loaded if ( tr.world == NULL ) { fogIndex = 0; } // see if it is in a fog volume else if ( tr.world->numfogs == 1 ) { fogIndex = 0; } else { // find which fog volume the poly is in VectorCopy( poly->verts[0].xyz, bounds[0] ); VectorCopy( poly->verts[0].xyz, bounds[1] ); for ( i = 1 ; i < poly->numVerts ; i++ ) { AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] ); } for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) { fog = &tr.world->fogs[fogIndex]; if ( bounds[1][0] >= fog->bounds[0][0] && bounds[1][1] >= fog->bounds[0][1] && bounds[1][2] >= fog->bounds[0][2] && bounds[0][0] <= fog->bounds[1][0] && bounds[0][1] <= fog->bounds[1][1] && bounds[0][2] <= fog->bounds[1][2] ) { break; } } if ( fogIndex == tr.world->numfogs ) { fogIndex = 0; } } poly->fogIndex = fogIndex; } }
/* ======================================================================================================================================= RE_AddPolyToScene ======================================================================================================================================= */ void RE_AddPolyToScene(qhandle_t hShader, int numVerts, const polyVert_t *verts) { srfPoly_t *poly; int i; int fogIndex; fog_t *fog; vec3_t bounds[2]; if (!tr.registered) { return; } if (!hShader) { // This isn't a useful warning, and an hShader of zero isn't a null shader, it's // the default shader. //ri.Printf(PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); //return; } if (((r_numpolyverts + numVerts) >= max_polyverts) || (r_numpolys >= max_polys)) { return; } poly = &backEndData->polys[r_numpolys]; poly->surfaceType = SF_POLY; poly->hShader = hShader; poly->numVerts = numVerts; poly->verts = &backEndData->polyVerts[r_numpolyverts]; memcpy(poly->verts, verts, numVerts * sizeof(*verts)); // Ridah if (glConfig.hardwareType == GLHW_RAGEPRO) { poly->verts->modulate[0] = 255; poly->verts->modulate[1] = 255; poly->verts->modulate[2] = 255; poly->verts->modulate[3] = 255; } // done. r_numpolys++; r_numpolyverts += numVerts; // see if it is in a fog volume if (tr.world->numfogs == 1) { fogIndex = 0; } else { // find which fog volume the poly is in VectorCopy(poly->verts[0].xyz, bounds[0]); VectorCopy(poly->verts[0].xyz, bounds[1]); for (i = 1 ; i < poly->numVerts ; i++) { AddPointToBounds(poly->verts[i].xyz, bounds[0], bounds[1]); } for (fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++) { fog = &tr.world->fogs[fogIndex]; if (bounds[1][0] >= fog->bounds[0][0] && bounds[1][1] >= fog->bounds[0][1] && bounds[1][2] >= fog->bounds[0][2] && bounds[0][0] <= fog->bounds[1][0] && bounds[0][1] <= fog->bounds[1][1] && bounds[0][2] <= fog->bounds[1][2]) { break; } } if (fogIndex == tr.world->numfogs) { fogIndex = 0; } } poly->fogIndex = fogIndex; }
/* ================= R_MarkFragments ================= */ int R_MarkFragments( int orientation, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { int numsurfaces, numPlanes; int i, j, k, m, n; surfaceType_t *surfaces[4096]; vec3_t mins, maxs; int returnedFragments; int returnedPoints; vec3_t normals[MAX_VERTS_ON_POLY + 2]; float dists[MAX_VERTS_ON_POLY + 2]; vec3_t clipPoints[2][MAX_VERTS_ON_POLY]; int numClipPoints; float *v; srfGridMesh_t *cv; drawVert_t *dv; vec3_t normal; vec3_t projectionDir; vec3_t v1, v2; int *indexes; float radius; vec3_t center; // center of original mark int numPoints = 4; // Ridah, we were only ever passing in 4, so I made this local and used the parameter for the orientation qboolean oldMapping = qfalse; if (numPoints <= 0) { return 0; } //increment view count for double check prevention tr.viewCount++; // RF, negative maxFragments means we want original mapping if ( maxFragments < 0 ) { maxFragments = -maxFragments; oldMapping = qtrue; } VectorClear( center ); for ( i = 0 ; i < numPoints ; i++ ) { VectorAdd( points[i], center, center ); } VectorScale( center, 1.0 / numPoints, center ); // radius = VectorNormalize2( projection, projectionDir ) / 2.0; bestdist = 0; VectorNegate( projectionDir, bestnormal ); // find all the brushes that are to be considered ClearBounds( mins, maxs ); for ( i = 0 ; i < numPoints ; i++ ) { vec3_t temp; AddPointToBounds( points[i], mins, maxs ); VectorMA( points[i], 1 * ( 1 + oldMapping * radius * 4 ), projection, temp ); AddPointToBounds( temp, mins, maxs ); // make sure we get all the leafs (also the one(s) in front of the hit surface) VectorMA( points[i], -20 * ( 1.0 + (float)oldMapping * ( radius / 20.0 ) * 4 ), projectionDir, temp ); AddPointToBounds( temp, mins, maxs ); } if ( numPoints > MAX_VERTS_ON_POLY ) { numPoints = MAX_VERTS_ON_POLY; } // create the bounding planes for the to be projected polygon for ( i = 0 ; i < numPoints ; i++ ) { VectorSubtract( points[( i + 1 ) % numPoints], points[i], v1 ); VectorAdd( points[i], projection, v2 ); VectorSubtract( points[i], v2, v2 ); CrossProduct( v1, v2, normals[i] ); VectorNormalize( normals[i] ); dists[i] = DotProduct( normals[i], points[i] ); } // add near and far clipping planes for projection VectorCopy( projectionDir, normals[numPoints] ); dists[numPoints] = DotProduct( normals[numPoints], points[0] ) - radius * ( 1 + oldMapping * 10 ); VectorCopy( projectionDir, normals[numPoints + 1] ); VectorInverse( normals[numPoints + 1] ); dists[numPoints + 1] = DotProduct( normals[numPoints + 1], points[0] ) - radius * ( 1 + oldMapping * 10 ); numPlanes = numPoints + 2; numsurfaces = 0; R_BoxSurfaces_r( tr.world->nodes, mins, maxs, surfaces, 4096, &numsurfaces, projectionDir ); returnedPoints = 0; returnedFragments = 0; // find the closest surface to center the decal there, and wrap around other surfaces if ( !oldMapping ) { VectorNegate( bestnormal, bestnormal ); } for ( i = 0 ; i < numsurfaces ; i++ ) { if ( *surfaces[i] == SF_GRID ) { cv = (srfGridMesh_t *) surfaces[i]; for ( m = 0 ; m < cv->height - 1 ; m++ ) { for ( n = 0 ; n < cv->width - 1 ; n++ ) { // We triangulate the grid and chop all triangles within // the bounding planes of the to be projected polygon. // LOD is not taken into account, not such a big deal though. // // It's probably much nicer to chop the grid itself and deal // with this grid as a normal SF_GRID surface so LOD will // be applied. However the LOD of that chopped grid must // be synced with the LOD of the original curve. // One way to do this; the chopped grid shares vertices with // the original curve. When LOD is applied to the original // curve the unused vertices are flagged. Now the chopped curve // should skip the flagged vertices. This still leaves the // problems with the vertices at the chopped grid edges. // // To avoid issues when LOD applied to "hollow curves" (like // the ones around many jump pads) we now just add a 2 unit // offset to the triangle vertices. // The offset is added in the vertex normal vector direction // so all triangles will still fit together. // The 2 unit offset should avoid pretty much all LOD problems. numClipPoints = 3; dv = cv->verts + m * cv->width + n; VectorCopy( dv[0].xyz, clipPoints[0][0] ); VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0] ); VectorCopy( dv[cv->width].xyz, clipPoints[0][1] ); VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] ); VectorCopy( dv[1].xyz, clipPoints[0][2] ); VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2] ); // check the normal of this triangle VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 ); VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 ); CrossProduct( v1, v2, normal ); VectorNormalize( normal ); if ( DotProduct( normal, projectionDir ) < -0.1 ) { // add the fragments of this triangle R_AddMarkFragments( numClipPoints, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } VectorCopy( dv[1].xyz, clipPoints[0][0] ); VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0] ); VectorCopy( dv[cv->width].xyz, clipPoints[0][1] ); VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] ); VectorCopy( dv[cv->width + 1].xyz, clipPoints[0][2] ); VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[cv->width + 1].normal, clipPoints[0][2] ); // check the normal of this triangle VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 ); VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 ); CrossProduct( v1, v2, normal ); VectorNormalize( normal ); if ( DotProduct( normal, projectionDir ) < -0.05 ) { // add the fragments of this triangle R_AddMarkFragments( numClipPoints, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } } } else if ( *surfaces[i] == SF_FACE ) { extern float VectorDistance( vec3_t v1, vec3_t v2 ); vec3_t axis[3]; float texCoordScale, dot; vec3_t originalPoints[4]; vec3_t newCenter, delta; int oldNumPoints; float epsilon = 0.5; // duplicated so we don't mess with the original clips for the curved surfaces vec3_t lnormals[MAX_VERTS_ON_POLY + 2]; float ldists[MAX_VERTS_ON_POLY + 2]; vec3_t lmins, lmaxs; srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i]; if ( !oldMapping ) { // Ridah, create a new clip box such that this decal surface is mapped onto // the current surface without distortion. To find the center of the new clip box, // we project the center of the original impact center out along the projection vector, // onto the current surface // find the center of the new decal dot = DotProduct( center, surf->plane.normal ); dot -= surf->plane.dist; // check the normal of this face if ( dot < -epsilon && DotProduct( surf->plane.normal, projectionDir ) >= 0.01 ) { continue; } else if ( fabs( dot ) > radius ) { continue; } // if the impact point is behind the surface, subtract the projection, otherwise add it VectorMA( center, -dot, bestnormal, newCenter ); // recalc dot from the offset position dot = DotProduct( newCenter, surf->plane.normal ); dot -= surf->plane.dist; VectorMA( newCenter, -dot, surf->plane.normal, newCenter ); VectorMA( newCenter, MARKER_OFFSET, surf->plane.normal, newCenter ); // create the texture axis VectorNormalize2( surf->plane.normal, axis[0] ); PerpendicularVector( axis[1], axis[0] ); RotatePointAroundVector( axis[2], axis[0], axis[1], (float)orientation ); CrossProduct( axis[0], axis[2], axis[1] ); texCoordScale = 0.5 * 1.0 / radius; // create the full polygon for ( j = 0 ; j < 3 ; j++ ) { originalPoints[0][j] = newCenter[j] - radius * axis[1][j] - radius * axis[2][j]; originalPoints[1][j] = newCenter[j] + radius * axis[1][j] - radius * axis[2][j]; originalPoints[2][j] = newCenter[j] + radius * axis[1][j] + radius * axis[2][j]; originalPoints[3][j] = newCenter[j] - radius * axis[1][j] + radius * axis[2][j]; } ClearBounds( lmins, lmaxs ); // create the bounding planes for the to be projected polygon for ( j = 0 ; j < 4 ; j++ ) { AddPointToBounds( originalPoints[j], lmins, lmaxs ); VectorSubtract( originalPoints[( j + 1 ) % numPoints], originalPoints[j], v1 ); VectorSubtract( originalPoints[j], surf->plane.normal, v2 ); VectorSubtract( originalPoints[j], v2, v2 ); CrossProduct( v1, v2, lnormals[j] ); VectorNormalize( lnormals[j] ); ldists[j] = DotProduct( lnormals[j], originalPoints[j] ); } numPlanes = numPoints; // done. indexes = ( int * )( (byte *)surf + surf->ofsIndices ); for ( k = 0 ; k < surf->numIndices ; k += 3 ) { for ( j = 0 ; j < 3 ; j++ ) { v = surf->points[0] + VERTEXSIZE * indexes[k + j]; VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); } oldNumPoints = returnedPoints; // add the fragments of this face R_AddMarkFragments( 3, clipPoints, numPlanes, lnormals, ldists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, lmins, lmaxs ); if ( oldNumPoints != returnedPoints ) { // flag this surface as already having computed ST's fragmentBuffer[returnedFragments - 1].numPoints *= -1; // Ridah, calculate ST's for ( j = 0 ; j < ( returnedPoints - oldNumPoints ) ; j++ ) { VectorSubtract( (float *)pointBuffer + 5 * ( oldNumPoints + j ), newCenter, delta ); *( (float *)pointBuffer + 5 * ( oldNumPoints + j ) + 3 ) = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale; *( (float *)pointBuffer + 5 * ( oldNumPoints + j ) + 4 ) = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale; } } if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } else { // old mapping // check the normal of this face //if (DotProduct(surf->plane.normal, projectionDir) > 0.0) { // continue; //} indexes = ( int * )( (byte *)surf + surf->ofsIndices ); for ( k = 0 ; k < surf->numIndices ; k += 3 ) { for ( j = 0 ; j < 3 ; j++ ) { v = surf->points[0] + VERTEXSIZE * indexes[k + j];; VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); } // add the fragments of this face R_AddMarkFragments( 3, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs ); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } } else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) { srfTriangles_t *surf = (srfTriangles_t *) surfaces[i]; for (k = 0; k < surf->numIndexes; k += 3) { for(j = 0; j < 3; j++) { v = surf->verts[surf->indexes[k + j]].xyz; VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]); } // add the fragments of this face R_AddMarkFragments(3, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); if(returnedFragments == maxFragments) { return returnedFragments; // not enough space for more fragments } } } } return returnedFragments; }
void BeginModel( void ){ bspModel_t *mod; brush_t *b; entity_t *e; vec3_t mins, maxs; vec3_t lgMins, lgMaxs; /* ydnar: lightgrid mins/maxs */ parseMesh_t *p; int i; /* test limits */ if ( numBSPModels == MAX_MAP_MODELS ) { Error( "MAX_MAP_MODELS" ); } /* get model and entity */ mod = &bspModels[ numBSPModels ]; e = &entities[ mapEntityNum ]; /* ydnar: lightgrid mins/maxs */ ClearBounds( lgMins, lgMaxs ); /* bound the brushes */ ClearBounds( mins, maxs ); for ( b = e->brushes; b; b = b->next ) { /* ignore non-real brushes (origin, etc) */ if ( b->numsides == 0 ) { continue; } AddPointToBounds( b->mins, mins, maxs ); AddPointToBounds( b->maxs, mins, maxs ); /* ydnar: lightgrid bounds */ if ( b->compileFlags & C_LIGHTGRID ) { AddPointToBounds( b->mins, lgMins, lgMaxs ); AddPointToBounds( b->maxs, lgMins, lgMaxs ); } } /* bound patches */ for ( p = e->patches; p; p = p->next ) { for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ ) AddPointToBounds( p->mesh.verts[i].xyz, mins, maxs ); } /* ydnar: lightgrid mins/maxs */ if ( lgMins[ 0 ] < 99999 ) { /* use lightgrid bounds */ VectorCopy( lgMins, mod->mins ); VectorCopy( lgMaxs, mod->maxs ); } else { /* use brush/patch bounds */ VectorCopy( mins, mod->mins ); VectorCopy( maxs, mod->maxs ); } /* note size */ Sys_FPrintf( SYS_VRB, "BSP bounds: { %f %f %f } { %f %f %f }\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] ); Sys_FPrintf( SYS_VRB, "Lightgrid bounds: { %f %f %f } { %f %f %f }\n", lgMins[ 0 ], lgMins[ 1 ], lgMins[ 2 ], lgMaxs[ 0 ], lgMaxs[ 1 ], lgMaxs[ 2 ] ); /* set firsts */ mod->firstBSPSurface = numBSPDrawSurfaces; mod->firstBSPBrush = numBSPBrushes; }
/* ================= R_LoadMD5 ================= */ qboolean R_LoadMD5( model_t *mod, void *buffer, int bufferSize, const char *modName ) { int i, j, k; md5Model_t *md5; md5Bone_t *bone; md5Surface_t *surf; md5Triangle_t *tri; md5Vertex_t *v; md5Weight_t *weight; int version; shader_t *sh; char *buf_p; char *token; vec3_t boneOrigin; quat_t boneQuat; matrix_t boneMat; buf_p = ( char * ) buffer; // skip MD5Version indent string COM_ParseExt2( &buf_p, qfalse ); // check version token = COM_ParseExt2( &buf_p, qfalse ); version = atoi( token ); if ( version != MD5_VERSION ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: %s has wrong version (%i should be %i)\n", modName, version, MD5_VERSION ); return qfalse; } mod->type = MOD_MD5; mod->dataSize += sizeof( md5Model_t ); md5 = mod->model.md5 = ri.Hunk_Alloc( sizeof( md5Model_t ), h_low ); // skip commandline <arguments string> token = COM_ParseExt2( &buf_p, qtrue ); token = COM_ParseExt2( &buf_p, qtrue ); // ri.Printf(PRINT_ALL, "%s\n", token); // parse numJoints <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numJoints" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numJoints' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); md5->numBones = atoi( token ); // parse numMeshes <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numMeshes" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numMeshes' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); md5->numSurfaces = atoi( token ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); if ( md5->numBones < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has no bones\n", modName ); return qfalse; } if ( md5->numBones > MAX_BONES ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones ); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i bones\n", modName, md5->numBones); // parse all the bones md5->bones = ri.Hunk_Alloc( sizeof( *bone ) * md5->numBones, h_low ); // parse joints { token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "joints" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'joints' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "{" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( i = 0, bone = md5->bones; i < md5->numBones; i++, bone++ ) { token = COM_ParseExt2( &buf_p, qtrue ); Q_strncpyz( bone->name, token, sizeof( bone->name ) ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s'\n", modName, bone->name); token = COM_ParseExt2( &buf_p, qfalse ); bone->parentIndex = atoi( token ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has bone '%s' with parent index %i\n", modName, bone->name, bone->parentIndex); if ( bone->parentIndex >= md5->numBones ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has bone '%s' with bad parent index %i while numBones is %i\n", modName, bone->name, bone->parentIndex, md5->numBones ); } // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( j = 0; j < 3; j++ ) { token = COM_ParseExt2( &buf_p, qfalse ); boneOrigin[ j ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( j = 0; j < 3; j++ ) { token = COM_ParseExt2( &buf_p, qfalse ); boneQuat[ j ] = atof( token ); } QuatCalcW( boneQuat ); MatrixFromQuat( boneMat, boneQuat ); VectorCopy( boneOrigin, bone->origin ); QuatCopy( boneQuat, bone->rotation ); MatrixSetupTransformFromQuat( bone->inverseTransform, boneQuat, boneOrigin ); MatrixInverse( bone->inverseTransform ); // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } } // parse } token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "}" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName ); return qfalse; } // parse all the surfaces if ( md5->numSurfaces < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: '%s' has no surfaces\n", modName ); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has %i surfaces\n", modName, md5->numSurfaces); md5->surfaces = ri.Hunk_Alloc( sizeof( *surf ) * md5->numSurfaces, h_low ); for ( i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++ ) { // parse mesh { token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "mesh" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'mesh' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "{" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '{' found '%s' in model '%s'\n", token, modName ); return qfalse; } // change to surface identifier surf->surfaceType = SF_MD5; // give pointer to model for Tess_SurfaceMD5 surf->model = md5; // parse shader <name> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "shader" ) ) { Q_strncpyz( surf->shader, "<default>", sizeof( surf->shader ) ); surf->shaderIndex = 0; } else { token = COM_ParseExt2( &buf_p, qfalse ); Q_strncpyz( surf->shader, token, sizeof( surf->shader ) ); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' uses shader '%s'\n", modName, surf->shader); // FIXME .md5mesh meshes don't have surface names // lowercase the surface name so skin compares are faster //Q_strlwr(surf->name); //ri.Printf(PRINT_ALL, "R_LoadMD5: '%s' has surface '%s'\n", modName, surf->name); // register the shaders sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); if ( sh->defaultShader ) { surf->shaderIndex = 0; } else { surf->shaderIndex = sh->index; } token = COM_ParseExt2( &buf_p, qtrue ); } // parse numVerts <number> if ( Q_stricmp( token, "numVerts" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numVerts' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numVerts = atoi( token ); if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, surf->numVerts ); } surf->verts = ri.Hunk_Alloc( sizeof( *v ) * surf->numVerts, h_low ); for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { // skip vert <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "vert" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'vert' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( k = 0; k < 2; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); v->texCoords[ k ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); v->firstWeight = atoi( token ); token = COM_ParseExt2( &buf_p, qfalse ); v->numWeights = atoi( token ); if ( v->numWeights > MAX_WEIGHTS ) { ri.Error( ERR_DROP, "R_LoadMD5: vertex %i requires more than %i weights on surface (%i) in model '%s'", j, MAX_WEIGHTS, i, modName ); } } // parse numTris <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numTris" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numTris' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numTriangles = atoi( token ); if ( surf->numTriangles > SHADER_MAX_TRIANGLES ) { ri.Error( ERR_DROP, "R_LoadMD5: '%s' has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, surf->numTriangles ); } surf->triangles = ri.Hunk_Alloc( sizeof( *tri ) * surf->numTriangles, h_low ); for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { // skip tri <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "tri" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'tri' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); for ( k = 0; k < 3; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); tri->indexes[ k ] = atoi( token ); } } // parse numWeights <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "numWeights" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'numWeights' found '%s' in model '%s'\n", token, modName ); return qfalse; } token = COM_ParseExt2( &buf_p, qfalse ); surf->numWeights = atoi( token ); surf->weights = ri.Hunk_Alloc( sizeof( *weight ) * surf->numWeights, h_low ); for ( j = 0, weight = surf->weights; j < surf->numWeights; j++, weight++ ) { // skip weight <number> token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "weight" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected 'weight' found '%s' in model '%s'\n", token, modName ); return qfalse; } COM_ParseExt2( &buf_p, qfalse ); token = COM_ParseExt2( &buf_p, qfalse ); weight->boneIndex = atoi( token ); token = COM_ParseExt2( &buf_p, qfalse ); weight->boneWeight = atof( token ); // skip ( token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, "(" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '(' found '%s' in model '%s'\n", token, modName ); return qfalse; } for ( k = 0; k < 3; k++ ) { token = COM_ParseExt2( &buf_p, qfalse ); weight->offset[ k ] = atof( token ); } // skip ) token = COM_ParseExt2( &buf_p, qfalse ); if ( Q_stricmp( token, ")" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected ')' found '%s' in model '%s'\n", token, modName ); return qfalse; } } // parse } token = COM_ParseExt2( &buf_p, qtrue ); if ( Q_stricmp( token, "}" ) ) { ri.Printf( PRINT_WARNING, "R_LoadMD5: expected '}' found '%s' in model '%s'\n", token, modName ); return qfalse; } // loop trough all vertices and set up the vertex weights for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { v->weights = ri.Hunk_Alloc( sizeof( *v->weights ) * v->numWeights, h_low ); for ( k = 0; k < v->numWeights; k++ ) { v->weights[ k ] = surf->weights + ( v->firstWeight + k ); } } } // loading is done now calculate the bounding box and tangent spaces ClearBounds( md5->bounds[ 0 ], md5->bounds[ 1 ] ); for ( i = 0, surf = md5->surfaces; i < md5->numSurfaces; i++, surf++ ) { for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { vec3_t tmpVert; md5Weight_t *w; VectorClear( tmpVert ); for ( k = 0, w = v->weights[ 0 ]; k < v->numWeights; k++, w++ ) { vec3_t offsetVec; bone = &md5->bones[ w->boneIndex ]; QuatTransformVector( bone->rotation, w->offset, offsetVec ); VectorAdd( bone->origin, offsetVec, offsetVec ); VectorMA( tmpVert, w->boneWeight, offsetVec, tmpVert ); } VectorCopy( tmpVert, v->position ); AddPointToBounds( tmpVert, md5->bounds[ 0 ], md5->bounds[ 1 ] ); } // calc normals { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t normal; for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorClear( v->tangent ); VectorClear( v->binormal ); VectorClear( v->normal ); } for ( j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++ ) { v0 = surf->verts[ tri->indexes[ 0 ] ].position; v1 = surf->verts[ tri->indexes[ 1 ] ].position; v2 = surf->verts[ tri->indexes[ 2 ] ].position; t0 = surf->verts[ tri->indexes[ 0 ] ].texCoords; t1 = surf->verts[ tri->indexes[ 1 ] ].texCoords; t2 = surf->verts[ tri->indexes[ 2 ] ].texCoords; R_CalcNormalForTriangle( normal, v0, v1, v2 ); for ( k = 0; k < 3; k++ ) { float *v; v = surf->verts[ tri->indexes[ k ] ].normal; VectorAdd( v, normal, v ); } } for ( j = 0, v = surf->verts; j < surf->numVerts; j++, v++ ) { VectorNormalize( v->normal ); } } #if 0 // do another extra smoothing for normals to avoid flat shading for ( j = 0; j < surf->numVerts; j++ ) { for ( k = 0; k < surf->numVerts; k++ ) { if ( j == k ) { continue; } if ( VectorCompare( surf->verts[ j ].position, surf->verts[ k ].position ) ) { VectorAdd( surf->verts[ j ].normal, surf->verts[ k ].normal, surf->verts[ j ].normal ); } } VectorNormalize( surf->verts[ j ].normal ); } #endif } return qtrue; }
/* ================= R_CreateSurfaceGridMesh ================= */ static srfGridMesh_t *R_CreateSurfaceGridMesh( int width, int height, srfVert_t ctrl[ MAX_GRID_SIZE ][ MAX_GRID_SIZE ], float errorTable[ 2 ][ MAX_GRID_SIZE ], int numTriangles, srfTriangle_t triangles[ SHADER_MAX_TRIANGLES ] ) { int i, j, size; srfVert_t *vert; vec3_t tmpVec; srfGridMesh_t *grid; // copy the results out to a grid size = sizeof( *grid ); if ( r_stitchCurves->integer ) { grid = (srfGridMesh_t*)/*ri.Hunk_Alloc */ Com_Allocate( size ); Com_Memset( grid, 0, size ); grid->widthLodError = (float*)/*ri.Hunk_Alloc */ Com_Allocate( width * 4 ); Com_Memcpy( grid->widthLodError, errorTable[ 0 ], width * 4 ); grid->heightLodError = (float*)/*ri.Hunk_Alloc */ Com_Allocate( height * 4 ); Com_Memcpy( grid->heightLodError, errorTable[ 1 ], height * 4 ); grid->numTriangles = numTriangles; grid->triangles = (srfTriangle_t*) Com_Allocate( grid->numTriangles * sizeof( srfTriangle_t ) ); Com_Memcpy( grid->triangles, triangles, numTriangles * sizeof( srfTriangle_t ) ); grid->numVerts = ( width * height ); grid->verts = (srfVert_t*) Com_Allocate( grid->numVerts * sizeof( srfVert_t ) ); } else { grid = (srfGridMesh_t*) ri.Hunk_Alloc( size, ha_pref::h_low ); Com_Memset( grid, 0, size ); grid->widthLodError = (float*) ri.Hunk_Alloc( width * 4, ha_pref::h_low ); Com_Memcpy( grid->widthLodError, errorTable[ 0 ], width * 4 ); grid->heightLodError = (float*) ri.Hunk_Alloc( height * 4, ha_pref::h_low ); Com_Memcpy( grid->heightLodError, errorTable[ 1 ], height * 4 ); grid->numTriangles = numTriangles; grid->triangles = (srfTriangle_t*) ri.Hunk_Alloc( grid->numTriangles * sizeof( srfTriangle_t ), ha_pref::h_low ); Com_Memcpy( grid->triangles, triangles, numTriangles * sizeof( srfTriangle_t ) ); grid->numVerts = ( width * height ); grid->verts = (srfVert_t*) ri.Hunk_Alloc( grid->numVerts * sizeof( srfVert_t ), ha_pref::h_low ); } grid->width = width; grid->height = height; grid->surfaceType = surfaceType_t::SF_GRID; ClearBounds( grid->bounds[ 0 ], grid->bounds[ 1 ] ); for ( i = 0; i < width; i++ ) { for ( j = 0; j < height; j++ ) { vert = &grid->verts[ j * width + i ]; *vert = ctrl[ j ][ i ]; AddPointToBounds( vert->xyz, grid->bounds[ 0 ], grid->bounds[ 1 ] ); } } // compute local origin and bounds VectorAdd( grid->bounds[ 0 ], grid->bounds[ 1 ], grid->origin ); VectorScale( grid->origin, 0.5f, grid->origin ); VectorSubtract( grid->bounds[ 0 ], grid->origin, tmpVec ); grid->radius = VectorLength( tmpVec ); VectorCopy( grid->origin, grid->lodOrigin ); grid->lodRadius = grid->radius; // return grid; }
/* * 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 ); }
/* ============== RE_BuildSkeleton ============== */ int RE_BuildSkeleton(refSkeleton_t *skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, qboolean clearOrigin) { skelAnimation_t *skelAnim; skelAnim = R_GetAnimationByHandle(hAnim); if (skelAnim->type == AT_MD5 && skelAnim->md5) { int i; md5Animation_t *anim = skelAnim->md5; md5Channel_t *channel; md5Frame_t *newFrame, *oldFrame; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; int componentsApplied; // Validate the frames so there is no chance of a crash. // This will write directly into the entity structure, so // when the surfaces are rendered, they don't need to be // range checked again. //if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0)) //{ // Ren_Developer( "RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name); // //startFrame = 0; // //endFrame = 0; //} Q_clamp(startFrame, 0, anim->numFrames - 1); Q_clamp(endFrame, 0, anim->numFrames - 1); // compute frame pointers oldFrame = &anim->frames[startFrame]; newFrame = &anim->frames[endFrame]; // calculate a bounding box in the current coordinate system for (i = 0; i < 3; i++) { skel->bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; skel->bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; } for (i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++) { // set baseframe values VectorCopy(channel->baseOrigin, newOrigin); VectorCopy(channel->baseOrigin, oldOrigin); quat_copy(channel->baseQuat, newQuat); quat_copy(channel->baseQuat, oldQuat); componentsApplied = 0; // update tranlation bits if (channel->componentsBits & COMPONENT_BIT_TX) { oldOrigin[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if (channel->componentsBits & COMPONENT_BIT_TY) { oldOrigin[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if (channel->componentsBits & COMPONENT_BIT_TZ) { oldOrigin[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[2] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } // update quaternion rotation bits if (channel->componentsBits & COMPONENT_BIT_QX) { ((vec_t *) oldQuat)[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if (channel->componentsBits & COMPONENT_BIT_QY) { ((vec_t *) oldQuat)[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if (channel->componentsBits & COMPONENT_BIT_QZ) { ((vec_t *) oldQuat)[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[2] = newFrame->components[channel->componentsOffset + componentsApplied]; } QuatCalcW(oldQuat); quat_norm(oldQuat); QuatCalcW(newQuat); quat_norm(newQuat); #if 1 VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); quat_slerp(oldQuat, newQuat, frac, lerpedQuat); #else VectorCopy(newOrigin, lerpedOrigin); quat_copy(newQuat, lerpedQuat); #endif // copy lerped information to the bone + extra data skel->bones[i].parentIndex = channel->parentIndex; if (channel->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } quat_copy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, channel->name, sizeof(skel->bones[i].name)); #endif } skel->numBones = anim->numChannels; skel->type = SK_RELATIVE; return qtrue; } else if (skelAnim->type == AT_PSA && skelAnim->psa) { int i; psaAnimation_t *anim = skelAnim->psa; axAnimationKey_t *newKey, *oldKey; axReferenceBone_t *refBone; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; refSkeleton_t skeleton; Q_clamp(startFrame, 0, anim->info.numRawFrames - 1); Q_clamp(endFrame, 0, anim->info.numRawFrames - 1); ClearBounds(skel->bounds[0], skel->bounds[1]); skel->numBones = anim->info.numBones; for (i = 0, refBone = anim->bones; i < anim->info.numBones; i++, refBone++) { oldKey = &anim->keys[startFrame * anim->info.numBones + i]; newKey = &anim->keys[endFrame * anim->info.numBones + i]; VectorCopy(newKey->position, newOrigin); VectorCopy(oldKey->position, oldOrigin); quat_copy(newKey->quat, newQuat); quat_copy(oldKey->quat, oldQuat); //QuatCalcW(oldQuat); //QuatNormalize(oldQuat); //QuatCalcW(newQuat); //QuatNormalize(newQuat); VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); quat_slerp(oldQuat, newQuat, frac, lerpedQuat); // copy lerped information to the bone + extra data skel->bones[i].parentIndex = refBone->parentIndex; if (refBone->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } quat_copy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, refBone->name, sizeof(skel->bones[i].name)); #endif // calculate absolute values for the bounding box approximation VectorCopy(skel->bones[i].origin, skeleton.bones[i].origin); quat_copy(skel->bones[i].rotation, skeleton.bones[i].rotation); if (refBone->parentIndex >= 0) { vec3_t rotated; quat_t quat; refBone_t *bone = &skeleton.bones[i]; refBone_t *parent = &skeleton.bones[refBone->parentIndex]; QuatTransformVector(parent->rotation, bone->origin, rotated); VectorAdd(parent->origin, rotated, bone->origin); QuatMultiply1(parent->rotation, bone->rotation, quat); quat_copy(quat, bone->rotation); AddPointToBounds(bone->origin, skel->bounds[0], skel->bounds[1]); } } skel->numBones = anim->info.numBones; skel->type = SK_RELATIVE; return qtrue; } //Ren_Warning( "RE_BuildSkeleton: bad animation '%s' with handle %i\n", anim->name, hAnim); // FIXME: clear existing bones and bounds? return qfalse; }
static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style ){ int i, j, k, l, v, x, y, samples; vec3_t color, mins, maxs; vec4_t textureColor; float alpha, alphaI, bf; vec3_t blend; float st[ 2 ], lightmap[ 2 ], *radLuxel; radVert_t *rv[ 3 ]; /* initial setup */ ClearBounds( mins, maxs ); VectorClear( average ); VectorClear( gradient ); alpha = 0; /* dummy check */ if ( rw == NULL || rw->numVerts < 3 ) { return; } /* start sampling */ samples = 0; /* sample vertex colors if no lightmap or this is the initial pass */ if ( lm == NULL || lm->radLuxels[ lightmapNum ] == NULL || bouncing == qfalse ) { for ( samples = 0; samples < rw->numVerts; samples++ ) { /* multiply by texture color */ if ( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, rw->verts[ samples ].st, textureColor ) ) { VectorCopy( si->averageColor, textureColor ); textureColor[ 4 ] = 255.0f; } for ( i = 0; i < 3; i++ ) color[ i ] = ( textureColor[ i ] / 255 ) * ( rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f ); AddPointToBounds( color, mins, maxs ); VectorAdd( average, color, average ); /* get alpha */ alpha += ( textureColor[ 3 ] / 255.0f ) * ( rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f ); } /* set style */ *style = ds->vertexStyles[ lightmapNum ]; } /* sample lightmap */ else { /* fracture the winding into a fan (including degenerate tris) */ for ( v = 1; v < ( rw->numVerts - 1 ) && samples < MAX_SAMPLES; v++ ) { /* get a triangle */ rv[ 0 ] = &rw->verts[ 0 ]; rv[ 1 ] = &rw->verts[ v ]; rv[ 2 ] = &rw->verts[ v + 1 ]; /* this code is embarassing (really should just rasterize the triangle) */ for ( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ ) { for ( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ ) { for ( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ ) { /* create a blend vector (barycentric coordinates) */ blend[ 0 ] = i; blend[ 1 ] = j; blend[ 2 ] = k; bf = ( 1.0 / ( blend[ 0 ] + blend[ 1 ] + blend[ 2 ] ) ); VectorScale( blend, bf, blend ); /* create a blended sample */ st[ 0 ] = st[ 1 ] = 0.0f; lightmap[ 0 ] = lightmap[ 1 ] = 0.0f; alphaI = 0.0f; for ( l = 0; l < 3; l++ ) { st[ 0 ] += ( rv[ l ]->st[ 0 ] * blend[ l ] ); st[ 1 ] += ( rv[ l ]->st[ 1 ] * blend[ l ] ); lightmap[ 0 ] += ( rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ] ); lightmap[ 1 ] += ( rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ] ); alphaI += ( rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ] ); } /* get lightmap xy coords */ x = lightmap[ 0 ] / (float) superSample; y = lightmap[ 1 ] / (float) superSample; if ( x < 0 ) { x = 0; } else if ( x >= lm->w ) { x = lm->w - 1; } if ( y < 0 ) { y = 0; } else if ( y >= lm->h ) { y = lm->h - 1; } /* get radiosity luxel */ radLuxel = RAD_LUXEL( lightmapNum, x, y ); /* ignore unlit/unused luxels */ if ( radLuxel[ 0 ] < 0.0f ) { continue; } /* inc samples */ samples++; /* multiply by texture color */ if ( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, st, textureColor ) ) { VectorCopy( si->averageColor, textureColor ); textureColor[ 4 ] = 255; } for ( i = 0; i < 3; i++ ) color[ i ] = ( textureColor[ i ] / 255 ) * ( radLuxel[ i ] / 255 ); AddPointToBounds( color, mins, maxs ); VectorAdd( average, color, average ); /* get alpha */ alpha += ( textureColor[ 3 ] / 255 ) * ( alphaI / 255 ); } } } } /* set style */ *style = ds->lightmapStyles[ lightmapNum ]; } /* any samples? */ if ( samples <= 0 ) { return; } /* average the color */ VectorScale( average, ( 1.0 / samples ), average ); /* create the color gradient */ //% VectorSubtract( maxs, mins, delta ); /* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */ //% gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f; //% gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f; //% gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f; /* newer: another contrast function */ for ( i = 0; i < 3; i++ ) gradient[ i ] = ( maxs[ i ] - mins[ i ] ) * maxs[ i ]; }
/* ================= R_LoadPSK ================= */ qboolean R_LoadPSK(model_t * mod, byte *buffer, int bufferSize, const char *modName) { int i, j, k; memStream_t *stream; axChunkHeader_t chunkHeader; int numPoints; axPoint_t *point; axPoint_t *points; int numVertexes; axVertex_t *vertex; axVertex_t *vertexes; //int numSmoothGroups; int numTriangles; axTriangle_t *triangle; axTriangle_t *triangles; int numMaterials; axMaterial_t *material; axMaterial_t *materials; int numReferenceBones; axReferenceBone_t *refBone; axReferenceBone_t *refBones; int numWeights; axBoneWeight_t *axWeight; axBoneWeight_t *axWeights; md5Model_t *md5; md5Bone_t *md5Bone; md5Weight_t *weight; vec3_t boneOrigin; quat_t boneQuat; //matrix_t boneMat; int materialIndex, oldMaterialIndex; int numRemaining; growList_t sortedTriangles; growList_t vboVertexes; growList_t vboTriangles; growList_t vboSurfaces; int numBoneReferences; int boneReferences[MAX_BONES]; matrix_t unrealToQuake; //MatrixSetupScale(unrealToQuake, 1, -1, 1); MatrixFromAngles(unrealToQuake, 0, 90, 0); stream = AllocMemStream(buffer, bufferSize); GetChunkHeader(stream, &chunkHeader); // check indent again if(Q_stricmpn(chunkHeader.ident, "ACTRHEAD", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "ACTRHEAD"); FreeMemStream(stream); return qfalse; } PrintChunkHeader(&chunkHeader); mod->type = MOD_MD5; mod->dataSize += sizeof(md5Model_t); md5 = mod->md5 = (md5Model_t*)ri.Hunk_Alloc(sizeof(md5Model_t), h_low); // read points GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "PNTS0000", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "PNTS0000"); FreeMemStream(stream); return qfalse; } if(chunkHeader.dataSize != sizeof(axPoint_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axPoint_t)); FreeMemStream(stream); return qfalse; } PrintChunkHeader(&chunkHeader); numPoints = chunkHeader.numData; points = (axPoint_t*)Com_Allocate(numPoints * sizeof(axPoint_t)); for(i = 0, point = points; i < numPoints; i++, point++) { point->point[0] = MemStreamGetFloat(stream); point->point[1] = MemStreamGetFloat(stream); point->point[2] = MemStreamGetFloat(stream); #if 0 // Tr3B: HACK convert from Unreal coordinate system to the Quake one MatrixTransformPoint2(unrealToQuake, point->point); #endif } // read vertices GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "VTXW0000", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "VTXW0000"); FreeMemStream(stream); Com_Dealloc(points); return qfalse; } if(chunkHeader.dataSize != sizeof(axVertex_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axVertex_t)); FreeMemStream(stream); Com_Dealloc(points); return qfalse; } PrintChunkHeader(&chunkHeader); numVertexes = chunkHeader.numData; vertexes = (axVertex_t*)Com_Allocate(numVertexes * sizeof(axVertex_t)); for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++) { vertex->pointIndex = MemStreamGetShort(stream); if(vertex->pointIndex < 0 || vertex->pointIndex >= numPoints) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has vertex with point index out of range (%i while max %i)\n", modName, vertex->pointIndex, numPoints); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); return qfalse; } vertex->unknownA = MemStreamGetShort(stream); vertex->st[0] = MemStreamGetFloat(stream); vertex->st[1] = MemStreamGetFloat(stream); vertex->materialIndex = MemStreamGetC(stream); vertex->reserved = MemStreamGetC(stream); vertex->unknownB = MemStreamGetShort(stream); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSK: axVertex_t(%i):\n" "axVertex:pointIndex: %i\n" "axVertex:unknownA: %i\n" "axVertex::st: %f %f\n" "axVertex:materialIndex: %i\n" "axVertex:reserved: %d\n" "axVertex:unknownB: %d\n", i, vertex->pointIndex, vertex->unknownA, vertex->st[0], vertex->st[1], vertex->materialIndex, vertex->reserved, vertex->unknownB); #endif } // read triangles GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "FACE0000", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "FACE0000"); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); return qfalse; } if(chunkHeader.dataSize != sizeof(axTriangle_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axTriangle_t)); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); return qfalse; } PrintChunkHeader(&chunkHeader); numTriangles = chunkHeader.numData; triangles = (axTriangle_t*)Com_Allocate(numTriangles * sizeof(axTriangle_t)); for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++) { for(j = 0; j < 3; j++) //for(j = 2; j >= 0; j--) { triangle->indexes[j] = MemStreamGetShort(stream); if(triangle->indexes[j] < 0 || triangle->indexes[j] >= numVertexes) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has triangle with vertex index out of range (%i while max %i)\n", modName, triangle->indexes[j], numVertexes); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); return qfalse; } } triangle->materialIndex = MemStreamGetC(stream); triangle->materialIndex2 = MemStreamGetC(stream); triangle->smoothingGroups = MemStreamGetLong(stream); } // read materials GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "MATT0000", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "MATT0000"); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); return qfalse; } if(chunkHeader.dataSize != sizeof(axMaterial_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axMaterial_t)); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); return qfalse; } PrintChunkHeader(&chunkHeader); numMaterials = chunkHeader.numData; materials = (axMaterial_t*)Com_Allocate(numMaterials * sizeof(axMaterial_t)); for(i = 0, material = materials; i < numMaterials; i++, material++) { MemStreamRead(stream, material->name, sizeof(material->name)); ri.Printf(PRINT_ALL, "R_LoadPSK: material name: '%s'\n", material->name); material->shaderIndex = MemStreamGetLong(stream); material->polyFlags = MemStreamGetLong(stream); material->auxMaterial = MemStreamGetLong(stream); material->auxFlags = MemStreamGetLong(stream); material->lodBias = MemStreamGetLong(stream); material->lodStyle = MemStreamGetLong(stream); } for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++) { if(vertex->materialIndex < 0 || vertex->materialIndex >= numMaterials) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has vertex with material index out of range (%i while max %i)\n", modName, vertex->materialIndex, numMaterials); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); return qfalse; } } for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++) { if(triangle->materialIndex < 0 || triangle->materialIndex >= numMaterials) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has triangle with material index out of range (%i while max %i)\n", modName, triangle->materialIndex, numMaterials); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); return qfalse; } } // read reference bones GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "REFSKELT", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "REFSKELT"); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); return qfalse; } if(chunkHeader.dataSize != sizeof(axReferenceBone_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axReferenceBone_t)); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); return qfalse; } PrintChunkHeader(&chunkHeader); numReferenceBones = chunkHeader.numData; refBones = (axReferenceBone_t*)Com_Allocate(numReferenceBones * sizeof(axReferenceBone_t)); for(i = 0, refBone = refBones; i < numReferenceBones; i++, refBone++) { MemStreamRead(stream, refBone->name, sizeof(refBone->name)); //ri.Printf(PRINT_ALL, "R_LoadPSK: reference bone name: '%s'\n", refBone->name); refBone->flags = MemStreamGetLong(stream); refBone->numChildren = MemStreamGetLong(stream); refBone->parentIndex = MemStreamGetLong(stream); GetBone(stream, &refBone->bone); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSK: axReferenceBone_t(%i):\n" "axReferenceBone_t::name: '%s'\n" "axReferenceBone_t::flags: %i\n" "axReferenceBone_t::numChildren %i\n" "axReferenceBone_t::parentIndex: %i\n" "axReferenceBone_t::quat: %f %f %f %f\n" "axReferenceBone_t::position: %f %f %f\n" "axReferenceBone_t::length: %f\n" "axReferenceBone_t::xSize: %f\n" "axReferenceBone_t::ySize: %f\n" "axReferenceBone_t::zSize: %f\n", i, refBone->name, refBone->flags, refBone->numChildren, refBone->parentIndex, refBone->bone.quat[0], refBone->bone.quat[1], refBone->bone.quat[2], refBone->bone.quat[3], refBone->bone.position[0], refBone->bone.position[1], refBone->bone.position[2], refBone->bone.length, refBone->bone.xSize, refBone->bone.ySize, refBone->bone.zSize); #endif } // read bone weights GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "RAWWEIGHTS", 10)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "RAWWEIGHTS"); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); Com_Dealloc(refBones); return qfalse; } if(chunkHeader.dataSize != sizeof(axBoneWeight_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, sizeof(axBoneWeight_t)); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); Com_Dealloc(refBones); return qfalse; } PrintChunkHeader(&chunkHeader); numWeights = chunkHeader.numData; axWeights = (axBoneWeight_t*)Com_Allocate(numWeights * sizeof(axBoneWeight_t)); for(i = 0, axWeight = axWeights; i < numWeights; i++, axWeight++) { axWeight->weight = MemStreamGetFloat(stream); axWeight->pointIndex = MemStreamGetLong(stream); axWeight->boneIndex = MemStreamGetLong(stream); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSK: axBoneWeight_t(%i):\n" "axBoneWeight_t::weight: %f\n" "axBoneWeight_t::pointIndex %i\n" "axBoneWeight_t::boneIndex: %i\n", i, axWeight->weight, axWeight->pointIndex, axWeight->boneIndex); #endif } // // convert the model to an internal MD5 representation // md5->numBones = numReferenceBones; // calc numMeshes <number> /* numSmoothGroups = 0; for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++) { if(triangle->smoothingGroups) { } } */ if(md5->numBones < 1) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has no bones\n", modName); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); Com_Dealloc(refBones); Com_Dealloc(axWeights); return qfalse; } if(md5->numBones > MAX_BONES) { ri.Printf(PRINT_WARNING, "R_LoadPSK: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); Com_Dealloc(refBones); Com_Dealloc(axWeights); return qfalse; } //ri.Printf(PRINT_ALL, "R_LoadPSK: '%s' has %i bones\n", modName, md5->numBones); // copy all reference bones md5->bones = (md5Bone_t*)ri.Hunk_Alloc(sizeof(*md5Bone) * md5->numBones, h_low); for(i = 0, md5Bone = md5->bones, refBone = refBones; i < md5->numBones; i++, md5Bone++, refBone++) { Q_strncpyz(md5Bone->name, refBone->name, sizeof(md5Bone->name)); if(i == 0) { md5Bone->parentIndex = refBone->parentIndex -1; } else { md5Bone->parentIndex = refBone->parentIndex; } //ri.Printf(PRINT_ALL, "R_LoadPSK: '%s' has bone '%s' with parent index %i\n", modName, md5Bone->name, md5Bone->parentIndex); if(md5Bone->parentIndex >= md5->numBones) { ri.Error(ERR_DROP, "R_LoadPSK: '%s' has bone '%s' with bad parent index %i while numBones is %i\n", modName, md5Bone->name, md5Bone->parentIndex, md5->numBones); } for(j = 0; j < 3; j++) { boneOrigin[j] = refBone->bone.position[j]; } // Tr3B: I have really no idea why the .psk format stores the first quaternion with inverted quats. // Furthermore only the X and Z components of the first quat are inverted ?!?! if(i == 0) { boneQuat[0] = refBone->bone.quat[0]; boneQuat[1] = -refBone->bone.quat[1]; boneQuat[2] = refBone->bone.quat[2]; boneQuat[3] = refBone->bone.quat[3]; } else { boneQuat[0] = -refBone->bone.quat[0]; boneQuat[1] = -refBone->bone.quat[1]; boneQuat[2] = -refBone->bone.quat[2]; boneQuat[3] = refBone->bone.quat[3]; } VectorCopy(boneOrigin, md5Bone->origin); //MatrixTransformPoint(unrealToQuake, boneOrigin, md5Bone->origin); QuatCopy(boneQuat, md5Bone->rotation); //QuatClear(md5Bone->rotation); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSK: md5Bone_t(%i):\n" "md5Bone_t::name: '%s'\n" "md5Bone_t::parentIndex: %i\n" "md5Bone_t::quat: %f %f %f %f\n" "md5bone_t::position: %f %f %f\n", i, md5Bone->name, md5Bone->parentIndex, md5Bone->rotation[0], md5Bone->rotation[1], md5Bone->rotation[2], md5Bone->rotation[3], md5Bone->origin[0], md5Bone->origin[1], md5Bone->origin[2]); #endif if(md5Bone->parentIndex >= 0) { vec3_t rotated; quat_t quat; md5Bone_t *parent; parent = &md5->bones[md5Bone->parentIndex]; QuatTransformVector(parent->rotation, md5Bone->origin, rotated); //QuatTransformVector(md5Bone->rotation, md5Bone->origin, rotated); VectorAdd(parent->origin, rotated, md5Bone->origin); QuatMultiply1(parent->rotation, md5Bone->rotation, quat); QuatCopy(quat, md5Bone->rotation); } MatrixSetupTransformFromQuat(md5Bone->inverseTransform, md5Bone->rotation, md5Bone->origin); MatrixInverse(md5Bone->inverseTransform); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSK: md5Bone_t(%i):\n" "md5Bone_t::name: '%s'\n" "md5Bone_t::parentIndex: %i\n" "md5Bone_t::quat: %f %f %f %f\n" "md5bone_t::position: %f %f %f\n", i, md5Bone->name, md5Bone->parentIndex, md5Bone->rotation[0], md5Bone->rotation[1], md5Bone->rotation[2], md5Bone->rotation[3], md5Bone->origin[0], md5Bone->origin[1], md5Bone->origin[2]); #endif } Com_InitGrowList(&vboVertexes, 10000); for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++) { md5Vertex_t *vboVert = (md5Vertex_t*)Com_Allocate(sizeof(*vboVert)); for(j = 0; j < 3; j++) { vboVert->position[j] = points[vertex->pointIndex].point[j]; } vboVert->texCoords[0] = vertex->st[0]; vboVert->texCoords[1] = vertex->st[1]; // find number of associated weights vboVert->numWeights = 0; for(j = 0, axWeight = axWeights; j < numWeights; j++, axWeight++) { if(axWeight->pointIndex == vertex->pointIndex && axWeight->weight > 0.0f) { vboVert->numWeights++; } } if(vboVert->numWeights > MAX_WEIGHTS) { ri.Error(ERR_DROP, "R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'", i, vboVert->numWeights, MAX_WEIGHTS, modName); //ri.Printf(PRINT_WARNING, "R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'\n", i, vboVert->numWeights, MAX_WEIGHTS, modName); } vboVert->weights = (md5Weight_t**)ri.Hunk_Alloc(sizeof(*vboVert->weights) * vboVert->numWeights, h_low); for(j = 0, axWeight = axWeights, k = 0; j < numWeights; j++, axWeight++) { if(axWeight->pointIndex == vertex->pointIndex && axWeight->weight > 0.0f) { weight = (md5Weight_t*)ri.Hunk_Alloc(sizeof(*weight), h_low); weight->boneIndex = axWeight->boneIndex; weight->boneWeight = axWeight->weight; // FIXME? weight->offset[0] = refBones[axWeight->boneIndex].bone.xSize; weight->offset[1] = refBones[axWeight->boneIndex].bone.ySize; weight->offset[2] = refBones[axWeight->boneIndex].bone.zSize; vboVert->weights[k++] = weight; } } Com_AddToGrowList(&vboVertexes, vboVert); } ClearBounds(md5->bounds[0], md5->bounds[1]); for(i = 0, vertex = vertexes; i < numVertexes; i++, vertex++) { AddPointToBounds(points[vertex->pointIndex].point, md5->bounds[0], md5->bounds[1]); } #if 0 ri.Printf(PRINT_ALL, "R_LoadPSK: AABB (%i %i %i) (%i %i %i)\n", (int)md5->bounds[0][0], (int)md5->bounds[0][1], (int)md5->bounds[0][2], (int)md5->bounds[1][0], (int)md5->bounds[1][1], (int)md5->bounds[1][2]); #endif // sort triangles qsort(triangles, numTriangles, sizeof(axTriangle_t), CompareTrianglesByMaterialIndex); Com_InitGrowList(&sortedTriangles, 1000); for(i = 0, triangle = triangles; i < numTriangles; i++, triangle++) { skelTriangle_t *sortTri = (skelTriangle_t*)Com_Allocate(sizeof(*sortTri)); for(j = 0; j < 3; j++) { sortTri->indexes[j] = triangle->indexes[j]; sortTri->vertexes[j] = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, triangle->indexes[j]); } sortTri->referenced = qfalse; Com_AddToGrowList(&sortedTriangles, sortTri); } // calc tangent spaces #if 1 { md5Vertex_t *v0, *v1, *v2; const float *p0, *p1, *p2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for(j = 0; j < vboVertexes.currentElements; j++) { v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, j); VectorClear(v0->tangent); VectorClear(v0->binormal); VectorClear(v0->normal); } for(j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *tri = (skelTriangle_t*)Com_GrowListElement(&sortedTriangles, j); v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[0]); v1 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[1]); v2 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[2]); p0 = v0->position; p1 = v1->position; p2 = v2->position; t0 = v0->texCoords; t1 = v1->texCoords; t2 = v2->texCoords; #if 1 R_CalcTangentSpace(tangent, binormal, normal, p0, p1, p2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, p0, p1, p2); R_CalcTangentsForTriangle(tangent, binormal, p0, p1, p2, t0, t1, t2); #endif for(k = 0; k < 3; k++) { float *v; v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, tri->indexes[k]); v = v0->tangent; VectorAdd(v, tangent, v); v = v0->binormal; VectorAdd(v, binormal, v); v = v0->normal; VectorAdd(v, normal, v); } } for(j = 0; j < vboVertexes.currentElements; j++) { v0 = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, j); VectorNormalize(v0->tangent); VectorNormalize(v0->binormal); VectorNormalize(v0->normal); } } #else { float bb, s, t; vec3_t bary; vec3_t faceNormal; md5Vertex_t *dv[3]; for(j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *tri = Com_GrowListElement(&sortedTriangles, j); dv[0] = Com_GrowListElement(&vboVertexes, tri->indexes[0]); dv[1] = Com_GrowListElement(&vboVertexes, tri->indexes[1]); dv[2] = Com_GrowListElement(&vboVertexes, tri->indexes[2]); R_CalcNormalForTriangle(faceNormal, dv[0]->position, dv[1]->position, dv[2]->position); // calculate barycentric basis for the triangle bb = (dv[1]->texCoords[0] - dv[0]->texCoords[0]) * (dv[2]->texCoords[1] - dv[0]->texCoords[1]) - (dv[2]->texCoords[0] - dv[0]->texCoords[0]) * (dv[1]->texCoords[1] - dv[0]->texCoords[1]); if(fabs(bb) < 0.00000001f) continue; // do each vertex for(k = 0; k < 3; k++) { // calculate s tangent vector s = dv[k]->texCoords[0] + 10.0f; t = dv[k]->texCoords[1]; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->tangent[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->tangent[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->tangent[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->tangent, dv[k]->position, dv[k]->tangent); VectorNormalize(dv[k]->tangent); // calculate t tangent vector (binormal) s = dv[k]->texCoords[0]; t = dv[k]->texCoords[1] + 10.0f; bary[0] = ((dv[1]->texCoords[0] - s) * (dv[2]->texCoords[1] - t) - (dv[2]->texCoords[0] - s) * (dv[1]->texCoords[1] - t)) / bb; bary[1] = ((dv[2]->texCoords[0] - s) * (dv[0]->texCoords[1] - t) - (dv[0]->texCoords[0] - s) * (dv[2]->texCoords[1] - t)) / bb; bary[2] = ((dv[0]->texCoords[0] - s) * (dv[1]->texCoords[1] - t) - (dv[1]->texCoords[0] - s) * (dv[0]->texCoords[1] - t)) / bb; dv[k]->binormal[0] = bary[0] * dv[0]->position[0] + bary[1] * dv[1]->position[0] + bary[2] * dv[2]->position[0]; dv[k]->binormal[1] = bary[0] * dv[0]->position[1] + bary[1] * dv[1]->position[1] + bary[2] * dv[2]->position[1]; dv[k]->binormal[2] = bary[0] * dv[0]->position[2] + bary[1] * dv[1]->position[2] + bary[2] * dv[2]->position[2]; VectorSubtract(dv[k]->binormal, dv[k]->position, dv[k]->binormal); VectorNormalize(dv[k]->binormal); // calculate the normal as cross product N=TxB #if 0 CrossProduct(dv[k]->tangent, dv[k]->binormal, dv[k]->normal); VectorNormalize(dv[k]->normal); // Gram-Schmidt orthogonalization process for B // compute the cross product B=NxT to obtain // an orthogonal basis CrossProduct(dv[k]->normal, dv[k]->tangent, dv[k]->binormal); if(DotProduct(dv[k]->normal, faceNormal) < 0) { VectorInverse(dv[k]->normal); //VectorInverse(dv[k]->tangent); //VectorInverse(dv[k]->binormal); } #else VectorAdd(dv[k]->normal, faceNormal, dv[k]->normal); #endif } } #if 1 for(j = 0; j < vboVertexes.currentElements; j++) { dv[0] = Com_GrowListElement(&vboVertexes, j); //VectorNormalize(dv[0]->tangent); //VectorNormalize(dv[0]->binormal); VectorNormalize(dv[0]->normal); } #endif } #endif #if 0 { md5Vertex_t *v0, *v1; // do another extra smoothing for normals to avoid flat shading for(j = 0; j < vboVertexes.currentElements; j++) { v0 = Com_GrowListElement(&vboVertexes, j); for(k = 0; k < vboVertexes.currentElements; k++) { if(j == k) continue; v1 = Com_GrowListElement(&vboVertexes, k); if(VectorCompare(v0->position, v1->position)) { VectorAdd(v0->position, v1->normal, v0->normal); } } VectorNormalize(v0->normal); } } #endif // split the surfaces into VBO surfaces by the maximum number of GPU vertex skinning bones Com_InitGrowList(&vboSurfaces, 10); materialIndex = oldMaterialIndex = -1; for(i = 0; i < numTriangles; i++) { triangle = &triangles[i]; materialIndex = triangle->materialIndex; if(materialIndex != oldMaterialIndex) { oldMaterialIndex = materialIndex; numRemaining = sortedTriangles.currentElements - i; while(numRemaining) { numBoneReferences = 0; Com_Memset(boneReferences, 0, sizeof(boneReferences)); Com_InitGrowList(&vboTriangles, 1000); for(j = i; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri; triangle = &triangles[j]; materialIndex = triangle->materialIndex; if(materialIndex != oldMaterialIndex) continue; sortTri = (skelTriangle_t*)Com_GrowListElement(&sortedTriangles, j); if(sortTri->referenced) continue; if(AddTriangleToVBOTriangleList(&vboTriangles, sortTri, &numBoneReferences, boneReferences)) { sortTri->referenced = qtrue; } } for(j = 0; j < MAX_BONES; j++) { if(boneReferences[j] > 0) { ri.Printf(PRINT_ALL, "R_LoadPSK: referenced bone: '%s'\n", (j < numReferenceBones) ? refBones[j].name : NULL); } } if(!vboTriangles.currentElements) { ri.Printf(PRINT_WARNING, "R_LoadPSK: could not add triangles to a remaining VBO surface for model '%s'\n", modName); break; } // FIXME skinIndex AddSurfaceToVBOSurfacesList2(&vboSurfaces, &vboTriangles, &vboVertexes, md5, vboSurfaces.currentElements, materials[oldMaterialIndex].name, numBoneReferences, boneReferences); numRemaining -= vboTriangles.currentElements; Com_DestroyGrowList(&vboTriangles); } } } for(j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *sortTri = (skelTriangle_t*)Com_GrowListElement(&sortedTriangles, j); Com_Dealloc(sortTri); } Com_DestroyGrowList(&sortedTriangles); for(j = 0; j < vboVertexes.currentElements; j++) { md5Vertex_t *v = (md5Vertex_t*)Com_GrowListElement(&vboVertexes, j); Com_Dealloc(v); } Com_DestroyGrowList(&vboVertexes); // move VBO surfaces list to hunk md5->numVBOSurfaces = vboSurfaces.currentElements; md5->vboSurfaces = (srfVBOMD5Mesh_t**)ri.Hunk_Alloc(md5->numVBOSurfaces * sizeof(*md5->vboSurfaces), h_low); for(i = 0; i < md5->numVBOSurfaces; i++) { md5->vboSurfaces[i] = (srfVBOMD5Mesh_t *) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); Com_Dealloc(refBones); Com_Dealloc(axWeights); ri.Printf(PRINT_ALL, "%i VBO surfaces created for PSK model '%s'\n", md5->numVBOSurfaces, modName); return qtrue; }
static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, qboolean original, radWinding_t *rw, clipWork_t *cw ){ int i, style; float dist, area, value; vec3_t mins, maxs, normal, d1, d2, cross, color, gradient; light_t *light, *splash; winding_t *w; /* dummy check */ if ( rw == NULL || rw->numVerts < 3 ) { return; } /* get bounds for winding */ ClearBounds( mins, maxs ); for ( i = 0; i < rw->numVerts; i++ ) AddPointToBounds( rw->verts[ i ].xyz, mins, maxs ); /* subdivide if necessary */ for ( i = 0; i < 3; i++ ) { if ( maxs[ i ] - mins[ i ] > subdivide ) { radWinding_t front, back; /* make axial plane */ VectorClear( normal ); normal[ i ] = 1; dist = ( maxs[ i ] + mins[ i ] ) * 0.5f; /* clip the winding */ RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw ); /* recurse */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &front, cw ); RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &back, cw ); return; } } /* check area */ area = 0.0f; for ( i = 2; i < rw->numVerts; i++ ) { VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 ); VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 ); CrossProduct( d1, d2, cross ); area += 0.5f * VectorLength( cross ); } if ( area < 1.0f || area > 20000000.0f ) { return; } /* more subdivision may be necessary */ if ( bouncing ) { /* get color sample for the surface fragment */ RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style ); /* if color gradient is too high, subdivide again */ if ( subdivide > minDiffuseSubdivide && ( gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT ) ) { RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, ( subdivide / 2.0f ), qfalse, rw, cw ); return; } } /* create a regular winding and an average normal */ w = AllocWinding( rw->numVerts ); w->numpoints = rw->numVerts; VectorClear( normal ); for ( i = 0; i < rw->numVerts; i++ ) { VectorCopy( rw->verts[ i ].xyz, w->p[ i ] ); VectorAdd( normal, rw->verts[ i ].normal, normal ); } VectorScale( normal, ( 1.0f / rw->numVerts ), normal ); if ( VectorNormalize( normal, normal ) == 0.0f ) { return; } /* early out? */ if ( bouncing && VectorLength( color ) < RADIOSITY_MIN ) { return; } /* debug code */ //% Sys_Printf( "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) ); //% Sys_Printf( "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] ); /* increment counts */ numDiffuseLights++; switch ( ds->surfaceType ) { case MST_PLANAR: numBrushDiffuseLights++; break; case MST_TRIANGLE_SOUP: numTriangleDiffuseLights++; break; case MST_PATCH: numPatchDiffuseLights++; break; } /* create a light */ light = safe_malloc( sizeof( *light ) ); memset( light, 0, sizeof( *light ) ); /* attach it */ ThreadLock(); light->next = lights; lights = light; ThreadUnlock(); /* initialize the light */ light->flags = LIGHT_AREA_DEFAULT; light->type = EMIT_AREA; light->si = si; light->fade = 1.0f; light->w = w; /* set falloff threshold */ light->falloffTolerance = falloffTolerance; /* bouncing light? */ if ( bouncing == qfalse ) { /* handle first-pass lights in normal q3a style */ value = si->value; light->photons = value * area * areaScale; light->add = value * formFactorValueScale * areaScale; VectorCopy( si->color, light->color ); VectorScale( light->color, light->add, light->emitColor ); light->style = si->lightStyle; if ( light->style < 0 || light->style >= LS_NONE ) { light->style = 0; } /* set origin */ VectorAdd( mins, maxs, light->origin ); VectorScale( light->origin, 0.5f, light->origin ); /* nudge it off the plane a bit */ VectorCopy( normal, light->normal ); VectorMA( light->origin, 1.0f, light->normal, light->origin ); light->dist = DotProduct( light->origin, normal ); /* optionally create a point splashsplash light for first pass */ if ( original && si->backsplashFraction > 0 ) { /* allocate a new point light */ splash = safe_malloc( sizeof( *splash ) ); memset( splash, 0, sizeof( *splash ) ); splash->next = lights; lights = splash; /* set it up */ splash->flags = LIGHT_Q3A_DEFAULT; splash->type = EMIT_POINT; splash->photons = light->photons * si->backsplashFraction; splash->fade = 1.0f; splash->si = si; VectorMA( light->origin, si->backsplashDistance, normal, splash->origin ); VectorCopy( si->color, splash->color ); splash->falloffTolerance = falloffTolerance; splash->style = light->style; /* add to counts */ numPointLights++; } } else { /* handle bounced light (radiosity) a little differently */ value = RADIOSITY_VALUE * si->bounceScale * 0.375f; light->photons = value * area * bounceScale; light->add = value * formFactorValueScale * bounceScale; VectorCopy( color, light->color ); VectorScale( light->color, light->add, light->emitColor ); light->style = style; if ( light->style < 0 || light->style >= LS_NONE ) { light->style = 0; } /* set origin */ WindingCenter( w, light->origin ); /* nudge it off the plane a bit */ VectorCopy( normal, light->normal ); VectorMA( light->origin, 1.0f, light->normal, light->origin ); light->dist = DotProduct( light->origin, normal ); } /* emit light from both sides? */ if ( si->compileFlags & C_FOG || si->twoSided ) { light->flags |= LIGHT_TWOSIDED; } //% Sys_Printf( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n", //% light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add, //% light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ], //% light->si->shader ); }
/* =================== CM_GeneratePatchCollide Creates an internal structure that will be used to perform collision detection with a patch mesh. Points is packed as concatenated rows. =================== */ struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ) { patchCollide_t *pf; MAC_STATIC cGrid_t grid; int i, j; if ( width <= 2 || height <= 2 || !points ) { Com_Error( ERR_DROP, "CM_GeneratePatchFacets: bad parameters: (%i, %i, %p)", width, height, points ); } if ( !(width & 1) || !(height & 1) ) { Com_Error( ERR_DROP, "CM_GeneratePatchFacets: even sizes are invalid for quadratic meshes" ); } if ( width > MAX_GRID_SIZE || height > MAX_GRID_SIZE ) { Com_Error( ERR_DROP, "CM_GeneratePatchFacets: source is > MAX_GRID_SIZE" ); } // build a grid grid.width = width; grid.height = height; grid.wrapWidth = false; grid.wrapHeight = false; for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { VectorCopy( points[j*width + i], grid.points[i][j] ); } } // subdivide the grid CM_SetGridWrapWidth( &grid ); CM_SubdivideGridColumns( &grid ); CM_RemoveDegenerateColumns( &grid ); CM_TransposeGrid( &grid ); CM_SetGridWrapWidth( &grid ); CM_SubdivideGridColumns( &grid ); CM_RemoveDegenerateColumns( &grid ); // we now have a grid of points exactly on the curve // the aproximate surface defined by these points will be // collided against pf = reinterpret_cast<patchCollide_t*>(Hunk_Alloc( sizeof( *pf ), h_high )); ClearBounds( pf->bounds[0], pf->bounds[1] ); for ( i = 0 ; i < grid.width ; i++ ) { for ( j = 0 ; j < grid.height ; j++ ) { AddPointToBounds( grid.points[i][j], pf->bounds[0], pf->bounds[1] ); } } c_totalPatchBlocks += ( grid.width - 1 ) * ( grid.height - 1 ); // generate a bsp tree for the surface CM_PatchCollideFromGrid( &grid, pf ); // expand by one unit for epsilon purposes pf->bounds[0][0] -= 1; pf->bounds[0][1] -= 1; pf->bounds[0][2] -= 1; pf->bounds[1][0] += 1; pf->bounds[1][1] += 1; pf->bounds[1][2] += 1; return pf; }
/* =============== ParseTriSurf =============== */ static void ParseTriSurf( dsurface_t *ds, mapVert_t *verts, msurface_t *surf, int *indexes, world_t &worldData, int index ) { srfTriangles_t *tri; int i, j, k; int numVerts, numIndexes; // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; if (index && !surf->fogIndex && tr.world && tr.world->globalFog != -1) { surf->fogIndex = worldData.globalFog; } // get shader surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapsVertex, ds->lightmapStyles, ds->vertexStyles, worldData ); if ( r_singleShader->integer && !surf->shader->sky ) { surf->shader = tr.defaultShader; } numVerts = LittleLong( ds->numVerts ); numIndexes = LittleLong( ds->numIndexes ); if ( numVerts >= SHADER_MAX_VERTEXES ) { Com_Error(ERR_DROP, "ParseTriSurf: verts > MAX (%d > %d) on misc_model %s", numVerts, SHADER_MAX_VERTEXES, surf->shader->name ); } if ( numIndexes >= SHADER_MAX_INDEXES ) { Com_Error(ERR_DROP, "ParseTriSurf: indices > MAX (%d > %d) on misc_model %s", numIndexes, SHADER_MAX_INDEXES, surf->shader->name ); } tri = (srfTriangles_t *) Z_Malloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) + numIndexes * sizeof( tri->indexes[0] ), TAG_HUNKMISCMODELS, qfalse ); tri->dlightBits = 0; //JIC tri->surfaceType = SF_TRIANGLES; tri->numVerts = numVerts; tri->numIndexes = numIndexes; tri->verts = (drawVert_t *)(tri + 1); tri->indexes = (int *)(tri->verts + tri->numVerts ); surf->data = (surfaceType_t *)tri; // copy vertexes verts += LittleLong( ds->firstVert ); ClearBounds( tri->bounds[0], tri->bounds[1] ); for ( i = 0 ; i < numVerts ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] ); } AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); for ( j = 0 ; j < 2 ; j++ ) { tri->verts[i].st[j] = LittleFloat( verts[i].st[j] ); for(k=0; k<MAXLIGHTMAPS; k++) { tri->verts[i].lightmap[k][j] = LittleFloat( verts[i].lightmap[k][j] ); } } for(k=0; k<MAXLIGHTMAPS; k++) { R_ColorShiftLightingBytes( verts[i].color[k], tri->verts[i].color[k] ); } } // copy indexes indexes += LittleLong( ds->firstIndex ); for ( i = 0 ; i < numIndexes ; i++ ) { tri->indexes[i] = LittleLong( indexes[i] ); if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { Com_Error( ERR_DROP, "Bad index in triangle surface" ); } } }
/* =============== GrabFrame =============== */ void GrabFrame (char *frame) { triangle_t *ptri; int i, j; trivert_t *ptrivert; int num_tris; char file1[1024]; frame_t *fr; vertexnormals_t vnorms[MAX_VERTS]; int index_xyz; char *framefile; // the frame 'run1' will be looked for as either // run.1 or run1.tri, so the new alias sequence save // feature an be used framefile = FindFrameFile (frame); sprintf (file1, "%s/%s", cdarchive, framefile); ExpandPathAndArchive (file1); sprintf (file1, "%s/%s",cddir, framefile); printf ("grabbing %s\n", file1); if (model.num_frames >= MAX_FRAMES) Error ("model.num_frames >= MAX_FRAMES"); fr = &g_frames[model.num_frames]; model.num_frames++; strcpy (fr->name, frame); // // load the frame // if (do3ds) Load3DSTriangleList (file1, &ptri, &num_tris); else LoadTriangleList (file1, &ptri, &num_tris); if (num_tris != model.num_tris) Error ("%s: number of triangles doesn't match base frame\n", file1); // // allocate storage for the frame's vertices // ptrivert = fr->v; for (i=0 ; i<model.num_xyz ; i++) { vnorms[i].numnormals = 0; VectorClear (vnorms[i].normalsum); } ClearBounds (fr->mins, fr->maxs); // // store the frame's vertices in the same order as the base. This assumes the // triangles and vertices in this frame are in exactly the same order as in the // base // for (i=0 ; i<num_tris ; i++) { vec3_t vtemp1, vtemp2, normal; float ftemp; VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1); VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2); CrossProduct (vtemp1, vtemp2, normal); VectorNormalize2 (normal, normal); // rotate the normal so the model faces down the positive x axis ftemp = normal[0]; normal[0] = -normal[1]; normal[1] = ftemp; for (j=0 ; j<3 ; j++) { index_xyz = triangles[i].index_xyz[j]; // rotate the vertices so the model faces down the positive x axis // also adjust the vertices to the desired origin ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) + adjust[0]; ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) + adjust[1]; ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) + adjust[2]; AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs); VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum); vnorms[index_xyz].numnormals++; } } // // calculate the vertex normals, match them to the template list, and store the // index of the best match // for (i=0 ; i<model.num_xyz ; i++) { int j; vec3_t v; float maxdot; int maxdotindex; int c; c = vnorms[i].numnormals; if (!c) Error ("Vertex with no triangles attached"); VectorScale (vnorms[i].normalsum, 1.0/c, v); VectorNormalize2 (v, v); maxdot = -999999.0; maxdotindex = -1; for (j=0 ; j<NUMVERTEXNORMALS ; j++) { float dot; dot = DotProduct (v, avertexnormals[j]); if (dot > maxdot) { maxdot = dot; maxdotindex = j; } } ptrivert[i].lightnormalindex = maxdotindex; } free (ptri); }
/* ===================== RE_AddPolysToScene ===================== */ void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; int i; int fogIndex; fog_t *fog; vec3_t bounds[ 2 ]; int j; if ( !tr.registered ) { return; } if ( !hShader ) { ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolysToScene: NULL poly shader\n" ); return; } for ( j = 0; j < numPolys; j++ ) { if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) { // ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolysToScene: MAX_POLYS or MAX_POLYVERTS reached\n"); return; } poly = &backEndData[ tr.smpFrame ]->polys[ r_numpolys ]; poly->surfaceType = SF_POLY; poly->hShader = hShader; poly->numVerts = numVerts; poly->verts = &backEndData[ tr.smpFrame ]->polyVerts[ r_numpolyverts ]; memcpy( poly->verts, &verts[ numVerts * j ], numVerts * sizeof( *verts ) ); // Ridah if ( glConfig.hardwareType == GLHW_RAGEPRO ) { poly->verts->modulate[ 0 ] = 255; poly->verts->modulate[ 1 ] = 255; poly->verts->modulate[ 2 ] = 255; poly->verts->modulate[ 3 ] = 255; } // done. r_numpolys++; r_numpolyverts += numVerts; // if no world is loaded if ( tr.world == NULL ) { fogIndex = 0; } // see if it is in a fog volume else if ( tr.world->numfogs == 1 ) { fogIndex = 0; } else { // find which fog volume the poly is in VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] ); VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] ); for ( i = 1; i < poly->numVerts; i++ ) { AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] ); } for ( fogIndex = 1; fogIndex < tr.world->numfogs; fogIndex++ ) { fog = &tr.world->fogs[ fogIndex ]; if ( bounds[ 1 ][ 0 ] >= fog->bounds[ 0 ][ 0 ] && bounds[ 1 ][ 1 ] >= fog->bounds[ 0 ][ 1 ] && bounds[ 1 ][ 2 ] >= fog->bounds[ 0 ][ 2 ] && bounds[ 0 ][ 0 ] <= fog->bounds[ 1 ][ 0 ] && bounds[ 0 ][ 1 ] <= fog->bounds[ 1 ][ 1 ] && bounds[ 0 ][ 2 ] <= fog->bounds[ 1 ][ 2 ] ) { break; } } if ( fogIndex == tr.world->numfogs ) { fogIndex = 0; } } poly->fogIndex = fogIndex; } }
void Select_AutoCaulk() { /*Sys_Printf*/common->Printf("Caulking...\n"); FacesToCaulk.Clear(); int iSystemBrushesSkipped = 0; face_t *pSelectedFace; brush_t *next; for (brush_t *pSelectedBrush = selected_brushes.next ; pSelectedBrush != &selected_brushes ; pSelectedBrush = next) { next = pSelectedBrush->next; if (pSelectedBrush->owner->eclass->fixedsize) continue; // apparently this means it's a model, so skip it... // new check, we can't caulk a brush that has any "system/" faces... // bool bSystemFacePresent = false; for ( pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next) { if (!strnicmp(pSelectedFace->d_texture->GetName(),"system/",7)) { bSystemFacePresent = true; break; } } if (bSystemFacePresent) { iSystemBrushesSkipped++; continue; // verboten to caulk this. } for (int iBrushListToScan = 0; iBrushListToScan<2; iBrushListToScan++) { brush_t *snext; for (brush_t *pScannedBrush = (iBrushListToScan?active_brushes.next:selected_brushes.next); pScannedBrush != (iBrushListToScan?&active_brushes:&selected_brushes) ; pScannedBrush = snext) { snext = pScannedBrush->next; if ( pScannedBrush == pSelectedBrush) continue; if (pScannedBrush->owner->eclass->fixedsize || pScannedBrush->pPatch || pScannedBrush->hiddenBrush) continue; if (FilterBrush(pScannedBrush)) continue; // idMaterial stuff no longer support this, not sure what else to do. // Searching for other occurences of QER_NOCARVE just shows people REMing the code and ignoring ths issue... // // if (pScannedBrush->brush_faces->d_texture->bFromShader && (pScannedBrush->brush_faces->d_texture->TestMaterialFlag(QER_NOCARVE))) // continue; // basic-reject first to see if brushes can even possibly touch (coplanar counts as touching) // int i; for (i=0 ; i<3 ; i++) { if (pSelectedBrush->mins[i] > pScannedBrush->maxs[i] || pSelectedBrush->maxs[i] < pScannedBrush->mins[i]) { break; } } if (i != 3) continue; // can't be touching // ok, now for the clever stuff, we need to detect only those faces that are both coplanar and smaller // or equal to the face they're coplanar with... // for (pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next) { idWinding *pSelectedWinding = pSelectedFace->face_winding; if (!pSelectedWinding) continue; // freed face, probably won't happen here, but who knows with this program? // SquaredFace_t SelectedSquaredFace; // WindingToSquaredFace( &SelectedSquaredFace, pSelectedWinding); for (face_t *pScannedFace = pScannedBrush->brush_faces; pScannedFace; pScannedFace = pScannedFace->next) { // don't even try caulking against a system face, because these are often transparent and will leave holes // if (!strnicmp(pScannedFace->d_texture->GetName(),"system/",7)) continue; // and don't try caulking against something inherently transparent... // if (pScannedFace->d_texture->TestMaterialFlag(QER_TRANS)) continue; idWinding *pScannedWinding = pScannedFace->face_winding; if (!pScannedWinding) continue; // freed face, probably won't happen here, but who knows with this program? // SquaredFace_t ScannedSquaredFace; // WindingToSquaredFace( &ScannedSquaredFace, pScannedWinding); /* if (VectorCompare(ScannedSquaredFace.v3NormalisedRotationVector, SelectedSquaredFace.v3NormalisedRotationVector) && VectorCompare(ScannedSquaredFace.v3NormalisedElevationVector, SelectedSquaredFace.v3NormalisedElevationVector) ) */ { // brush faces are in parallel planes to each other, so check that their normals // are opposite, by adding them together and testing for zero... // (if normals are opposite, then faces can be against/touching each other?) // idVec3 v3ZeroTest; idVec3 v3Zero;v3Zero.Zero(); //static idVec3 v3Zero={0,0,0}; VectorAdd(pSelectedFace->plane.Normal(),pScannedFace->plane.Normal(),v3ZeroTest); if (v3ZeroTest == v3Zero) { // planes are facing each other... // // coplanar? (this is some maths of Gil's, which I don't even pretend to understand) // float fTotalDist = 0; for (int _i=0; _i<3; _i++) { fTotalDist += fabs( DotProduct(pSelectedFace->plane.Normal(),(*pSelectedWinding)[0]) - DotProduct(pSelectedFace->plane.Normal(),(*pScannedWinding)[i]) ); } //OutputDebugString(va("Dist = %g\n",fTotalDist)); if (fTotalDist > 0.01) continue; // every point in the selected face must be within (or equal to) the bounds of the // scanned face... // // work out the bounds first... // idVec3 v3ScannedBoundsMins, v3ScannedBoundsMaxs; ClearBounds (v3ScannedBoundsMins, v3ScannedBoundsMaxs); int iPoint; for (iPoint=0; iPoint<pScannedWinding->GetNumPoints(); iPoint++) { AddPointToBounds( (*pScannedWinding)[iPoint].ToVec3(), v3ScannedBoundsMins, v3ScannedBoundsMaxs); } // floor 'em... (or .001 differences mess things up... // FloorBounds(v3ScannedBoundsMins, v3ScannedBoundsMaxs); // now check points from selected face... // bool bWithin = true; for (iPoint=0; iPoint < pSelectedWinding->GetNumPoints(); iPoint++) { for (int iXYZ=0; iXYZ<3; iXYZ++) { float f = floor((*pSelectedWinding)[iPoint][iXYZ] + 0.5); if (! ( f >= v3ScannedBoundsMins[iXYZ] && f <= v3ScannedBoundsMaxs[iXYZ] ) ) { bWithin = false; } } } if (bWithin) { PairBrushFace_t PairBrushFace; PairBrushFace.pFace = pSelectedFace; PairBrushFace.pBrush= pSelectedBrush; FacesToCaulk.Append(PairBrushFace); } } } } } } } } // apply caulk... // int iFacesCaulked = 0; if (FacesToCaulk.Num()) { LPCSTR psCaulkName = "textures/common/caulk"; const idMaterial *pCaulk = Texture_ForName(psCaulkName); if (pCaulk) { // // and call some other junk that Radiant wants so so we can use it later... // texdef_t tex; memset (&tex, 0, sizeof(tex)); tex.scale[0] = 1; tex.scale[1] = 1; //tex.flags = pCaulk->flags; // field missing in Q4 //tex.value = pCaulk->value; // ditto //tex.contents = pCaulk->contents; // ditto tex.SetName( pCaulk->GetName() ); //Texture_SetTexture (&tex); for (int iListEntry = 0; iListEntry < FacesToCaulk.Num(); iListEntry++) { PairBrushFace_t &PairBrushFace = FacesToCaulk[iListEntry]; face_t *pFace = PairBrushFace.pFace; brush_t*pBrush= PairBrushFace.pBrush; pFace->d_texture = pCaulk; pFace->texdef = tex; Face_FitTexture(pFace, 1, 1); // this doesn't work here for some reason... duh. Brush_Build(pBrush); iFacesCaulked++; } } else { /*Sys_Printf*/common->Printf(" Unable to locate caulk texture at: \"%s\"!\n",psCaulkName); } } /*Sys_Printf*/common->Printf("( %d faces caulked )\n",iFacesCaulked); if (iSystemBrushesSkipped) { /*Sys_Printf*/common->Printf("( %d system-faced brushes skipped )\n",iSystemBrushesSkipped); } Sys_UpdateWindows (W_ALL); }
/* RE_ProjectDecal() 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 */ 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; int i; float radius, iDist; vec3_t xyz; decalVert_t dv[4]; 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_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); 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; // fixme: stale refdef time 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(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])) { 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)) { 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 = &backEndData->decalProjectors[r_numDecalProjectors]; Com_Memcpy(dp, &temp, sizeof(*dp)); dp->projectorNum = totalProjectors++; // we have a winner r_numDecalProjectors++; }
/* * Mod_LoadAliasMD3Model */ void Mod_LoadAliasMD3Model( model_t *mod, model_t *parent, void *buffer, bspFormatDesc_t *unused ) { int version, i, j, l; int bufsize, numverts; qbyte *buf; dmd3header_t *pinmodel; dmd3frame_t *pinframe; dmd3tag_t *pintag; dmd3mesh_t *pinmesh; dmd3skin_t *pinskin; dmd3coord_t *pincoord; dmd3vertex_t *pinvert; unsigned int *pinelem; elem_t *poutelem; maliasvertex_t *poutvert; vec2_t *poutcoord; maliasskin_t *poutskin; maliasmesh_t *poutmesh; maliastag_t *pouttag; maliasframe_t *poutframe; maliasmodel_t *poutmodel; drawSurfaceAlias_t *drawSurf; pinmodel = ( dmd3header_t * )buffer; version = LittleLong( pinmodel->version ); if( version != MD3_ALIAS_VERSION ) ri.Com_Error( ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, version, MD3_ALIAS_VERSION ); mod->type = mod_alias; mod->extradata = poutmodel = ( maliasmodel_t * )Mod_Malloc( mod, sizeof( maliasmodel_t ) ); mod->radius = 0; mod->registrationSequence = rsh.registrationSequence; mod->touch = &Mod_TouchAliasModel; ClearBounds( mod->mins, mod->maxs ); // byte swap the header fields and sanity check poutmodel->numframes = LittleLong( pinmodel->num_frames ); poutmodel->numtags = LittleLong( pinmodel->num_tags ); poutmodel->nummeshes = LittleLong( pinmodel->num_meshes ); poutmodel->numskins = 0; if( poutmodel->numframes <= 0 ) ri.Com_Error( ERR_DROP, "model %s has no frames", mod->name ); // else if( poutmodel->numframes > MD3_MAX_FRAMES ) // ri.Com_Error( ERR_DROP, "model %s has too many frames", mod->name ); if( poutmodel->numtags > MD3_MAX_TAGS ) ri.Com_Error( ERR_DROP, "model %s has too many tags", mod->name ); else if( poutmodel->numtags < 0 ) ri.Com_Error( ERR_DROP, "model %s has invalid number of tags", mod->name ); if( poutmodel->nummeshes < 0 ) ri.Com_Error( ERR_DROP, "model %s has invalid number of meshes", mod->name ); else if( !poutmodel->nummeshes && !poutmodel->numtags ) ri.Com_Error( ERR_DROP, "model %s has no meshes and no tags", mod->name ); // else if( poutmodel->nummeshes > MD3_MAX_MESHES ) // ri.Com_Error( ERR_DROP, "model %s has too many meshes", mod->name ); bufsize = poutmodel->numframes * ( sizeof( maliasframe_t ) + sizeof( maliastag_t ) * poutmodel->numtags ) + poutmodel->nummeshes * sizeof( maliasmesh_t ) + poutmodel->nummeshes * sizeof( drawSurfaceAlias_t ); buf = ( qbyte * )Mod_Malloc( mod, bufsize ); // // load the frames // pinframe = ( dmd3frame_t * )( ( qbyte * )pinmodel + LittleLong( pinmodel->ofs_frames ) ); poutframe = poutmodel->frames = ( maliasframe_t * )buf; buf += sizeof( maliasframe_t ) * poutmodel->numframes; for( i = 0; i < poutmodel->numframes; i++, pinframe++, poutframe++ ) { for( j = 0; j < 3; j++ ) { poutframe->scale[j] = MD3_XYZ_SCALE; poutframe->translate[j] = LittleFloat( pinframe->translate[j] ); } // never trust the modeler utility and recalculate bbox and radius ClearBounds( poutframe->mins, poutframe->maxs ); } // // load the tags // pintag = ( dmd3tag_t * )( ( qbyte * )pinmodel + LittleLong( pinmodel->ofs_tags ) ); pouttag = poutmodel->tags = ( maliastag_t * )buf; buf += sizeof( maliastag_t ) * poutmodel->numframes * poutmodel->numtags; for( i = 0; i < poutmodel->numframes; i++ ) { for( l = 0; l < poutmodel->numtags; l++, pintag++, pouttag++ ) { mat3_t axis; for( j = 0; j < 3; j++ ) { axis[AXIS_FORWARD+j] = LittleFloat( pintag->axis[0][j] ); axis[AXIS_RIGHT+j] = LittleFloat( pintag->axis[1][j] ); axis[AXIS_UP+j] = LittleFloat( pintag->axis[2][j] ); pouttag->origin[j] = LittleFloat( pintag->origin[j] ); } Quat_FromMatrix3( axis, pouttag->quat ); Quat_Normalize( pouttag->quat ); Q_strncpyz( pouttag->name, pintag->name, MD3_MAX_PATH ); } } // // allocate drawSurfs // drawSurf = poutmodel->drawSurfs = ( drawSurfaceAlias_t * )buf; buf += sizeof( drawSurfaceAlias_t ) * poutmodel->nummeshes; for( i = 0; i < poutmodel->nummeshes; i++, drawSurf++ ) { drawSurf->type = ST_ALIAS; drawSurf->model = mod; drawSurf->mesh = poutmodel->meshes + i; } // // load meshes // pinmesh = ( dmd3mesh_t * )( ( qbyte * )pinmodel + LittleLong( pinmodel->ofs_meshes ) ); poutmesh = poutmodel->meshes = ( maliasmesh_t * )buf; buf += sizeof( maliasmesh_t ) * poutmodel->nummeshes; for( i = 0; i < poutmodel->nummeshes; i++, poutmesh++ ) { if( strncmp( (const char *)pinmesh->id, IDMD3HEADER, 4 ) ) ri.Com_Error( ERR_DROP, "mesh %s in model %s has wrong id (%s should be %s)", pinmesh->name, mod->name, pinmesh->id, IDMD3HEADER ); Q_strncpyz( poutmesh->name, pinmesh->name, MD3_MAX_PATH ); Mod_StripLODSuffix( poutmesh->name ); poutmesh->numtris = LittleLong( pinmesh->num_tris ); poutmesh->numskins = LittleLong( pinmesh->num_skins ); poutmesh->numverts = numverts = LittleLong( pinmesh->num_verts ); /* if( poutmesh->numskins <= 0 ) ri.Com_Error( ERR_DROP, "mesh %i in model %s has no skins", i, mod->name ); else*/ if( poutmesh->numskins > MD3_MAX_SHADERS ) ri.Com_Error( ERR_DROP, "mesh %i in model %s has too many skins", i, mod->name ); if( poutmesh->numtris <= 0 ) ri.Com_Error( ERR_DROP, "mesh %i in model %s has no elements", i, mod->name ); else if( poutmesh->numtris > MD3_MAX_TRIANGLES ) ri.Com_Error( ERR_DROP, "mesh %i in model %s has too many triangles", i, mod->name ); if( poutmesh->numverts <= 0 ) ri.Com_Error( ERR_DROP, "mesh %i in model %s has no vertices", i, mod->name ); else if( poutmesh->numverts > MD3_MAX_VERTS ) ri.Com_Error( ERR_DROP, "mesh %i in model %s has too many vertices", i, mod->name ); bufsize = ALIGN( sizeof( maliasskin_t ) * poutmesh->numskins, sizeof( vec_t ) ) + numverts * ( sizeof( vec2_t ) + sizeof( maliasvertex_t ) * poutmodel->numframes ) + poutmesh->numtris * sizeof( elem_t ) * 3; buf = ( qbyte * )Mod_Malloc( mod, bufsize ); // // load the skins // pinskin = ( dmd3skin_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_skins ) ); poutskin = poutmesh->skins = ( maliasskin_t * )buf; buf += ALIGN( sizeof( maliasskin_t ) * poutmesh->numskins, sizeof( vec_t ) ); for( j = 0; j < poutmesh->numskins; j++, pinskin++, poutskin++ ) { Q_strncpyz( poutskin->name, pinskin->name, sizeof( poutskin->name ) ); poutskin->shader = R_RegisterSkin( poutskin->name ); } // // load the texture coordinates // pincoord = ( dmd3coord_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_tcs ) ); poutcoord = poutmesh->stArray = ( vec2_t * )buf; buf += poutmesh->numverts * sizeof( vec2_t ); for( j = 0; j < poutmesh->numverts; j++, pincoord++ ) { poutcoord[j][0] = LittleFloat( pincoord->st[0] ); poutcoord[j][1] = LittleFloat( pincoord->st[1] ); } // // load the vertexes and normals // pinvert = ( dmd3vertex_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_verts ) ); poutvert = poutmesh->vertexes = ( maliasvertex_t * )buf; buf += poutmesh->numverts * sizeof( maliasvertex_t ) * poutmodel->numframes; for( l = 0, poutframe = poutmodel->frames; l < poutmodel->numframes; l++, poutframe++, pinvert += poutmesh->numverts, poutvert += poutmesh->numverts ) { vec3_t v; for( j = 0; j < poutmesh->numverts; j++ ) { poutvert[j].point[0] = LittleShort( pinvert[j].point[0] ); poutvert[j].point[1] = LittleShort( pinvert[j].point[1] ); poutvert[j].point[2] = LittleShort( pinvert[j].point[2] ); poutvert[j].latlong[0] = pinvert[j].norm[0]; poutvert[j].latlong[1] = pinvert[j].norm[1]; VectorCopy( poutvert[j].point, v ); AddPointToBounds( v, poutframe->mins, poutframe->maxs ); } } // // load the elems // pinelem = ( unsigned int * )( ( qbyte * )pinmesh + LittleLong( pinmesh->ofs_elems ) ); poutelem = poutmesh->elems = ( elem_t * )buf; for( j = 0; j < poutmesh->numtris; j++, pinelem += 3, poutelem += 3 ) { poutelem[0] = (elem_t)LittleLong( pinelem[0] ); poutelem[1] = (elem_t)LittleLong( pinelem[1] ); poutelem[2] = (elem_t)LittleLong( pinelem[2] ); } pinmesh = ( dmd3mesh_t * )( ( qbyte * )pinmesh + LittleLong( pinmesh->meshsize ) ); } // // setup drawSurfs // for( i = 0; i < poutmodel->nummeshes; i++ ) { drawSurf = poutmodel->drawSurfs + i; drawSurf->type = ST_ALIAS; drawSurf->model = mod; drawSurf->mesh = poutmodel->meshes + i; } // // build S and T vectors for frame 0 // Mod_AliasBuildMeshesForFrame0( mod ); // // calculate model bounds // poutframe = poutmodel->frames; for( i = 0; i < poutmodel->numframes; i++, poutframe++ ) { VectorMA( poutframe->translate, MD3_XYZ_SCALE, poutframe->mins, poutframe->mins ); VectorMA( poutframe->translate, MD3_XYZ_SCALE, poutframe->maxs, poutframe->maxs ); poutframe->radius = RadiusFromBounds( poutframe->mins, poutframe->maxs ); AddPointToBounds( poutframe->mins, mod->mins, mod->maxs ); AddPointToBounds( poutframe->maxs, mod->mins, mod->maxs ); mod->radius = max( mod->radius, poutframe->radius ); } }
/* ===================== R_AddPolysToScene ===================== */ static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; int i, j; int fogIndex; fog_t *fog; vec3_t bounds[ 2 ]; if ( !tr.registered ) { return; } if ( !r_drawpolies->integer ) { return; } if ( !hShader ) { Log::Warn("RE_AddPolyToScene: NULL poly shader" ); return; } for ( j = 0; j < numPolys; j++ ) { if ( r_numPolyVerts + numVerts >= r_maxPolyVerts->integer || r_numPolys >= r_maxPolys->integer ) { /* NOTE TTimo this was initially Log::Warn but it happens a lot with high fighting scenes and particles since we don't plan on changing the const and making for room for those effects simply cut this message to developer only */ Log::Debug("RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n" ); return; } poly = &backEndData[ tr.smpFrame ]->polys[ r_numPolys ]; poly->surfaceType = surfaceType_t::SF_POLY; poly->hShader = hShader; poly->numVerts = numVerts; poly->verts = &backEndData[ tr.smpFrame ]->polyVerts[ r_numPolyVerts ]; Com_Memcpy( poly->verts, &verts[ numVerts * j ], numVerts * sizeof( *verts ) ); // done. r_numPolys++; r_numPolyVerts += numVerts; // if no world is loaded if ( tr.world == nullptr ) { fogIndex = 0; } // see if it is in a fog volume else if ( tr.world->numFogs == 1 ) { fogIndex = 0; } else { // find which fog volume the poly is in VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] ); VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] ); for ( i = 1; i < poly->numVerts; i++ ) { AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] ); } for ( fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++ ) { fog = &tr.world->fogs[ fogIndex ]; if ( BoundsIntersect( bounds[ 0 ], bounds[ 1 ], fog->bounds[ 0 ], fog->bounds[ 1 ] ) ) { break; } } if ( fogIndex == tr.world->numFogs ) { fogIndex = 0; } } poly->fogIndex = fogIndex; } }
/* * R_AddLightOccluder */ qboolean R_AddLightOccluder( const entity_t *ent ) { int i; float maxSide; vec3_t origin; unsigned int hash_key; shadowGroup_t *group; mleaf_t *leaf; vec3_t mins, maxs, bbox[8]; qboolean bmodelRotated = qfalse; if( rn.refdef.rdflags & RDF_NOWORLDMODEL ) return qfalse; if( !ent->model || ent->model->type == mod_brush ) return qfalse; VectorCopy( ent->lightingOrigin, origin ); if( ent->model->type == mod_brush ) { vec3_t t; VectorAdd( ent->model->mins, ent->model->maxs, t ); VectorMA( ent->origin, 0.5, t, origin ); } if( VectorCompare( origin, vec3_origin ) ) return qfalse; // find lighting group containing entities with same lightingOrigin as ours hash_key = (unsigned int)( origin[0] * 7 + origin[1] * 5 + origin[2] * 3 ); hash_key &= ( SHADOWGROUPS_HASH_SIZE-1 ); for( group = r_shadowGroups_hash[hash_key]; group; group = group->hashNext ) { if( VectorCompare( group->origin, origin ) ) goto add; // found an existing one, add } if( rsc.numShadowGroups == MAX_SHADOWGROUPS ) return qfalse; // no free groups leaf = Mod_PointInLeaf( origin, rsh.worldModel ); // start a new group group = &rsc.shadowGroups[rsc.numShadowGroups]; memset( group, 0, sizeof( *group ) ); group->id = group - rsc.shadowGroups + 1; group->bit = ( 1<<rsc.numShadowGroups ); group->vis = Mod_ClusterPVS( leaf->cluster, rsh.worldModel ); group->useOrtho = qtrue; group->alpha = r_shadows_alpha->value; // clear group bounds VectorCopy( origin, group->origin ); ClearBounds( group->mins, group->maxs ); ClearBounds( group->visMins, group->visMaxs ); // add to hash table group->hashNext = r_shadowGroups_hash[hash_key]; r_shadowGroups_hash[hash_key] = group; rsc.numShadowGroups++; add: // get model bounds if( ent->model->type == mod_alias ) R_AliasModelBBox( ent, mins, maxs ); else if( ent->model->type == mod_skeletal ) R_SkeletalModelBBox( ent, mins, maxs ); else if( ent->model->type == mod_brush ) R_BrushModelBBox( ent, mins, maxs, &bmodelRotated ); else ClearBounds( mins, maxs ); maxSide = 0; for( i = 0; i < 3; i++ ) { if( mins[i] >= maxs[i] ) return qfalse; maxSide = max( maxSide, maxs[i] - mins[i] ); } // ignore tiny objects if( maxSide < 10 ) { return qfalse; } rsc.entShadowGroups[R_ENT2NUM(ent)] = group->id; if( ent->flags & RF_WEAPONMODEL ) return qtrue; if( ent->model->type == mod_brush ) { VectorCopy( mins, group->mins ); VectorCopy( maxs, group->maxs ); } else { // rotate local bounding box and compute the full bounding box for this group R_TransformBounds( ent->origin, ent->axis, mins, maxs, bbox ); for( i = 0; i < 8; i++ ) { AddPointToBounds( bbox[i], group->mins, group->maxs ); } } // increase projection distance if needed VectorSubtract( group->mins, origin, mins ); VectorSubtract( group->maxs, origin, maxs ); group->radius = RadiusFromBounds( mins, maxs ); group->projDist = max( group->projDist, group->radius + min( r_shadows_projection_distance->value, 256 ) ); return qtrue; }
/* ================= R_MarkFragments ================= */ int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { int numsurfaces, numPlanes; int i, j, k, m, n; surfaceType_t *surfaces[64]; vec3_t mins, maxs; int returnedFragments; int returnedPoints; vec3_t normals[MAX_VERTS_ON_POLY+2]; float dists[MAX_VERTS_ON_POLY+2]; vec3_t clipPoints[2][MAX_VERTS_ON_POLY]; int numClipPoints; float *v; srfSurfaceFace_t *surf; srfGridMesh_t *cv; drawVert_t *dv; vec3_t normal; vec3_t projectionDir; vec3_t v1, v2; int *indexes; //increment view count for double check prevention tr.viewCount++; // VectorNormalize2( projection, projectionDir ); // find all the brushes that are to be considered ClearBounds( mins, maxs ); for ( i = 0 ; i < numPoints ; i++ ) { vec3_t temp; AddPointToBounds( points[i], mins, maxs ); VectorAdd( points[i], projection, temp ); AddPointToBounds( temp, mins, maxs ); // make sure we get all the leafs (also the one(s) in front of the hit surface) VectorMA( points[i], -20, projectionDir, temp ); AddPointToBounds( temp, mins, maxs ); } if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY; // create the bounding planes for the to be projected polygon for ( i = 0 ; i < numPoints ; i++ ) { VectorSubtract(points[(i+1)%numPoints], points[i], v1); VectorAdd(points[i], projection, v2); VectorSubtract(points[i], v2, v2); CrossProduct(v1, v2, normals[i]); VectorNormalizeFast(normals[i]); dists[i] = DotProduct(normals[i], points[i]); } // add near and far clipping planes for projection VectorCopy(projectionDir, normals[numPoints]); dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32; VectorCopy(projectionDir, normals[numPoints+1]); VectorInverse(normals[numPoints+1]); dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20; numPlanes = numPoints + 2; numsurfaces = 0; R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir); //assert(numsurfaces <= 64); //assert(numsurfaces != 64); returnedPoints = 0; returnedFragments = 0; for ( i = 0 ; i < numsurfaces ; i++ ) { if (*surfaces[i] == SF_GRID) { cv = (srfGridMesh_t *) surfaces[i]; for ( m = 0 ; m < cv->height - 1 ; m++ ) { for ( n = 0 ; n < cv->width - 1 ; n++ ) { // We triangulate the grid and chop all triangles within // the bounding planes of the to be projected polygon. // LOD is not taken into account, not such a big deal though. // // It's probably much nicer to chop the grid itself and deal // with this grid as a normal SF_GRID surface so LOD will // be applied. However the LOD of that chopped grid must // be synced with the LOD of the original curve. // One way to do this; the chopped grid shares vertices with // the original curve. When LOD is applied to the original // curve the unused vertices are flagged. Now the chopped curve // should skip the flagged vertices. This still leaves the // problems with the vertices at the chopped grid edges. // // To avoid issues when LOD applied to "hollow curves" (like // the ones around many jump pads) we now just add a 2 unit // offset to the triangle vertices. // The offset is added in the vertex normal vector direction // so all triangles will still fit together. // The 2 unit offset should avoid pretty much all LOD problems. numClipPoints = 3; dv = cv->verts + m * cv->width + n; VectorCopy(dv[0].xyz, clipPoints[0][0]); VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]); VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); VectorCopy(dv[1].xyz, clipPoints[0][2]); VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]); // check the normal of this triangle VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); CrossProduct(v1, v2, normal); VectorNormalizeFast(normal); if (DotProduct(normal, projectionDir) < -0.1) { // add the fragments of this triangle R_AddMarkFragments(numClipPoints, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } VectorCopy(dv[1].xyz, clipPoints[0][0]); VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]); VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]); VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]); // check the normal of this triangle VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); CrossProduct(v1, v2, normal); VectorNormalizeFast(normal); if (DotProduct(normal, projectionDir) < -0.05) { // add the fragments of this triangle R_AddMarkFragments(numClipPoints, clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } } } } else if (*surfaces[i] == SF_FACE) { surf = ( srfSurfaceFace_t * ) surfaces[i]; // check the normal of this face if (DotProduct(surf->plane.normal, projectionDir) > -0.5) { continue; } /* VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); CrossProduct(v1, v2, normal); VectorNormalize(normal); if (DotProduct(normal, projectionDir) > -0.5) continue; */ #ifdef _XBOX const unsigned char * const indexes = (unsigned char *)( (byte *)surf + surf->ofsIndices ); int nextSurfPoint = NEXT_SURFPOINT(surf->flags); #else indexes = (int *)( (byte *)surf + surf->ofsIndices ); #endif for ( k = 0 ; k < surf->numIndices ; k += 3 ) { for ( j = 0 ; j < 3 ; j++ ) { #ifdef _XBOX const unsigned short* v = surf->srfPoints + nextSurfPoint * indexes[k+j]; float fVec[3]; Q_CastShort2Float(&fVec[0], (short*)v + 0); Q_CastShort2Float(&fVec[1], (short*)v + 1); Q_CastShort2Float(&fVec[2], (short*)v + 2); VectorMA( fVec, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); #else v = surf->points[0] + VERTEXSIZE * indexes[k+j];; VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); #endif } // add the fragments of this face R_AddMarkFragments( 3 , clipPoints, numPlanes, normals, dists, maxPoints, pointBuffer, maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); if ( returnedFragments == maxFragments ) { return returnedFragments; // not enough space for more fragments } } continue; } else { // ignore all other world surfaces // might be cool to also project polygons on a triangle soup // however this will probably create huge amounts of extra polys // even more than the projection onto curves continue; } } return returnedFragments; }
/* ================= CreateBrushFaces ================= */ void CreateBrushFaces (void) { int i,j, k; vec_t r; face_t *f, *next; winding_t *w; plane_t clipplane, faceplane; mface_t *mf; vec3_t offset, point; offset[0] = offset[1] = offset[2] = 0; ClearBounds( brush_mins, brush_maxs ); brush_faces = NULL; if (!strncmp(ValueForKey(CurrentEntity, "classname"), "rotate_", 7)) { entity_t *FoundEntity; char *searchstring; char text[20]; searchstring = ValueForKey (CurrentEntity, "target"); FoundEntity = FindTargetEntity(searchstring); if (FoundEntity) GetVectorForKey(FoundEntity, "origin", offset); sprintf(text, "%g %g %g", offset[0], offset[1], offset[2]); SetKeyValue(CurrentEntity, "origin", text); } GetVectorForKey(CurrentEntity, "origin", offset); //printf("%i brushfaces at offset %f %f %f\n", numbrushfaces, offset[0], offset[1], offset[2]); for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; //printf("plane %f %f %f %f\n", mf->plane.normal[0], mf->plane.normal[1], mf->plane.normal[2], mf->plane.dist); faceplane = mf->plane; w = BaseWindingForPlane (&faceplane); //VectorNegate( faceplane.normal, point ); for (j = 0;j < numbrushfaces && w;j++) { clipplane = faces[j].plane; if( j == i/* || VectorCompare( clipplane.normal, point )*/ ) continue; // flip the plane, because we want to keep the back side VectorNegate(clipplane.normal, clipplane.normal); clipplane.dist *= -1; w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true); } if (!w) { //printf("----- skipped plane -----\n"); continue; // overcontrained plane } // this face is a keeper f = AllocFace (); f->winding = w; for (j = 0;j < w->numpoints;j++) { for (k = 0;k < 3;k++) { point[k] = w->points[j][k] - offset[k]; r = Q_rint( point[k] ); if ( fabs( point[k] - r ) < ZERO_EPSILON) w->points[j][k] = r; else w->points[j][k] = point[k]; // check for incomplete brushes if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE ) break; } // remove this brush if (k < 3) { FreeFace (f); for (f = brush_faces; f; f = next) { next = f->next; FreeFace (f); } brush_faces = NULL; //printf("----- skipped brush -----\n"); return; } AddPointToBounds( w->points[j], brush_mins, brush_maxs ); } CheckWinding( w ); faceplane.dist -= DotProduct(faceplane.normal, offset); f->texturenum = mf->texinfo; f->planenum = FindPlane (&faceplane, &f->planeside); f->next = brush_faces; brush_faces = f; } // Rotatable objects have to have a bounding box big enough // to account for all its rotations. if (DotProduct(offset, offset)) { vec_t delta; delta = RadiusFromBounds( brush_mins, brush_maxs ); for (k = 0;k < 3;k++) { brush_mins[k] = -delta; brush_maxs[k] = delta; } } //printf("%i : %f %f %f : %f %f %f\n", numbrushfaces, brush_mins[0], brush_mins[1], brush_mins[2], brush_maxs[0], brush_maxs[1], brush_maxs[2]); }
/* RE_ProjectDecal() 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 */ 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++; }