static void PopulateWithBSPModel(bspModel_t * model, matrix_t transform) { int i, j, x, y, pw[5], r, nodeNum; bspDrawSurface_t *ds; surfaceInfo_t *info; bspDrawVert_t *verts; int *indexes; mesh_t srcMesh, *mesh, *subdivided; traceInfo_t ti; traceWinding_t tw; /* dummy check */ if(model == NULL || transform == NULL) return; /* walk the list of surfaces in this model and fill out the info structs */ for(i = 0; i < model->numBSPSurfaces; i++) { /* get surface and info */ ds = &bspDrawSurfaces[model->firstBSPSurface + i]; info = &surfaceInfos[model->firstBSPSurface + i]; if(info->si == NULL) continue; /* no shadows */ if(!info->castShadows) continue; /* patchshadows? */ if(ds->surfaceType == MST_PATCH && patchShadows == qfalse) continue; /* some surfaces in the bsp might have been tagged as nodraw, with a bogus shader */ if((bspShaders[ds->shaderNum].contentFlags & noDrawContentFlags) || (bspShaders[ds->shaderNum].surfaceFlags & noDrawSurfaceFlags)) continue; /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */ if((info->si->compileFlags & C_NODRAW)) continue; if((info->si->compileFlags & C_TRANSLUCENT) && !(info->si->compileFlags & C_ALPHASHADOW) && !(info->si->compileFlags & C_LIGHTFILTER)) continue; /* setup trace info */ ti.si = info->si; ti.castShadows = info->castShadows; ti.surfaceNum = model->firstBSPBrush + i; ti.skipGrid = (ds->surfaceType == MST_PATCH); /* choose which node (normal or skybox) */ if(info->parentSurfaceNum >= 0) { nodeNum = skyboxNodeNum; /* sky surfaces in portal skies are ignored */ if(info->si->compileFlags & C_SKY) continue; } else nodeNum = headNodeNum; /* setup trace winding */ memset(&tw, 0, sizeof(tw)); tw.infoNum = AddTraceInfo(&ti); tw.numVerts = 3; /* switch on type */ switch (ds->surfaceType) { /* handle patches */ case MST_PATCH: /* subdivide the surface */ srcMesh.width = ds->patchWidth; srcMesh.height = ds->patchHeight; srcMesh.verts = &bspDrawVerts[ds->firstVert]; //% subdivided = SubdivideMesh( srcMesh, 8, 512 ); subdivided = SubdivideMesh2(srcMesh, info->patchIterations); /* fit it to the curve and remove colinear verts on rows/columns */ PutMeshOnCurve(*subdivided); mesh = RemoveLinearMeshColumnsRows(subdivided); FreeMesh(subdivided); /* set verts */ verts = mesh->verts; /* subdivide each quad to place the models */ for(y = 0; y < (mesh->height - 1); y++) { for(x = 0; x < (mesh->width - 1); x++) { /* set indexes */ pw[0] = x + (y * mesh->width); pw[1] = x + ((y + 1) * mesh->width); pw[2] = x + 1 + ((y + 1) * mesh->width); pw[3] = x + 1 + (y * mesh->width); pw[4] = x + (y * mesh->width); /* same as pw[ 0 ] */ /* set radix */ r = (x + y) & 1; /* make first triangle */ VectorCopy(verts[pw[r + 0]].xyz, tw.v[0].xyz); Vector2Copy(verts[pw[r + 0]].st, tw.v[0].st); VectorCopy(verts[pw[r + 1]].xyz, tw.v[1].xyz); Vector2Copy(verts[pw[r + 1]].st, tw.v[1].st); VectorCopy(verts[pw[r + 2]].xyz, tw.v[2].xyz); Vector2Copy(verts[pw[r + 2]].st, tw.v[2].st); MatrixTransformPoint2(transform, tw.v[0].xyz); MatrixTransformPoint2(transform, tw.v[1].xyz); MatrixTransformPoint2(transform, tw.v[2].xyz); FilterTraceWindingIntoNodes_r(&tw, nodeNum); /* make second triangle */ VectorCopy(verts[pw[r + 0]].xyz, tw.v[0].xyz); Vector2Copy(verts[pw[r + 0]].st, tw.v[0].st); VectorCopy(verts[pw[r + 2]].xyz, tw.v[1].xyz); Vector2Copy(verts[pw[r + 2]].st, tw.v[1].st); VectorCopy(verts[pw[r + 3]].xyz, tw.v[2].xyz); Vector2Copy(verts[pw[r + 3]].st, tw.v[2].st); MatrixTransformPoint2(transform, tw.v[0].xyz); MatrixTransformPoint2(transform, tw.v[1].xyz); MatrixTransformPoint2(transform, tw.v[2].xyz); FilterTraceWindingIntoNodes_r(&tw, nodeNum); } } /* free the subdivided mesh */ FreeMesh(mesh); break; /* handle triangle surfaces */ case MST_TRIANGLE_SOUP: case MST_PLANAR: /* set verts and indexes */ verts = &bspDrawVerts[ds->firstVert]; indexes = &bspDrawIndexes[ds->firstIndex]; /* walk the triangle list */ for(j = 0; j < ds->numIndexes; j += 3) { VectorCopy(verts[indexes[j]].xyz, tw.v[0].xyz); Vector2Copy(verts[indexes[j]].st, tw.v[0].st); VectorCopy(verts[indexes[j + 1]].xyz, tw.v[1].xyz); Vector2Copy(verts[indexes[j + 1]].st, tw.v[1].st); VectorCopy(verts[indexes[j + 2]].xyz, tw.v[2].xyz); Vector2Copy(verts[indexes[j + 2]].st, tw.v[2].st); MatrixTransformPoint2(transform, tw.v[0].xyz); MatrixTransformPoint2(transform, tw.v[1].xyz); MatrixTransformPoint2(transform, tw.v[2].xyz); FilterTraceWindingIntoNodes_r(&tw, nodeNum); } break; /* other surface types do not cast shadows */ default: break; } } }
qboolean R_LoadPSK(model_t *mod, void *buffer, int bufferSize, const char *modName) { int i, j, k; memStream_t *stream = NULL; axChunkHeader_t chunkHeader; int numPoints; axPoint_t *point; axPoint_t *points = NULL; int numVertexes; axVertex_t *vertex; axVertex_t *vertexes = NULL; //int numSmoothGroups; int numTriangles; axTriangle_t *triangle; axTriangle_t *triangles = NULL; int numMaterials; axMaterial_t *material; axMaterial_t *materials = NULL; int numReferenceBones; axReferenceBone_t *refBone; axReferenceBone_t *refBones = NULL; int numWeights; axBoneWeight_t *axWeight; axBoneWeight_t *axWeights = NULL; md5Model_t *md5; md5Bone_t *md5Bone; md5Weight_t *weight; vec3_t boneOrigin; quat_t boneQuat; //mat4_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]; mat4_t unrealToQuake; #define DeallocAll() Com_Dealloc(materials); \ Com_Dealloc(points); \ Com_Dealloc(vertexes); \ Com_Dealloc(triangles); \ Com_Dealloc(refBones); \ Com_Dealloc(axWeights); \ FreeMemStream(stream); //MatrixSetupScale(unrealToQuake, 1, -1, 1); mat4_from_angles(unrealToQuake, 0, 90, 0); stream = AllocMemStream(buffer, bufferSize); GetChunkHeader(stream, &chunkHeader); // check indent again if (Q_stricmpn(chunkHeader.ident, "ACTRHEAD", 8)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "ACTRHEAD"); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); mod->type = MOD_MD5; mod->dataSize += sizeof(md5Model_t); md5 = mod->md5 = ri.Hunk_Alloc(sizeof(md5Model_t), h_low); // read points GetChunkHeader(stream, &chunkHeader); if (Q_stricmpn(chunkHeader.ident, "PNTS0000", 8)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "PNTS0000"); DeallocAll(); return qfalse; } if (chunkHeader.dataSize != sizeof(axPoint_t)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axPoint_t)); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); numPoints = chunkHeader.numData; points = 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 // 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)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "VTXW0000"); DeallocAll(); return qfalse; } if (chunkHeader.dataSize != sizeof(axVertex_t)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axVertex_t)); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); numVertexes = chunkHeader.numData; vertexes = Com_Allocate(numVertexes * sizeof(axVertex_t)); { int tmpVertexInt = -1; // tmp vertex member values - MemStreamGet functions return -1 if they fail // now we print a warning if they do or abort if pointIndex is invalid for (i = 0, vertex = vertexes; i < numVertexes; i++, vertex++) { tmpVertexInt = MemStreamGetShort(stream); if (tmpVertexInt < 0 || tmpVertexInt >= numPoints) { ri.Printf(PRINT_ERROR, "R_LoadPSK: '%s' has vertex with point index out of range (%i while max %i)\n", modName, tmpVertexInt, numPoints); DeallocAll(); return qfalse; } vertex->pointIndex = tmpVertexInt; tmpVertexInt = MemStreamGetShort(stream); if (tmpVertexInt < 0) { Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->unknownA)\n"); } vertex->unknownA = tmpVertexInt; vertex->st[0] = MemStreamGetFloat(stream); if (vertex->st[0] == -1) { Ren_Warning("R_LoadPSK: MemStream possibly NULL or empty (vertex->st[0])\n"); } vertex->st[1] = MemStreamGetFloat(stream); if (vertex->st[1] == -1) { Ren_Warning("R_LoadPSK: MemStream possibly NULL or empty (vertex->st[1])\n"); } tmpVertexInt = MemStreamGetC(stream); if (tmpVertexInt < 0) { Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->materialIndex)\n"); } vertex->materialIndex = tmpVertexInt; tmpVertexInt = MemStreamGetC(stream); if (tmpVertexInt < 0) { Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->materialIndex)\n"); } vertex->reserved = tmpVertexInt; tmpVertexInt = MemStreamGetShort(stream); if (tmpVertexInt < 0) { Ren_Warning("R_LoadPSK: MemStream NULL or empty (vertex->materialIndex)\n"); } vertex->unknownB = tmpVertexInt; #if 0 Ren_Print("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)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "FACE0000"); DeallocAll(); return qfalse; } if (chunkHeader.dataSize != sizeof(axTriangle_t)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axTriangle_t)); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); numTriangles = chunkHeader.numData; triangles = 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--) { tmpVertexInt = MemStreamGetShort(stream); if (tmpVertexInt < 0) { Ren_Warning("R_LoadPSK: '%s' MemStream NULL or empty (triangle->indexes[%i])\n", modName, j); DeallocAll(); return qfalse; } if (tmpVertexInt >= numVertexes) { Ren_Warning("R_LoadPSK: '%s' has triangle with vertex index out of range (%i while max %i)\n", modName, tmpVertexInt, numVertexes); DeallocAll(); return qfalse; } triangle->indexes[j] = tmpVertexInt; } triangle->materialIndex = MemStreamGetC(stream); triangle->materialIndex2 = MemStreamGetC(stream); triangle->smoothingGroups = MemStreamGetLong(stream); } } // read materials GetChunkHeader(stream, &chunkHeader); if (Q_stricmpn(chunkHeader.ident, "MATT0000", 8)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "MATT0000"); DeallocAll(); return qfalse; } if (chunkHeader.dataSize != sizeof(axMaterial_t)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axMaterial_t)); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); numMaterials = chunkHeader.numData; materials = Com_Allocate(numMaterials * sizeof(axMaterial_t)); for (i = 0, material = materials; i < numMaterials; i++, material++) { MemStreamRead(stream, material->name, sizeof(material->name)); Ren_Print("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) { Ren_Warning("R_LoadPSK: '%s' has vertex with material index out of range (%i while max %i)\n", modName, vertex->materialIndex, numMaterials); DeallocAll(); return qfalse; } } for (i = 0, triangle = triangles; i < numTriangles; i++, triangle++) { if (triangle->materialIndex < 0 || triangle->materialIndex >= numMaterials) { Ren_Warning("R_LoadPSK: '%s' has triangle with material index out of range (%i while max %i)\n", modName, triangle->materialIndex, numMaterials); DeallocAll(); return qfalse; } } // read reference bones GetChunkHeader(stream, &chunkHeader); if (Q_stricmpn(chunkHeader.ident, "REFSKELT", 8)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "REFSKELT"); DeallocAll(); return qfalse; } if (chunkHeader.dataSize != sizeof(axReferenceBone_t)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axReferenceBone_t)); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); numReferenceBones = chunkHeader.numData; refBones = Com_Allocate(numReferenceBones * sizeof(axReferenceBone_t)); for (i = 0, refBone = refBones; i < numReferenceBones; i++, refBone++) { MemStreamRead(stream, refBone->name, sizeof(refBone->name)); //Ren_Print("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 Ren_Print("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)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk indent ('%s' should be '%s')\n", modName, chunkHeader.ident, "RAWWEIGHTS"); DeallocAll(); return qfalse; } if (chunkHeader.dataSize != sizeof(axBoneWeight_t)) { Ren_Warning("R_LoadPSK: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", modName, chunkHeader.dataSize, ( int ) sizeof(axBoneWeight_t)); DeallocAll(); return qfalse; } PrintChunkHeader(&chunkHeader); numWeights = chunkHeader.numData; axWeights = 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 Ren_Print("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) { Ren_Warning("R_LoadPSK: '%s' has no bones\n", modName); DeallocAll(); return qfalse; } if (md5->numBones > MAX_BONES) { Ren_Warning("R_LoadPSK: '%s' has more than %i bones (%i)\n", modName, MAX_BONES, md5->numBones); DeallocAll(); return qfalse; } //Ren_Print("R_LoadPSK: '%s' has %i bones\n", modName, md5->numBones); // copy all reference bones md5->bones = 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; } //Ren_Print("R_LoadPSK: '%s' has bone '%s' with parent index %i\n", modName, md5Bone->name, md5Bone->parentIndex); if (md5Bone->parentIndex >= md5->numBones) { DeallocAll(); Ren_Drop("R_LoadPSK: '%s' has bone '%s' with bad parent index %i while numBones is %i", modName, md5Bone->name, md5Bone->parentIndex, md5->numBones); } for (j = 0; j < 3; j++) { boneOrigin[j] = refBone->bone.position[j]; } // 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); quat_copy(boneQuat, md5Bone->rotation); //QuatClear(md5Bone->rotation); #if 0 Ren_Print("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); quat_copy(quat, md5Bone->rotation); } MatrixSetupTransformFromQuat(md5Bone->inverseTransform, md5Bone->rotation, md5Bone->origin); mat4_inverse_self(md5Bone->inverseTransform); #if 0 Ren_Print("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 = 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) { DeallocAll(); Ren_Drop("R_LoadPSK: vertex %i requires more weights %i than the maximum of %i in model '%s'", i, vboVert->numWeights, MAX_WEIGHTS, modName); //Ren_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 = 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 = 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 Ren_Print("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 = Com_Allocate(sizeof(*sortTri)); for (j = 0; j < 3; j++) { sortTri->indexes[j] = triangle->indexes[j]; sortTri->vertexes[j] = 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 = { 0, 0, 0 }; vec3_t binormal; vec3_t normal; for (j = 0; j < vboVertexes.currentElements; j++) { v0 = Com_GrowListElement(&vboVertexes, j); VectorClear(v0->tangent); VectorClear(v0->binormal); VectorClear(v0->normal); } for (j = 0; j < sortedTriangles.currentElements; j++) { skelTriangle_t *tri = Com_GrowListElement(&sortedTriangles, j); v0 = Com_GrowListElement(&vboVertexes, tri->indexes[0]); v1 = Com_GrowListElement(&vboVertexes, tri->indexes[1]); v2 = 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 = 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 = 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 = 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) { Ren_Print("R_LoadPSK: referenced bone: '%s'\n", (j < numReferenceBones) ? refBones[j].name : NULL); } } if (!vboTriangles.currentElements) { Ren_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 = Com_GrowListElement(&sortedTriangles, j); Com_Dealloc(sortTri); } Com_DestroyGrowList(&sortedTriangles); for (j = 0; j < vboVertexes.currentElements; j++) { md5Vertex_t *v = Com_GrowListElement(&vboVertexes, j); Com_Dealloc(v); } Com_DestroyGrowList(&vboVertexes); // 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); FreeMemStream(stream); Com_Dealloc(points); Com_Dealloc(vertexes); Com_Dealloc(triangles); Com_Dealloc(materials); Ren_Developer("%i VBO surfaces created for PSK model '%s'\n", md5->numVBOSurfaces, modName); return qtrue; }
static void PopulateWithPicoModel(int castShadows, picoModel_t * model, matrix_t transform) { int i, j, k, numSurfaces, numIndexes; picoSurface_t *surface; picoShader_t *shader; picoVec_t *xyz, *st; picoIndex_t *indexes; traceInfo_t ti; traceWinding_t tw; /* dummy check */ if(model == NULL || transform == NULL) return; /* get info */ numSurfaces = PicoGetModelNumSurfaces(model); /* walk the list of surfaces in this model and fill out the info structs */ for(i = 0; i < numSurfaces; i++) { /* get surface */ surface = PicoGetModelSurface(model, i); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* get shader (fixme: support shader remapping) */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) continue; ti.si = ShaderInfoForShader(PicoGetShaderName(shader)); if(ti.si == NULL) continue; /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */ if((ti.si->compileFlags & C_NODRAW)) continue; if((ti.si->compileFlags & C_TRANSLUCENT) && !(ti.si->compileFlags & C_ALPHASHADOW) && !(ti.si->compileFlags & C_LIGHTFILTER)) continue; /* setup trace info */ ti.castShadows = castShadows; ti.surfaceNum = -1; ti.skipGrid = qtrue; // also ignore picomodels when skipping patches /* setup trace winding */ memset(&tw, 0, sizeof(tw)); tw.infoNum = AddTraceInfo(&ti); tw.numVerts = 3; /* get info */ numIndexes = PicoGetSurfaceNumIndexes(surface); indexes = PicoGetSurfaceIndexes(surface, 0); /* walk the triangle list */ for(j = 0; j < numIndexes; j += 3, indexes += 3) { for(k = 0; k < 3; k++) { xyz = PicoGetSurfaceXYZ(surface, indexes[k]); st = PicoGetSurfaceST(surface, 0, indexes[k]); VectorCopy(xyz, tw.v[k].xyz); Vector2Copy(st, tw.v[k].st); MatrixTransformPoint2(transform, tw.v[k].xyz); } FilterTraceWindingIntoNodes_r(&tw, headNodeNum); } } }
void InsertModel(char *name, int frame, matrix_t transform, matrix_t nTransform, remap_t * remap, shaderInfo_t * celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle) { int i, j, k, s, numSurfaces; matrix_t identity; picoModel_t *model; picoShader_t *shader; picoSurface_t *surface; shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; char *picoShaderName; char shaderName[MAX_QPATH]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; remap_t *rm, *glob; double normalEpsilon_save; double distanceEpsilon_save; /* get model */ model = LoadModel(name, frame); if(model == NULL) return; /* handle null matrix */ if(transform == NULL) { MatrixIdentity(identity); transform = identity; } /* create transform matrix for normals */ #if 0 MatrixCopy(transform, nTransform); if(MatrixInverse(nTransform)) { Sys_FPrintf(SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n"); MatrixTranspose(transform, nTransform); } #endif /* fix bogus lightmap scale */ if(lightmapScale <= 0.0f) lightmapScale = 1.0f; /* fix bogus shade angle */ if(shadeAngle <= 0.0f) shadeAngle = 0.0f; /* each surface on the model will become a new map drawsurface */ numSurfaces = PicoGetModelNumSurfaces(model); //% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces ); for(s = 0; s < numSurfaces; s++) { /* get surface */ surface = PicoGetModelSurface(model, s); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* fix the surface's normals */ PicoFixSurfaceNormals(surface); /* allocate a surface (ydnar: gs mods) */ ds = AllocDrawSurface(SURFACE_TRIANGLES); ds->entityNum = eNum; ds->castShadows = castShadows; ds->recvShadows = recvShadows; /* get shader name */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) picoShaderName = ""; else picoShaderName = PicoGetShaderName(shader); /* handle shader remapping */ glob = NULL; for(rm = remap; rm != NULL; rm = rm->next) { if(rm->from[0] == '*' && rm->from[1] == '\0') glob = rm; else if(!Q_stricmp(picoShaderName, rm->from)) { Sys_FPrintf(SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to); picoShaderName = rm->to; glob = NULL; break; } } if(glob != NULL) { Sys_FPrintf(SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to); picoShaderName = glob->to; } /* shader renaming for sof2 */ if(renameModelShaders) { strcpy(shaderName, picoShaderName); StripExtension(shaderName); if(spawnFlags & 1) strcat(shaderName, "_RMG_BSP"); else strcat(shaderName, "_BSP"); si = ShaderInfoForShader(shaderName); } else { si = ShaderInfoForShader(picoShaderName); // Tr3B: HACK to support the messy Doom 3 materials provided by .ASE files if(!si->explicitDef) { picoShaderName = PicoGetShaderMapName(shader); Q_strncpyz(shaderName, picoShaderName, sizeof(shaderName)); StripExtension(shaderName); i = 0; while(shaderName[i]) { if(shaderName[i] == '\\') shaderName[i] = '/'; i++; } if(strstr(shaderName, "base/")) { si = ShaderInfoForShader(strstr(shaderName, "base/") + strlen("base/")); Sys_FPrintf(SYS_WRN, "WARNING: Applied .ASE material loader HACK to '%s' -> '%s'\n", picoShaderName, si->shader); } } } /* set shader */ ds->shaderInfo = si; /* force to meta? */ if((si != NULL && si->forceMeta) || (spawnFlags & 4)) /* 3rd bit */ ds->type = SURFACE_FORCED_META; /* fix the surface's normals (jal: conditioned by shader info) */ //if(!(spawnFlags & 64) && (shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META)) // PicoFixSurfaceNormals(surface); /* set sample size */ if(lightmapSampleSize > 0.0f) ds->sampleSize = lightmapSampleSize; /* set lightmap scale */ if(lightmapScale > 0.0f) ds->lightmapScale = lightmapScale; /* set shading angle */ if(shadeAngle > 0.0f) ds->shadeAngleDegrees = shadeAngle; /* set particulars */ ds->numVerts = PicoGetSurfaceNumVertexes(surface); ds->verts = safe_malloc(ds->numVerts * sizeof(ds->verts[0])); memset(ds->verts, 0, ds->numVerts * sizeof(ds->verts[0])); ds->numIndexes = PicoGetSurfaceNumIndexes(surface); ds->indexes = safe_malloc(ds->numIndexes * sizeof(ds->indexes[0])); memset(ds->indexes, 0, ds->numIndexes * sizeof(ds->indexes[0])); /* copy vertexes */ for(i = 0; i < ds->numVerts; i++) { /* get vertex */ dv = &ds->verts[i]; /* xyz and normal */ xyz = PicoGetSurfaceXYZ(surface, i); VectorCopy(xyz, dv->xyz); MatrixTransformPoint2(transform, dv->xyz); normal = PicoGetSurfaceNormal(surface, i); VectorCopy(normal, dv->normal); MatrixTransformNormal2(nTransform, dv->normal); VectorNormalize2(dv->normal, dv->normal); /* ydnar: tek-fu celshading support for flat shaded shit */ if(flat) { dv->st[0] = si->stFlat[0]; dv->st[1] = si->stFlat[1]; } /* ydnar: gs mods: added support for explicit shader texcoord generation */ else if(si->tcGen) { /* project the texture */ dv->st[0] = DotProduct(si->vecs[0], dv->xyz); dv->st[1] = DotProduct(si->vecs[1], dv->xyz); } /* normal texture coordinates */ else { st = PicoGetSurfaceST(surface, 0, i); dv->st[0] = st[0]; dv->st[1] = st[1]; } /* set lightmap/color bits */ color = PicoGetSurfaceColor(surface, 0, i); dv->paintColor[0] = color[0] / 255.0f; dv->paintColor[1] = color[1] / 255.0f; dv->paintColor[2] = color[2] / 255.0f; dv->paintColor[3] = color[3] / 255.0f; for(j = 0; j < MAX_LIGHTMAPS; j++) { dv->lightmap[j][0] = 0.0f; dv->lightmap[j][1] = 0.0f; dv->lightColor[j][0] = 255; dv->lightColor[j][1] = 255; dv->lightColor[j][2] = 255; dv->lightColor[j][3] = 255; } } /* copy indexes */ indexes = PicoGetSurfaceIndexes(surface, 0); for(i = 0; i < ds->numIndexes; i++) ds->indexes[i] = indexes[i]; /* set cel shader */ ds->celShader = celShader; /* ydnar: giant hack land: generate clipping brushes for model triangles */ if(si->clipModel || (spawnFlags & 2)) /* 2nd bit */ { vec3_t points[4], backs[3]; vec4_t plane, reverse, pa, pb, pc; /* temp hack */ if(!si->clipModel && (((si->compileFlags & C_TRANSLUCENT) && !(si->compileFlags & C_COLLISION)) || !(si->compileFlags & C_SOLID))) continue; /* walk triangle list */ for(i = 0; i < ds->numIndexes; i += 3) { /* overflow hack */ AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes + 64) << 1, allocatedmapplanes, 1024); /* make points and back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; /* copy xyz */ VectorCopy(dv->xyz, points[j]); VectorCopy(dv->xyz, backs[j]); /* find nearest axial to normal and push back points opposite */ /* note: this doesn't work as well as simply using the plane of the triangle, below */ for(k = 0; k < 3; k++) { if(fabs(dv->normal[k]) >= fabs(dv->normal[(k + 1) % 3]) && fabs(dv->normal[k]) >= fabs(dv->normal[(k + 2) % 3])) { backs[j][k] += dv->normal[k] < 0.0f ? 64.0f : -64.0f; break; } } } VectorCopy(points[0], points[3]); // for cyclic usage /* make plane for triangle */ // div0: add some extra spawnflags: // 0: snap normals to axial planes for extrusion // 8: extrude with the original normals // 16: extrude only with up/down normals (ideal for terrain) // 24: extrude by distance zero (may need engine changes) if(PlaneFromPoints(plane, points[0], points[1], points[2], qtrue)) { vec3_t bestNormal; float backPlaneDistance = 2; if(spawnFlags & 8) // use a DOWN normal { if(spawnFlags & 16) { // 24: normal as is, and zero width (broken) VectorCopy(plane, bestNormal); } else { // 8: normal as is VectorCopy(plane, bestNormal); } } else { if(spawnFlags & 16) { // 16: UP/DOWN normal VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } else { // 0: axial normal if(fabs(plane[0]) > fabs(plane[1])) // x>y if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // x>y, z>=y if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // z>=x, x>y VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); else // y>=x if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0); else // z>=y, y>=x VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } } /* build a brush */ buildBrush = AllocBrush(48); buildBrush->entityNum = mapEntityNum; buildBrush->original = buildBrush; buildBrush->contentShader = si; buildBrush->compileFlags = si->compileFlags; buildBrush->contentFlags = si->contentFlags; buildBrush->generatedClipBrush = qtrue; normalEpsilon_save = normalEpsilon; distanceEpsilon_save = distanceEpsilon; if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here { buildBrush->detail = qfalse; // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model) if(normalEpsilon > 0) normalEpsilon = 0; if(distanceEpsilon > 0) distanceEpsilon = 0; } else buildBrush->detail = qtrue; /* regenerate back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; // shift by some units VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit } /* make back plane */ VectorScale(plane, -1.0f, reverse); reverse[3] = -plane[3]; if((spawnFlags & 24) != 24) reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance; // that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane) if(PlaneFromPoints(pa, points[2], points[1], backs[1], qtrue) && PlaneFromPoints(pb, points[1], points[0], backs[0], qtrue) && PlaneFromPoints(pc, points[0], points[2], backs[2], qtrue)) { /* set up brush sides */ buildBrush->numsides = 5; buildBrush->sides[0].shaderInfo = si; for(j = 1; j < buildBrush->numsides; j++) buildBrush->sides[j].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works buildBrush->sides[0].planenum = FindFloatPlane(plane, plane[3], 3, points); buildBrush->sides[1].planenum = FindFloatPlane(pa, pa[3], 2, &points[1]); // pa contains points[1] and points[2] buildBrush->sides[2].planenum = FindFloatPlane(pb, pb[3], 2, &points[0]); // pb contains points[0] and points[1] buildBrush->sides[3].planenum = FindFloatPlane(pc, pc[3], 2, &points[2]); // pc contains points[2] and points[0] (copied to points[3] buildBrush->sides[4].planenum = FindFloatPlane(reverse, reverse[3], 3, backs); } else { free(buildBrush); continue; } normalEpsilon = normalEpsilon_save; distanceEpsilon = distanceEpsilon_save; /* add to entity */ if(CreateBrushWindings(buildBrush)) { AddBrushBevels(); //% EmitBrushes( buildBrush, NULL, NULL ); buildBrush->next = entities[mapEntityNum].brushes; entities[mapEntityNum].brushes = buildBrush; entities[mapEntityNum].numBrushes++; } else free(buildBrush); } } } } }