static void DrawOriginInfo() { RE_DrawTextLeft("Player position:\n----------------", RGB(0.4,0.4,0.6)); RE_DrawTextLeft(va("Point: %.0f %.0f %.0f", VECTOR_ARG(cl.refdef.vieworg)), RGB(0.2,0.4,0.1)); CVec3 view; Euler2Vecs(cl.refdef.viewangles, &view, NULL, NULL); RE_DrawTextLeft(va("View direction: %g %g %g", VECTOR_ARG(view)), RGB(0.2,0.4,0.1)); const CBspLeaf *leaf = CM_FindLeaf(cl.refdef.vieworg); RE_DrawTextLeft(va("Leaf: %d, cluster: %d, zone: %d", leaf->num, leaf->cluster, leaf->zone), RGB(0.2,0.4,0.1)); #if 0 //!!!!!!!!! RE_DrawTextLeft(va("nBrushes: %d", leaf->numBrushes), RGB(1,0.2,0.2)); for (int i = 0; i < leaf->numBrushes; i++) { cbrush_t* b = leaf->brushes[i]; RE_DrawTextLeft(va("%d: b=%d", i, b - map_brushes), RGB(1,0,0)); DecodeContents(b->contents); } //!!!!!!!!! #endif DecodeContents(leaf->GetContents(cl.refdef.vieworg) | CM_PointModelContents(cl.refdef.vieworg)); RE_DrawTextLeft(""); // empty line }
// function is similar to part of CSkelMeshInstance::SetMesh() and Rune mesh loader static void BuildSkeleton(TArray<CCoords> &Coords, const TArray<CSkelMeshBone> &Bones) { guard(BuildSkeleton); int numBones = Bones.Num(); Coords.Empty(numBones); Coords.AddZeroed(numBones); for (int i = 0; i < numBones; i++) { const CSkelMeshBone &B = Bones[i]; CCoords &BC = Coords[i]; // compute reference bone coords CVec3 BP; CQuat BO; // get default pose BP = B.Position; BO = B.Orientation; #if MIRROR_MESH BP[1] *= -1; // y BO.y *= -1; BO.w *= -1; #endif if (!i) BO.Conjugate(); // root bone BC.origin = BP; BO.ToAxis(BC.axis); // move bone position to global coordinate space if (i) // do not rotate root bone { assert(B.ParentIndex < i); Coords[B.ParentIndex].UnTransformCoords(BC, BC); } #if 0 //!! if (i == 32) { appNotify("Bone %d (%8.3f %8.3f %8.3f) - (%8.3f %8.3f %8.3f %8.3f)", i, VECTOR_ARG(BP), QUAT_ARG(BO)); #define C BC appNotify("REF : o=%8.3f %8.3f %8.3f", VECTOR_ARG(C.origin )); appNotify(" 0=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[0])); appNotify(" 1=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[1])); appNotify(" 2=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[2])); #undef C } #endif } unguard; }
void ExportMd5Mesh(const CSkeletalMesh *Mesh) { guard(ExportMd5Mesh); int i; UObject *OriginalMesh = Mesh->OriginalMesh; if (!Mesh->Lods.Num()) { appNotify("Mesh %s has 0 lods", OriginalMesh->Name); return; } FArchive *Ar = CreateExportArchive(OriginalMesh, "%s.md5mesh", OriginalMesh->Name); if (!Ar) return; const CSkelMeshLod &Lod = Mesh->Lods[0]; Ar->Printf( "MD5Version 10\n" "commandline \"Created with UE Viewer\"\n" "\n" "numJoints %d\n" "numMeshes %d\n" "\n", Mesh->RefSkeleton.Num(), Lod.Sections.Num() ); // compute skeleton TArray<CCoords> BoneCoords; BuildSkeleton(BoneCoords, Mesh->RefSkeleton); // write joints Ar->Printf("joints {\n"); for (i = 0; i < Mesh->RefSkeleton.Num(); i++) { const CSkelMeshBone &B = Mesh->RefSkeleton[i]; const CCoords &BC = BoneCoords[i]; CVec3 BP; CQuat BO; BP = BC.origin; BO.FromAxis(BC.axis); if (BO.w < 0) BO.Negate(); // W-component of quaternion will be removed ... Ar->Printf( "\t\"%s\"\t%d ( %f %f %f ) ( %.10f %.10f %.10f )\n", *B.Name, (i == 0) ? -1 : B.ParentIndex, VECTOR_ARG(BP), BO.x, BO.y, BO.z ); #if 0 //!! if (i == 32 || i == 34) { CCoords BC; BC.origin = BP; BO.ToAxis(BC.axis); appNotify("Bone %d (%8.3f %8.3f %8.3f) - (%8.3f %8.3f %8.3f %8.3f)", i, VECTOR_ARG(BP), QUAT_ARG(BO)); #define C BC appNotify("INV : o=%8.3f %8.3f %8.3f", VECTOR_ARG(C.origin )); appNotify(" 0=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[0])); appNotify(" 1=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[1])); appNotify(" 2=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[2])); #undef C // BO.Negate(); BO.w *= -1; BO.ToAxis(BC.axis); #define C BC appNotify("INV2 : o=%8.3f %8.3f %8.3f", VECTOR_ARG(C.origin )); appNotify(" 0=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[0])); appNotify(" 1=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[1])); appNotify(" 2=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[2])); #undef C } #endif } Ar->Printf("}\n\n"); // collect weights information TArray<VertInfluences> Weights; // Point -> Influences Weights.AddZeroed(Lod.NumVerts); for (i = 0; i < Lod.NumVerts; i++) { const CSkelMeshVertex &V = Lod.Verts[i]; CVec4 UnpackedWeights; V.UnpackWeights(UnpackedWeights); for (int j = 0; j < NUM_INFLUENCES; j++) { if (V.Bone[j] < 0) break; VertInfluence *I = new (Weights[i].Inf) VertInfluence; I->Bone = V.Bone[j]; I->Weight = UnpackedWeights[j]; } } CIndexBuffer::IndexAccessor_t Index = Lod.Indices.GetAccessor(); // write meshes // we are using some terms here: // - "mesh vertex" is a vertex in Lod.Verts[] array, global for whole mesh // - "surcace vertex" is a vertex from the mesh stripped to only one (current) section for (int m = 0; m < Lod.Sections.Num(); m++) { const CMeshSection &Sec = Lod.Sections[m]; TArray<int> MeshVerts; // surface vertex -> mesh vertex TArray<int> BackWedge; // mesh vertex -> surface vertex TArray<bool> UsedVerts; // mesh vertex -> surface: used of not TArray<int> MeshWeights; // mesh vertex -> weight index MeshVerts.Empty(Lod.NumVerts); UsedVerts.AddZeroed(Lod.NumVerts); BackWedge.AddZeroed(Lod.NumVerts); MeshWeights.AddZeroed(Lod.NumVerts); // find verts and triangles for current material for (i = 0; i < Sec.NumFaces * 3; i++) { int idx = Index(i + Sec.FirstIndex); if (UsedVerts[idx]) continue; // vertex is already used in previous triangle UsedVerts[idx] = true; int locWedge = MeshVerts.Add(idx); BackWedge[idx] = locWedge; } // find influences int WeightIndex = 0; for (i = 0; i < Lod.NumVerts; i++) { if (!UsedVerts[i]) continue; MeshWeights[i] = WeightIndex; WeightIndex += Weights[i].Inf.Num(); } // mesh header const UUnrealMaterial *Tex = Sec.Material; if (Tex) { Ar->Printf( "mesh {\n" "\tshader \"%s\"\n\n", Tex->Name ); ExportObject(Tex); } else { Ar->Printf( "mesh {\n" "\tshader \"material_%d\"\n\n", m ); } // verts Ar->Printf("\tnumverts %d\n", MeshVerts.Num()); for (i = 0; i < MeshVerts.Num(); i++) { int iPoint = MeshVerts[i]; const CSkelMeshVertex &V = Lod.Verts[iPoint]; Ar->Printf("\tvert %d ( %f %f ) %d %d\n", i, V.UV.U, V.UV.V, MeshWeights[iPoint], Weights[iPoint].Inf.Num()); } // triangles Ar->Printf("\n\tnumtris %d\n", Sec.NumFaces); for (i = 0; i < Sec.NumFaces; i++) { Ar->Printf("\ttri %d", i); #if MIRROR_MESH for (int j = 2; j >= 0; j--) #else for (int j = 0; j < 3; j++) #endif Ar->Printf(" %d", BackWedge[Index(Sec.FirstIndex + i * 3 + j)]); Ar->Printf("\n"); } // weights Ar->Printf("\n\tnumweights %d\n", WeightIndex); int saveWeightIndex = WeightIndex; WeightIndex = 0; for (i = 0; i < Lod.NumVerts; i++) { if (!UsedVerts[i]) continue; for (int j = 0; j < Weights[i].Inf.Num(); j++) { const VertInfluence &I = Weights[i].Inf[j]; CVec3 v; v = Lod.Verts[i].Position; #if MIRROR_MESH v[1] *= -1; // y #endif BoneCoords[I.Bone].TransformPoint(v, v); Ar->Printf( "\tweight %d %d %f ( %f %f %f )\n", WeightIndex, I.Bone, I.Weight, VECTOR_ARG(v) ); WeightIndex++; } } assert(saveWeightIndex == WeightIndex); // mesh footer Ar->Printf("}\n"); } delete Ar; unguard; }
void ExportMd5Anim(const CAnimSet *Anim) { guard(ExportMd5Anim); int numBones = Anim->TrackBoneNames.Num(); UObject *OriginalAnim = Anim->OriginalAnim; for (int AnimIndex = 0; AnimIndex < Anim->Sequences.Num(); AnimIndex++) { int i; const CAnimSequence &S = Anim->Sequences[AnimIndex]; FArchive *Ar = CreateExportArchive(OriginalAnim, "%s/%s.md5anim", OriginalAnim->Name, *S.Name); if (!Ar) continue; Ar->Printf( "MD5Version 10\n" "commandline \"Created with UE Viewer\"\n" "\n" "numFrames %d\n" "numJoints %d\n" "frameRate %g\n" "numAnimatedComponents %d\n" "\n", S.NumFrames, numBones, S.Rate, numBones * 6 ); // skeleton Ar->Printf("hierarchy {\n"); for (i = 0; i < numBones; i++) { Ar->Printf("\t\"%s\" %d %d %d\n", *Anim->TrackBoneNames[i], (i == 0) ? -1 : 0, 63, i * 6); // ParentIndex is unknown for UAnimSet, so always write "0" // here: 6 is number of components per frame, 63 = (1<<6)-1 -- flags "all components are used" } // bounds Ar->Printf("}\n\nbounds {\n"); for (i = 0; i < S.NumFrames; i++) Ar->Printf("\t( -100 -100 -100 ) ( 100 100 100 )\n"); //!! dummy Ar->Printf("}\n\n"); // baseframe and frames for (int Frame = -1; Frame < S.NumFrames; Frame++) { int t = Frame; if (Frame == -1) { Ar->Printf("baseframe {\n"); t = 0; } else Ar->Printf("frame %d {\n", Frame); for (int b = 0; b < numBones; b++) { CVec3 BP; CQuat BO; S.Tracks[b].GetBonePosition(t, S.NumFrames, false, BP, BO); if (!b) BO.Conjugate(); // root bone #if MIRROR_MESH BO.y *= -1; BO.w *= -1; BP[1] *= -1; // y #endif if (BO.w < 0) BO.Negate(); // W-component of quaternion will be removed ... if (Frame < 0) Ar->Printf("\t( %f %f %f ) ( %.10f %.10f %.10f )\n", VECTOR_ARG(BP), BO.x, BO.y, BO.z); else Ar->Printf("\t%f %f %f %.10f %.10f %.10f\n", VECTOR_ARG(BP), BO.x, BO.y, BO.z); } Ar->Printf("}\n\n"); } delete Ar; } unguard; }
static void ExportSection(ExportContext& Context, const CBaseMeshLod& Lod, const CMeshVertex* Verts, int SectonIndex, FArchive& Ar) { guard(ExportSection); int VertexSize = Context.IsSkeletal() ? sizeof(CSkelMeshVertex) : sizeof(CStaticMeshVertex); const CMeshSection& S = Lod.Sections[SectonIndex]; bool bLast = (SectonIndex == Lod.Sections.Num()-1); // Remap section indices to local indices CIndexBuffer::IndexAccessor_t GetIndex = Lod.Indices.GetAccessor(); TArray<int> indexRemap; // old vertex index -> new vertex index indexRemap.Init(-1, Lod.NumVerts); int numLocalVerts = 0; int numLocalIndices = S.NumFaces * 3; for (int idx = 0; idx < numLocalIndices; idx++) { int vertIndex = GetIndex(S.FirstIndex + idx); if (indexRemap[vertIndex] == -1) { indexRemap[vertIndex] = numLocalVerts++; } } // Prepare buffers int IndexBufIndex = Context.Data.AddZeroed(); int PositionBufIndex = Context.Data.AddZeroed(); int NormalBufIndex = Context.Data.AddZeroed(); int TangentBufIndex = Context.Data.AddZeroed(); int BonesBufIndex = -1; int WeightsBufIndex = -1; if (Context.IsSkeletal()) { BonesBufIndex = Context.Data.AddZeroed(); WeightsBufIndex = Context.Data.AddZeroed(); } int UVBufIndex[MAX_MESH_UV_SETS]; for (int i = 0; i < Lod.NumTexCoords; i++) { UVBufIndex[i] = Context.Data.AddZeroed(); } BufferData& IndexBuf = Context.Data[IndexBufIndex]; BufferData& PositionBuf = Context.Data[PositionBufIndex]; BufferData& NormalBuf = Context.Data[NormalBufIndex]; BufferData& TangentBuf = Context.Data[TangentBufIndex]; BufferData* UVBuf[MAX_MESH_UV_SETS]; BufferData* BonesBuf = NULL; BufferData* WeightsBuf = NULL; PositionBuf.Setup(numLocalVerts, "VEC3", BufferData::FLOAT, sizeof(CVec3)); NormalBuf.Setup(numLocalVerts, "VEC3", BufferData::FLOAT, sizeof(CVec3)); TangentBuf.Setup(numLocalVerts, "VEC4", BufferData::FLOAT, sizeof(CVec4)); for (int i = 0; i < Lod.NumTexCoords; i++) { UVBuf[i] = &Context.Data[UVBufIndex[i]]; UVBuf[i]->Setup(numLocalVerts, "VEC2", BufferData::FLOAT, sizeof(CMeshUVFloat)); } if (Context.IsSkeletal()) { BonesBuf = &Context.Data[BonesBufIndex]; WeightsBuf = &Context.Data[WeightsBufIndex]; BonesBuf->Setup(numLocalVerts, "VEC4", BufferData::UNSIGNED_SHORT, sizeof(uint16)*4); WeightsBuf->Setup(numLocalVerts, "VEC4", BufferData::UNSIGNED_BYTE, sizeof(uint32), /*InNormalized=*/ true); } // Prepare and build indices TArray<int> localIndices; localIndices.AddUninitialized(numLocalIndices); int* pIndex = localIndices.GetData(); for (int i = 0; i < numLocalIndices; i++) { *pIndex++ = GetIndex(S.FirstIndex + i); } if (numLocalVerts <= 65536) { IndexBuf.Setup(numLocalIndices, "SCALAR", BufferData::UNSIGNED_SHORT, sizeof(uint16)); for (int idx = 0; idx < numLocalIndices; idx++) { IndexBuf.Put<uint16>(indexRemap[localIndices[idx]]); } } else { IndexBuf.Setup(numLocalIndices, "SCALAR", BufferData::UNSIGNED_INT, sizeof(uint32)); for (int idx = 0; idx < numLocalIndices; idx++) { IndexBuf.Put<uint32>(indexRemap[localIndices[idx]]); } } // Build reverse index map for fast lookup of vertex by its new index. // It maps new vertex index to old vertex index. TArray<int> revIndexMap; revIndexMap.AddUninitialized(numLocalVerts); for (int i = 0; i < indexRemap.Num(); i++) { int newIndex = indexRemap[i]; if (newIndex != -1) { revIndexMap[newIndex] = i; } } // Build vertices for (int i = 0; i < numLocalVerts; i++) { int vertIndex = revIndexMap[i]; const CMeshVertex& V = VERT(vertIndex); CVec3 Position = V.Position; CVec4 Normal, Tangent; Unpack(Normal, V.Normal); Unpack(Tangent, V.Tangent); // Unreal (and we are) using normal.w for computing binormal. glTF // uses tangent.w for that. Make this value exactly 1.0 of -1.0 to make glTF // validator happy. #if 0 // There's some problem: V.Normal.W == 0x80 -> -1.008 instead of -1.0 if (Normal.w > 1.001 || Normal.w < -1.001) { appError("%X -> %g\n", V.Normal.Data, Normal.w); } #endif Tangent.w = (Normal.w < 0) ? -1 : 1; TransformPosition(Position); TransformDirection(Normal); TransformDirection(Tangent); Normal.Normalize(); Tangent.Normalize(); // Fill buffers PositionBuf.Put(Position); NormalBuf.Put(Normal.xyz); TangentBuf.Put(Tangent); UVBuf[0]->Put(V.UV); } // Compute bounds for PositionBuf CVec3 Mins, Maxs; ComputeBounds((CVec3*)PositionBuf.Data, numLocalVerts, sizeof(CVec3), Mins, Maxs); char buf[256]; appSprintf(ARRAY_ARG(buf), "[ %g, %g, %g ]", VECTOR_ARG(Mins)); PositionBuf.BoundsMin = buf; appSprintf(ARRAY_ARG(buf), "[ %g, %g, %g ]", VECTOR_ARG(Maxs)); PositionBuf.BoundsMax = buf; if (Context.IsSkeletal()) { for (int i = 0; i < numLocalVerts; i++) { int vertIndex = revIndexMap[i]; const CMeshVertex& V0 = VERT(vertIndex); const CSkelMeshVertex& V = static_cast<const CSkelMeshVertex&>(V0); int16 Bones[NUM_INFLUENCES]; static_assert(NUM_INFLUENCES == 4, "Code designed for 4 influences"); static_assert(sizeof(Bones) == sizeof(V.Bone), "Unexpected V.Bones size"); memcpy(Bones, V.Bone, sizeof(Bones)); for (int j = 0; j < NUM_INFLUENCES; j++) { // We have INDEX_NONE as list terminator, should replace with something else for glTF if (Bones[j] == INDEX_NONE) { Bones[j] = 0; } } BonesBuf->Put(*(uint64*)&Bones); WeightsBuf->Put(V.PackedWeights); } } // Secondary UVs for (int uvIndex = 1; uvIndex < Lod.NumTexCoords; uvIndex++) { BufferData* pBuf = UVBuf[uvIndex]; const CMeshUVFloat* srcUV = Lod.ExtraUV[uvIndex-1]; for (int i = 0; i < numLocalVerts; i++) { int vertIndex = revIndexMap[i]; pBuf->Put(srcUV[vertIndex]); } } // Write primitive information to json Ar.Printf( " {\n" " \"attributes\" : {\n" " \"POSITION\" : %d,\n" " \"NORMAL\" : %d,\n" " \"TANGENT\" : %d,\n", PositionBufIndex, NormalBufIndex, TangentBufIndex ); if (Context.IsSkeletal()) { Ar.Printf( " \"JOINTS_0\" : %d,\n" " \"WEIGHTS_0\" : %d,\n", BonesBufIndex, WeightsBufIndex ); } for (int i = 0; i < Lod.NumTexCoords; i++) { Ar.Printf( " \"TEXCOORD_%d\" : %d%s\n", i, UVBufIndex[i], i < (Lod.NumTexCoords-1) ? "," : "" ); } Ar.Printf( " },\n" " \"indices\" : %d,\n" " \"material\" : %d\n" " }%s\n", IndexBufIndex, SectonIndex, SectonIndex < (Lod.Sections.Num()-1) ? "," : "" ); unguard; }
void SV_LinkEdict(edict_t *ent) { guard(SV_LinkEdict); int i, j, k; //!! HOOK - move outside ? if (bspfile.type != map_q2) { if (ent->s.modelindex >= 0 && ent->s.modelindex < MAX_MODELS) { // link model hook const char *modelName = sv.configstrings[CS_MODELS + ent->s.modelindex]; if (!strcmp(modelName, "models/objects/dmspot/tris.md2")) { // teleporter (source+target) was found // appPrintf(S_CYAN"teleport: %g %g %g\n", VECTOR_ARG(ent->s.origin)); return; // simply do not link model; trigger entity will be added anyway } } } if (ent->area.prev) SV_UnlinkEdict(ent); // unlink from old position (i.e. relink edict) if (ent == ge->edicts) return; // don't add the world if (!ent->inuse) return; entityHull_t &ex = ents[NUM_FOR_EDICT(ent)]; memset(&ex, 0, sizeof(entityHull_t)); ex.owner = ent; ex.axis.FromEuler(ent->s.angles); // set the size VectorSubtract(ent->bounds.maxs, ent->bounds.mins, ent->size); // encode the size into the entity_state for client prediction if (ent->solid == SOLID_BBOX) { // assume that x/y are equal and symetric i = appRound(ent->bounds.maxs[0] / 8); // z is not symetric j = appRound(-ent->bounds.mins[2] / 8); // and z maxs can be negative... k = appRound((ent->bounds.maxs[2] + 32) / 8); // original Q2 have bounded i/j/k/ with lower margin==1 (for client prediction only); this will // produce incorrect collision test when bbox mins/maxs is (0,0,0) i = bound(i, 0, 31); // mins/maxs[0,1] range is -248..0/0..248 j = bound(j, 0, 31); // mins[2] range is [-248..0] k = bound(k, 0, 63); // maxs[2] range is [-32..472] // if SVF_DEADMONSTER, s.solid should be 0 ent->s.solid = (ent->svflags & SVF_DEADMONSTER) ? 0 : (k<<10) | (j<<5) | i; i *= 8; j *= 8; k *= 8; ex.bounds.mins.Set(-i, -i, -j); ex.bounds.maxs.Set(i, i, k - 32); ex.bounds.GetCenter(ex.center); ex.center.Add(ent->s.origin); ex.model = NULL; ex.radius = VectorDistance(ex.bounds.maxs, ex.bounds.mins) / 2; } else if (ent->solid == SOLID_BSP) { ex.model = sv.models[ent->s.modelindex]; if (!ex.model) Com_DropError("MOVETYPE_PUSH with a non bsp model"); CVec3 v; ex.model->bounds.GetCenter(v); UnTransformPoint(ent->s.origin, ex.axis, v, ex.center); ex.radius = ex.model->radius; ent->s.solid = 31; // a SOLID_BBOX will never create this value (mins=(-248,-248,0) maxs=(248,248,-32)) } else if (ent->solid == SOLID_TRIGGER) { ent->s.solid = 0; // check for model link ex.model = sv.models[ent->s.modelindex]; if (!ex.model) { // model not attached by game, check entstring //?? can optimize: add 'bool spawningEnts', set to 'true' before SpawnEntities() //?? and 'false' after; skip code below when 'false' for (triggerModelLink_t *link = bspfile.modelLinks; link; link = link->next) #define CMP(n) (fabs(ent->s.origin[n] - link->origin[n]) < 0.5f) if (CMP(0) && CMP(1) && CMP(2)) { CBspModel *model = CM_InlineModel(link->modelIdx); VectorSubtract(model->bounds.maxs, ent->s.origin, ent->bounds.maxs); VectorSubtract(model->bounds.mins, ent->s.origin, ent->bounds.mins); break; } #undef CMP } } else ent->s.solid = 0; // set the abs box if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { // expand for rotation for (i = 0; i < 3 ; i++) { ent->absBounds.mins[i] = ex.center[i] - ex.radius; ent->absBounds.maxs[i] = ex.center[i] + ex.radius; } } else { // normal VectorAdd(ent->s.origin, ent->bounds.mins, ent->absBounds.mins); VectorAdd(ent->s.origin, ent->bounds.maxs, ent->absBounds.maxs); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch for (i = 0; i < 3; i++) { ent->absBounds.mins[i] -= 1; ent->absBounds.maxs[i] += 1; } // link to PVS leafs ent->num_clusters = 0; ent->zonenum = 0; ent->zonenum2 = 0; // get all leafs, including solids CBspLeaf *leafs[MAX_TOTAL_ENT_LEAFS]; int topnode; int num_leafs = CM_BoxLeafs(ent->absBounds, ARRAY_ARG(leafs), &topnode); // set zones int clusters[MAX_TOTAL_ENT_LEAFS]; for (i = 0; i < num_leafs; i++) { clusters[i] = leafs[i]->cluster; int zone = leafs[i]->zone; if (zone) { // doors may legally straggle two zones, // but nothing should evern need more than that if (ent->zonenum && ent->zonenum != zone) { if (ent->zonenum2 && ent->zonenum2 != zone && sv.state == ss_loading) Com_DPrintf("Object touching 3 zones at %g %g %g\n", VECTOR_ARG(ent->absBounds.mins)); ent->zonenum2 = zone; } else ent->zonenum = zone; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = topnode; } else { ent->num_clusters = 0; for (i = 0; i < num_leafs; i++) { if (clusters[i] == -1) continue; // not a visible leaf for (j = 0; j < i; j++) if (clusters[j] == clusters[i]) break; if (j == i) { if (ent->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = topnode; break; } ent->clusternums[ent->num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if (!ent->linkcount) ent->s.old_origin = ent->s.origin; ent->linkcount++; if (ent->solid == SOLID_NOT) return; // find the first node that the ent's box crosses areanode_t *node = areaNodes; while (node->axis != -1) { if (ent->absBounds.mins[node->axis] > node->dist) node = node->children[0]; else if (ent->absBounds.maxs[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in areanode_t *node2 = node; if (ent->solid == SOLID_TRIGGER) { InsertLinkBefore(ent->area, node->trigEdicts); for ( ; node2; node2 = node2->parent) node2->numTrigEdicts++; } else { InsertLinkBefore(ent->area, node->solidEdicts); for ( ; node2; node2 = node2->parent) node2->numSolidEdicts++; } ex.area = node; unguard; }
CSkelMeshViewer::CSkelMeshViewer(CSkeletalMesh* Mesh0, CApplication* Window) : CMeshViewer(Mesh0->OriginalMesh, Window) , Mesh(Mesh0) , AnimIndex(-1) , IsFollowingMesh(false) , ShowSkel(0) , ShowLabels(false) , ShowAttach(false) , ShowUV(false) { CSkelMeshInstance *SkelInst = new CSkelMeshInstance(); SkelInst->SetMesh(Mesh); if (GForceAnimSet) { CAnimSet *AttachAnim = GetAnimSet(GForceAnimSet); if (!AttachAnim) { appPrintf("WARNING: specified wrong AnimSet (%s class) object to attach\n", GForceAnimSet->GetClassName()); GForceAnimSet = NULL; } if (AttachAnim) SkelInst->SetAnim(AttachAnim); } else if (Mesh->OriginalMesh->IsA("SkeletalMesh")) // UE2 class { const USkeletalMesh *OriginalMesh = static_cast<USkeletalMesh*>(Mesh->OriginalMesh); if (OriginalMesh->Animation) SkelInst->SetAnim(OriginalMesh->Animation->ConvertedAnim); } Inst = SkelInst; // compute bounds for the current mesh CVec3 Mins, Maxs; if (Mesh0->Lods.Num()) { const CSkelMeshLod &Lod = Mesh0->Lods[0]; ComputeBounds(&Lod.Verts[0].Position, Lod.NumVerts, sizeof(CSkelMeshVertex), Mins, Maxs); // ... transform bounds SkelInst->BaseTransformScaled.TransformPointSlow(Mins, Mins); SkelInst->BaseTransformScaled.TransformPointSlow(Maxs, Maxs); } else { Mins = Maxs = nullVec3; } // extend bounds with additional meshes for (int i = 0; i < TaggedMeshes.Num(); i++) { CSkelMeshInstance* Inst = TaggedMeshes[i]; if (Inst->pMesh != SkelInst->pMesh) { const CSkeletalMesh *Mesh2 = Inst->pMesh; // the same code for Inst CVec3 Bounds2[2]; const CSkelMeshLod &Lod2 = Mesh2->Lods[0]; ComputeBounds(&Lod2.Verts[0].Position, Lod2.NumVerts, sizeof(CSkelMeshVertex), Bounds2[0], Bounds2[1]); Inst->BaseTransformScaled.TransformPointSlow(Bounds2[0], Bounds2[0]); Inst->BaseTransformScaled.TransformPointSlow(Bounds2[1], Bounds2[1]); ComputeBounds(Bounds2, 2, sizeof(CVec3), Mins, Maxs, true); // include Bounds2 into Mins/Maxs } // reset animation for all meshes TaggedMeshes[i]->TweenAnim(NULL, 0); } InitViewerPosition(Mins, Maxs); #if HIGHLIGHT_CURRENT TimeSinceCreate = -2; // ignore first 2 frames: 1st frame will be called after mesh changing, 2nd frame could load textures etc #endif #if SHOW_BOUNDS appPrintf("Bounds.min = %g %g %g\n", FVECTOR_ARG(Mesh->BoundingBox.Min)); appPrintf("Bounds.max = %g %g %g\n", FVECTOR_ARG(Mesh->BoundingBox.Max)); appPrintf("Origin = %g %g %g\n", FVECTOR_ARG(Mesh->MeshOrigin)); appPrintf("Sphere = %g %g %g R=%g\n", FVECTOR_ARG(Mesh->BoundingSphere), Mesh->BoundingSphere.R); appPrintf("Offset = %g %g %g\n", VECTOR_ARG(offset)); #endif // SHOW_BOUNDS }
static void DrawSurfInfo() { static const flagInfo_t surfNames[] = { #define T(name) {SURF_##name, #name} T(LIGHT), T(SLICK), T(SKY), T(WARP), T(TRANS33), T(TRANS66), T(FLOWING), T(NODRAW), // Kingpin flags T(ALPHA), T(DIFFUSE), T(SPECULAR), // new flags (temp ??) T(AUTOFLARE) #undef T }; static const char *materialNames[MATERIAL_COUNT] = { "silent", "concrete", "fabric", "gravel", "metal", "metal_l", "snow", "tin", "tile", "wood", "water", "glass", "dirt" }; CVec3 start, end; start = cl.refdef.vieworg; Euler2Vecs(cl.refdef.viewangles, &end, NULL, NULL); end.Scale(500); end.Add(start); unsigned cont = r_surfinfo->integer & 4 ? MASK_ALL : MASK_SHOT|MASK_WATER; trace_t trace; #if TRACE_DEBUG cm_showTrace = true; #endif //static CBox nullBox = {{-2,-2,-2},{2,2,2}}; //static CBox nullBox = {{-10,-10,-10},{10,10,10}}; //static CBox nullBox = {{-20,-20,-20},{20,20,20}}; CM_BoxTrace(trace, start, end, nullBox, 0, cont); CM_ClipTraceToModels(trace, start, end, nullBox, cont); #if TRACE_DEBUG cm_showTrace = false; #endif if (!(r_surfinfo->integer & 2)) CL_EntityTrace(trace, start, end, nullBox, cont); V_AddLight(trace.endpos, 5, 0.3, 0.6, 0.3); //?? keep or remove? intens > trace bounds! if (trace.fraction < 1.0) { if (trace.brushNum >= 0) { // brush RE_DrawTextLeft(va("BrushNum: %d", trace.brushNum), RGB(0.2,0.4,0.1)); if (r_surfinfo->integer & 8) { // visualize brush if (!debugMem) debugMem = new CMemoryChain; CBrush *brush = CM_BuildBrush(trace.brushNum, debugMem); if (brush) RE_DrawBrush(brush, "brush", trace.brushNum); else RE_DrawTextLeft(va("brush %d: error", trace.brushNum), RGB(1,0,0)); } } RE_DrawTextLeft("Surface info:\n-------------", RGB(0.4,0.4,0.6)); RE_DrawTextLeft(va("Point: %g %g %g", VECTOR_ARG(trace.endpos)), RGB(0.2,0.4,0.1)); csurface_t *surf = trace.surface; RE_DrawTextLeft(va("Normal: %g %g %g", VECTOR_ARG(trace.plane.normal)), RGB(0.2,0.4,0.1)); if (surf->fullName[0]) // non-null surface { RE_DrawTextLeft(va("Texture: %s", surf->fullName), RGB(0.2,0.4,0.1)); if (surf->value) RE_DrawTextLeft(va("Value: %d", surf->value), RGB(0.2,0.4,0.1)); DrawFlag(surf->flags, ARRAY_ARG(surfNames), "SURF_"); // material const char *s = (surf->material > 0 && surf->material < MATERIAL_COUNT) ? materialNames[surf->material] : "?? bad ??"; RE_DrawTextLeft(va("Material: %s", s), RGB(0.3,0.6,0.4)); } DecodeContents(trace.contents); if (trace.ent) { clEntityState_t *ent = (clEntityState_t*)trace.ent; RE_DrawTextLeft("\nEntity:\n-------", RGB(0.4,0.4,0.6)); RE_DrawTextLeft(va("Origin: %g %g %g", VECTOR_ARG(ent->origin)), RGB(0.2,0.4,0.1)); RE_DrawTextLeft(va("fx: %X rfx: %X", ent->effects, ent->renderfx), RGB(0.2,0.4,0.1)); if (ent->modelindex) RE_DrawTextLeft(va("model: %s", ModelName(ent->modelindex)), RGB(0.2,0.4,0.1)); if (ent->modelindex2) RE_DrawTextLeft(va("model2: %s", ModelName(ent->modelindex2)), RGB(0.2,0.4,0.1)); if (ent->modelindex3) RE_DrawTextLeft(va("model3: %s", ModelName(ent->modelindex3)), RGB(0.2,0.4,0.1)); if (ent->modelindex4) RE_DrawTextLeft(va("model4: %s", ModelName(ent->modelindex4)), RGB(0.2,0.4,0.1)); CL_AddEntityBox(ent, RGB(1,0.1,0.1)); } RE_DrawTextLeft(""); // empty line } }