void InitAnimation() { if (forceAnimUpdate) { forceAnimUpdate = false; DeinitAnimation(); // Init animation if (anim != null) { //animationSpeed = a3d.speed; // Init channels & subobjects subObjects = new GameObject[anim.num_channels][]; channelObjects = new GameObject[anim.num_channels]; subObjectBones = new GameObject[anim.num_channels][][]; subObjectMorph = new GameObject[anim.num_channels][]; //if (anim.a3d.num_morphData > 0) fullMorphPOs = new Dictionary<ushort, GameObject>[anim.a3d.num_channels]; currentActivePO = new int[anim.num_channels]; channelParents = new bool[anim.num_channels]; channelNTTO = new short[anim.num_channels][]; for (int i = 0; i < anim.num_channels; i++) { PS1AnimationChannel ch = anim.channels[i]; short id = ch.id; channelObjects[i] = new GameObject("Channel " + id + " - " + ch.Offset); channelObjects[i].transform.SetParent(transform); channelObjects[i].transform.localPosition = Vector3.zero; currentActivePO[i] = -1; AddChannelID(id, i); channelNTTO[i] = ch.frames?.SelectMany(f => (f.ntto != null && f.ntto >= 1) ? new short[] { f.ntto.Value } : new short[0]).Distinct().ToArray(); subObjects[i] = new GameObject[channelNTTO[i] != null ? channelNTTO[i].Length : 0]; subObjectBones[i] = new GameObject[subObjects[i].Length][]; subObjectMorph[i] = new GameObject[anim.num_frames]; ObjectsTable geometricObjectsDynamic = h.geometricObjectsDynamic; switch (anim.file_index) { case 1: geometricObjectsDynamic = a1h?.geometricObjectsDynamic; break; case 2: geometricObjectsDynamic = a2h?.geometricObjectsDynamic; break; } for (int k = 0; k < subObjects[i].Length; k++) { int j = (int)channelNTTO[i][k] - 1; if (j == 0xFFFF || j < 0 || j > geometricObjectsDynamic.length.Value) { subObjects[i][k] = new GameObject(); subObjects[i][k].transform.parent = channelObjects[i].transform; subObjects[i][k].name = "[" + j + "] Invisible NTTO"; subObjects[i][k].SetActive(false); } else { GameObject[] bones = null; GameObject c = geometricObjectsDynamic.GetGameObject(j, perso.p3dData?.collisionMapping, out bones); if (c != null) { //GeometricObject geo = geometricObjectsDynamic.entries[j].geo; if (Settings.s.hasDeformations && bones != null) { subObjectBones[i][k] = bones; hasBones = true; } } subObjects[i][k] = c; /*if (entry.scale.HasValue) { * objectIndexScales[ntto.object_index] = new Vector3(entry.scale.Value.x, entry.scale.Value.z, entry.scale.Value.y); * subObjects[i][j].transform.localScale = objectIndexScales[ntto.object_index]; * }*/ c.transform.parent = channelObjects[i].transform; c.name = "[" + j + "] " + c.name; c.SetActive(false); } subObjects[i][k].transform.localPosition = Vector3.zero; subObjects[i][k].transform.localRotation = Quaternion.identity; subObjects[i][k].transform.localScale = Vector3.one; } // Calculate morph objects int nextChannelFrame = 0; int curChannelFrame = 0; for (int f = 0; f < ch.frames.Length; f++) { if (!ch.frames[f].ntto.HasValue || ch.frames[f].ntto >= 0 || ch.frames[f].ntto == -20) { PS1AnimationKeyframe frame = ch.frames[f]; curChannelFrame = nextChannelFrame; nextChannelFrame++; if (frame.extraDuration.HasValue && frame.extraDuration.Value > 0) { nextChannelFrame += frame.extraDuration.Value; } int curFrameIndex = f; PS1AnimationKeyframe nextKF = null; if (frame.extraDuration.HasValue && frame.extraDuration.Value > 0 && curFrameIndex + 1 < ch.frames.Length) { nextKF = ch.frames[curFrameIndex + 1]; //print(ch.frames[0].Offset); if (nextKF.ntto.HasValue && (nextKF.ntto.Value < 0 && nextKF.ntto.Value != -20)) { nextKF = ch.frames[curFrameIndex + 2]; } } if (ch.frames[f].HasFlag(PS1AnimationKeyframe.AnimationFlags.Morph) && ch.frames[f].HasFlag(PS1AnimationKeyframe.AnimationFlags.NTTO)) { int j = ch.frames[f].ntto.Value - 1; int morphJ = ch.frames[f].morphNTTO.Value - 1; if (j < 0 || j > geometricObjectsDynamic.length.Value || morphJ < 0 || morphJ > geometricObjectsDynamic.length.Value) { } else { int duration = nextChannelFrame - curChannelFrame; if (duration < 2 || nextKF == null || nextKF.ntto != frame.ntto || nextKF.morphNTTO != frame.morphNTTO || nextKF.morphProgress == frame.morphProgress) { if (frame.morphProgress.Value == 0) { continue; } float morphProgress = frame.morphProgress.Value / 100f; GameObject c = geometricObjectsDynamic.GetGameObject(j, perso.p3dData?.collisionMapping, out _, morphI: morphJ, morphProgress: morphProgress); for (int d = 0; d < duration; d++) { subObjectMorph[i][curChannelFrame + d] = c; } c.transform.parent = channelObjects[i].transform; c.name = "[" + j + "] " + c.name + " - " + morphJ; c.SetActive(false); c.transform.localPosition = Vector3.zero; c.transform.localRotation = Quaternion.identity; c.transform.localScale = Vector3.one; } else { for (int d = 0; d < duration; d++) { float interpolation = d / (float)duration; float morphProgress = Mathf.Lerp(frame.morphProgress.Value / 100f, nextKF.morphProgress.Value / 100f, interpolation); GameObject c = geometricObjectsDynamic.GetGameObject(j, perso.p3dData?.collisionMapping, out _, morphI: morphJ, morphProgress: morphProgress); subObjectMorph[i][curChannelFrame + d] = c; c.transform.parent = channelObjects[i].transform; c.SetActive(false); c.transform.localPosition = Vector3.zero; c.transform.localRotation = Quaternion.identity; c.transform.localScale = Vector3.one; } } } } } } } // Keep lighting last so that it is applied to all new sub objects /*if (!isAlways) { * controller.sectorManager.ApplySectorLighting(sector, gameObject, OpenSpace.Visual.LightInfo.ObjectLightedFlag.Perso); * } else { * controller.sectorManager.ApplySectorLighting(sector, gameObject, OpenSpace.Visual.LightInfo.ObjectLightedFlag.None); * }*/ } IsLoaded = true; } }
// TODO: Clean up this method, split in smaller parts, update it as more info is available private bool Read(BinaryReader br) { try { Header header = new Header(); header.Read(br); NodesTree nodesTree = new NodesTree(); nodesTree.Read(br); Name = nodesTree.name; br.BaseStream.Seek(header.ContentsTableOffset, SeekOrigin.Begin); ContentsTable contentsTable = new ContentsTable(); contentsTable.Read(br); MaterialsHeader materialsHeader = new MaterialsHeader(); materialsHeader.Read(br); ObjectsTable objectsTable = new ObjectsTable(); objectsTable.SetCount(contentsTable.objectsCount); objectsTable.Read(br); SubObjectsTable subObjectsTable = new SubObjectsTable(); subObjectsTable.SetCount(contentsTable.subObjectsCount); subObjectsTable.Read(br); // In theory, it could be read all the data from current stream position // but it would be safer to read by using the already read offsets // Read the materials table MaterialsTable materialsTable = new MaterialsTable(); materialsTable.SetCount(materialsHeader.materialsCount); br.BaseStream.Seek(materialsHeader.materialsTableOffset, SeekOrigin.Begin); materialsTable.Read(br); // Build the materials list for (int i = 0; i < materialsTable.GetCount(); i++) { Material mat = materialsTable[i].GetMaterial(br); Materials.Add(mat); } // TODO: Read data for all the subobjects // Read data for all the objects for (int i = 0; i < objectsTable.GetCount(); i++) { Model model = new Model(); br.BaseStream.Seek(objectsTable[i].nameAddress, SeekOrigin.Begin); model.name = Text.ReadText(br); if (objectsTable[i].materialDefsAddress != null && objectsTable[i].materialDefsCount > 0) { br.BaseStream.Seek(objectsTable[i].materialDefsAddress.Value, SeekOrigin.Begin); MaterialDef matDef = new MaterialDef(); matDef.Read(br); model.materialIndices = matDef.materialIndices; } if (objectsTable[i].childDefsAddress != null && objectsTable[i].childsDefsCount > 0) { br.BaseStream.Seek(objectsTable[i].childDefsAddress.Value, SeekOrigin.Begin); ChildsDef childsDef = new ChildsDef(); childsDef.SetCount(objectsTable[i].childsDefsCount); childsDef.Read(br); // TODO: Decide how to add it to the model } if (objectsTable[i].data1Address != null && objectsTable[i].data1Count > 0) { List <int> meshesAddress = new List <int>(); br.BaseStream.Seek(objectsTable[i].data1Address.Value, SeekOrigin.Begin); for (int j = 0; j < objectsTable[i].data1Count; j++) { int address = (int)ReadRelativeOffset(br); meshesAddress.Add(address); } for (int j = 0; j < objectsTable[i].data1Count; j++) { br.BaseStream.Seek(meshesAddress[j], SeekOrigin.Begin); Def def = new Def(); if (!def.Read(br)) { return(false); } if (def.nextChunk != null && def.nextChunk is MeshDef) { MeshDef md = (MeshDef)def.nextChunk; for (int k = 0; k < md.meshesData.Count; k++) { MeshData data = md.meshesData[k]; Mesh mesh = new Mesh(data.boundingBox, data.vertices, data.triangles, data.textureVertices); model.meshes.Add(mesh); } } } } Models.Add(model); } } catch { // TODO: Handle here any error reading return(false); } return(true); }
// Update is called once per frame void Update() { if (IsLoaded) { if (hasStates) { if (stateIndex != currentState) { currentState = stateIndex; SetState(currentState); } } MapLoader l = MapLoader.Loader; if (poListIndex != currentPOList && perso.p3dData != null) { if (poListIndex > 0 && poListIndex < poListNames.Length + 1) { currentPOList = poListIndex; ObjectsTable newOT = ROMStruct.Loader.objectsTables[currentPOList - 1]; perso.p3dData.Value.objectsTable = new Reference <ObjectsTable>(newOT.Index, newOT); } else { poListIndex = 0; currentPOList = 0; perso.p3dData.Value.objectsTable = new Reference <ObjectsTable>(); } forceAnimUpdate = true; SetState(currentState); } } if (!IsLoaded || !(controller.LoadState == Controller.State.Finished || controller.LoadState == Controller.State.Error)) { return; } bool sectorActive = false, insideSectors = false; if (sector == null || IsAlways || AlwaysPlayAnimation || sector.Loaded) { sectorActive = true; } if (sector == null || IsAlways || AlwaysPlayAnimation || controller.sectorManager.activeSector != null) { insideSectors = true; } if (controller.playAnimations && playAnimation && sectorActive) { updateCounter += Time.deltaTime * animationSpeed; // If the camera is not inside a sector, animations will only update 1 out of 2 times (w/ frameskip) to avoid lag if ((!insideSectors && updateCounter >= 2f) || (insideSectors && updateCounter >= 1f)) { uint passedFrames = (uint)Mathf.FloorToInt(updateCounter); updateCounter %= 1; currentFrame += passedFrames; if (shAnim != null && currentFrame >= shAnim.num_frames) { if (autoNextState) { ROMShAnimation prevshAnim = shAnim; GotoAutoNextState(); if (shAnim == prevshAnim) { currentFrame = currentFrame % shAnim.num_frames; UpdateAnimation(); } } else { currentFrame = currentFrame % shAnim.num_frames; UpdateAnimation(); } } else { UpdateAnimation(); } } } }
public async Task LoadData() { Reader reader = files_array[SMem.Data].reader; if (exportTextures) { string state = loadingState; loadingState = "Exporting textures"; await WaitIfNecessary(); ExportTextures(reader); loadingState = state; await WaitIfNecessary(); return; } // Load current level data loadingState = "Loading level data"; await WaitIfNecessary(); LevelHeader lh = GetOrRead <LevelHeader>(reader, (ushort)(CurrentLevel | (ushort)FATEntry.Flag.Fix)); level = lh; loadingState = "Loading additional object lists"; await WaitIfNecessary(); for (ushort i = 0; i < 0x7FFF; i++) { // Only do it a few times because we're trying to load way more than there is, // so it takes really long if we yield for everything if (i % 4096 == 0) { loadingState = "Loading additional object lists: " + (i + 1); await WaitIfNecessary(); } ObjectsTable ot = GetOrRead <ObjectsTable>(reader, (ushort)(i | (ushort)FATEntry.Flag.Fix)); if (ot != null) { objectsTables.Add(ot); } } for (ushort i = 0; i < 0x8000; i++) { // Only do it a few times because we're trying to load way more than there is, // so it takes really long if we yield for everything if (i % 4096 == 0) { loadingState = "Loading additional object lists: " + (i + 1); await WaitIfNecessary(); } ObjectsTable ot = GetOrRead <ObjectsTable>(reader, i); if (ot != null) { objectsTables.Add(ot); } } for (ushort i = 0; i < 0x7FFF; i++) { // Only do it a few times because we're trying to load way more than there is, // so it takes really long if we yield for everything if (i % 4096 == 0) { loadingState = "Loading waypoints: " + (i + 1); await WaitIfNecessary(); } WayPoint wp = GetOrRead <WayPoint>(reader, (ushort)(i | (ushort)FATEntry.Flag.Fix)); } for (ushort i = 0; i < 0x8000; i++) { // Only do it a few times because we're trying to load way more than there is, // so it takes really long if we yield for everything if (i % 4096 == 0) { loadingState = "Loading waypoints: " + (i + 1); await WaitIfNecessary(); } WayPoint wp = GetOrRead <WayPoint>(reader, i); } for (ushort i = 0; i < 0x7FFF; i++) { // Only do it a few times because we're trying to load way more than there is, // so it takes really long if we yield for everything if (i % 4096 == 0) { loadingState = "Loading graphs: " + (i + 1); await WaitIfNecessary(); } Graph g = GetOrRead <Graph>(reader, (ushort)(i | (ushort)FATEntry.Flag.Fix)); } for (ushort i = 0; i < 0x8000; i++) { // Only do it a few times because we're trying to load way more than there is, // so it takes really long if we yield for everything if (i % 4096 == 0) { loadingState = "Loading graphs: " + (i + 1); await WaitIfNecessary(); } Graph g = GetOrRead <Graph>(reader, i); } loadingState = "Initializing hierarchy"; await WaitIfNecessary(); if (lh != null) { if (lh.hierarchyRoot.Value != null) { lh.hierarchyRoot.Value.GetGameObject(); } } }