void USkelModel::Serialize(FArchive &Ar) { guard(USkelModel::Serialize); assert(Ar.IsLoading); // no saving ... Super::Serialize(Ar); // USkelModel data int nummeshes; int numjoints; int numframes; int numsequences; int numskins; int rootjoint; FVector PosOffset; // Offset of creature relative to base FRotator RotOffset; // Offset of creatures rotation TArray<RMesh> meshes; TArray<RJoint> joints; TArray<FRSkelAnimSeq> AnimSeqs; // Compressed animation data for sequence TArray<RAnimFrame> frames; Ar << nummeshes << numjoints << numframes << numsequences << numskins << rootjoint; Ar << meshes << joints << AnimSeqs << frames << PosOffset << RotOffset; int modelIdx; // create all meshes first, then fill them (for better view order) for (modelIdx = 0; modelIdx < meshes.Num(); modelIdx++) { // create new USkeletalMesh // use "CreateClass()" instead of "new USkeletalMesh" to allow this object to be // placed in GObjObjects array and be browsable in a viewer USkeletalMesh *sm = static_cast<USkeletalMesh*>(CreateClass("SkeletalMesh")); char nameBuf[256]; appSprintf(ARRAY_ARG(nameBuf), "%s_%d", Name, modelIdx); const char *name = appStrdupPool(nameBuf); Meshes.Add(sm); // setup UOnject sm->Name = name; sm->Package = Package; sm->PackageIndex = INDEX_NONE; // not really exported sm->Outer = NULL; } // create animation Anim = static_cast<UMeshAnimation*>(CreateClass("MeshAnimation")); Anim->Name = Name; Anim->Package = Package; Anim->PackageIndex = INDEX_NONE; // not really exported Anim->Outer = NULL; ConvertRuneAnimations(*Anim, joints, AnimSeqs); Anim->ConvertAnims(); //?? second conversion // get baseframe assert(strcmp(Anim->AnimSeqs[0].Name, "baseframe") == 0); const TArray<AnalogTrack> &BaseAnim = Anim->Moves[0].AnimTracks; // compute bone coordinates TArray<CCoords> BoneCoords; BuildSkeleton(BoneCoords, joints, BaseAnim); // setup meshes for (modelIdx = 0; modelIdx < meshes.Num(); modelIdx++) { int i, j; const RMesh &src = meshes[modelIdx]; USkeletalMesh *sm = Meshes[modelIdx]; sm->Animation = Anim; // setup ULodMesh sm->RotOrigin = RotOffset; sm->MeshScale.Set(1, 1, 1); sm->MeshOrigin = PosOffset; // copy skeleton sm->RefSkeleton.Empty(joints.Num()); for (i = 0; i < joints.Num(); i++) { const RJoint &J = joints[i]; FMeshBone *B = new(sm->RefSkeleton) FMeshBone; B->Name = J.name; B->Flags = 0; B->ParentIndex = (J.parent > 0) ? J.parent : 0; // -1 -> 0 // copy bone orientations from base animation frame B->BonePos.Orientation = BaseAnim[i].KeyQuat[0]; B->BonePos.Position = BaseAnim[i].KeyPos[0]; } // copy vertices int VertexCount = sm->VertexCount = src.verts.Num(); sm->Points.Empty(VertexCount); for (i = 0; i < VertexCount; i++) { const RVertex &v1 = src.verts[i]; FVector *V = new(sm->Points) FVector; // transform point from local bone space to model space BoneCoords[v1.joint1].UnTransformPoint(CVT(v1.point1), CVT(*V)); } // copy triangles and create wedges // here we create 3 wedges for each triangle. // it is possible to reduce number of wedges by finding duplicates, but we don't // need it here ... int TrisCount = src.tris.Num(); sm->Triangles.Empty(TrisCount); sm->Wedges.Empty(TrisCount * 3); int numMaterials = 0; // should detect real material count for (i = 0; i < TrisCount; i++) { const RTriangle &tri = src.tris[i]; // create triangle VTriangle *T = new(sm->Triangles) VTriangle; T->MatIndex = tri.polygroup; if (numMaterials <= tri.polygroup) numMaterials = tri.polygroup+1; // create wedges for (j = 0; j < 3; j++) { T->WedgeIndex[j] = sm->Wedges.Num(); FMeshWedge *W = new(sm->Wedges) FMeshWedge; W->iVertex = tri.vIndex[j]; W->TexUV = tri.tex[j]; } // reverse order of triangle vertices Exchange(T->WedgeIndex[0], T->WedgeIndex[1]); } // build influences for (i = 0; i < VertexCount; i++) { const RVertex &v1 = src.verts[i]; FVertInfluence *Inf = new(sm->VertInfluences) FVertInfluence; Inf->PointIndex = i; Inf->BoneIndex = v1.joint1; Inf->Weight = v1.weight1; if (Inf->Weight != 1.0f) { // influence for 2nd bone Inf = new(sm->VertInfluences) FVertInfluence; Inf->PointIndex = i; Inf->BoneIndex = v1.joint2; Inf->Weight = 1.0f - v1.weight1; } } // create materials for (i = 0; i < numMaterials; i++) { const char *texName = src.PolyGroupSkinNames[i]; FMeshMaterial *M1 = new(sm->Materials) FMeshMaterial; M1->PolyFlags = src.GroupFlags[i]; M1->TextureIndex = sm->Textures.Num(); if (strcmp(texName, "None") == 0) { // texture should be set from script sm->Textures.Add(NULL); continue; } // find texture in object's package int texExportIdx = Package->FindExport(texName); if (texExportIdx == INDEX_NONE) { appPrintf("ERROR: unable to find export \"%s\" for mesh \"%s\" (%d)\n", texName, Name, modelIdx); continue; } // load and remember texture UMaterial *Tex = static_cast<UMaterial*>(Package->CreateExport(texExportIdx)); sm->Textures.Add(Tex); } // setup UPrimitive properties using 1st animation frame // note: this->BoundingBox and this->BoundingSphere are null const RAnimFrame &F = frames[0]; assert(strcmp(AnimSeqs[0].Name, "baseframe") == 0 && AnimSeqs[0].StartFrame == 0); CVec3 mins, maxs; sm->BoundingBox = F.bounds; mins = CVT(F.bounds.Min); maxs = CVT(F.bounds.Max); CVec3 ¢er = CVT(sm->BoundingSphere); for (i = 0; i < 3; i++) center[i] = (mins[i] + maxs[i]) / 2; sm->BoundingSphere.R = VectorDistance(center, mins); // create CSkeletalMesh sm->ConvertMesh(); } unguard; }
static bool RegisterGameFile(const char *FullName, FVirtualFileSystem* parentVfs = NULL) { guard(RegisterGameFile); // printf("..file %s\n", FullName); if (!parentVfs) // no nested VFSs { const char* ext = strrchr(FullName, '.'); if (ext) { guard(MountVFS); ext++; FVirtualFileSystem* vfs = NULL; FArchive* reader = NULL; #if SUPPORT_ANDROID if (!stricmp(ext, "obb")) { GForcePlatform = PLATFORM_ANDROID; reader = new FFileReader(FullName); if (!reader) return true; reader->Game = GAME_UE3; vfs = new FObbVFS(FullName); } #endif // SUPPORT_ANDROID #if UNREAL4 if (!stricmp(ext, "pak")) { reader = new FFileReader(FullName); if (!reader) return true; reader->Game = GAME_UE4_BASE; vfs = new FPakVFS(FullName); //!! detect game by file name } #endif // UNREAL4 //!! process other VFS types here if (vfs) { assert(reader); // read VF directory if (!vfs->AttachReader(reader)) { // something goes wrong delete vfs; delete reader; return true; } // add game files int NumVFSFiles = vfs->NumFiles(); for (int i = 0; i < NumVFSFiles; i++) { if (!RegisterGameFile(vfs->FileName(i), vfs)) return false; } return true; } unguard; } } bool IsPackage = false; if (FindExtension(FullName, ARRAY_ARG(PackageExtensions))) { IsPackage = true; } else { #if HAS_SUPORT_FILES if (!FindExtension(FullName, ARRAY_ARG(KnownExtensions))) #endif { // ignore any unknown files inside VFS if (parentVfs) return true; // ignore unknown files inside "cooked" or "content" directories if (appStristr(FullName, "cooked") || appStristr(FullName, "content")) return true; // perhaps this file was exported by our tool - skip it if (FindExtension(FullName, ARRAY_ARG(SkipExtensions))) return true; // unknown file type if (++GNumForeignFiles >= MAX_FOREIGN_FILES) appError("Too many unknown files - bad root directory (%s)?", RootDirectory); return true; } } // create entry CGameFileInfo *info = new CGameFileInfo; GameFiles.Add(info); info->IsPackage = IsPackage; info->FileSystem = parentVfs; if (IsPackage) GNumPackageFiles++; if (!parentVfs) { // regular file FILE* f = fopen(FullName, "rb"); if (f) { fseek(f, 0, SEEK_END); info->SizeInKb = (ftell(f) + 512) / 1024; fclose(f); } else { info->SizeInKb = 0; } // cut RootDirectory from filename const char *s = FullName + strlen(RootDirectory) + 1; assert(s[-1] == '/'); info->RelativeName = appStrdupPool(s); } else { // file in virtual file system info->SizeInKb = parentVfs->GetFileSize(FullName); info->RelativeName = appStrdupPool(FullName); } // find filename const char* s = strrchr(info->RelativeName, '/'); if (s) s++; else s = info->RelativeName; info->ShortFilename = s; // find extension s = strrchr(info->ShortFilename, '.'); if (s) s++; info->Extension = s; #if UNREAL3 if (info->IsPackage && (strnicmp(info->ShortFilename, "startup", 7) == 0)) { // Register a startup package // possible name variants: // - startup // - startup_int // - startup_* int startupWeight = 0; if (info->ShortFilename[7] == '.') startupWeight = 30; // "startup.upk" else if (strnicmp(info->ShortFilename+7, "_int.", 5) == 0) startupWeight = 20; // "startup_int.upk" else if (strnicmp(info->ShortFilename+7, "_loc_int.", 9) == 0) startupWeight = 20; // "startup_int.upk" else if (info->ShortFilename[7] == '_') startupWeight = 1; // non-int locale, lower priority - use if when other is not detected if (startupWeight > GStartupPackageInfoWeight) { GStartupPackageInfoWeight = startupWeight; GStartupPackageInfo = info; } } #endif // UNREAL3 // insert CGameFileInfo into hash table int hash = GetHashForFileName(info->ShortFilename, true); info->HashNext = GGameFileHash[hash]; GGameFileHash[hash] = info; #if DEBUG_HASH printf("--> add(%s) pkg=%d hash=%X\n", info->ShortFilename, info->IsPackage, hash); #endif return true; unguardf("%s", FullName); }