public async Task LoadAnims() { // Read anims.bin Reader reader = files_array[SMem.Anims].reader; loadingState = "Loading animations"; await WaitIfNecessary(); uint num_anims = reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); Pointer eof = null; romAnims = new ROMAnimation[num_anims]; for (uint i = 0; i < num_anims; i++) { uint offset = reader.ReadUInt32(); romAnims[i] = new ROMAnimation() { compressed = (offset & 0x80000000) == 0x80000000, index = i }; romAnims[i].Init(new Pointer(offset & 0x7FFFFFFF, files_array[SMem.Anims])); if (i > 0) { romAnims[i - 1].compressedSize = romAnims[i].Offset.offset - romAnims[i - 1].Offset.offset; } } eof = new Pointer(reader.ReadUInt32(), files_array[SMem.Anims]); // EOF if (num_anims > 0) { romAnims[num_anims - 1].compressedSize = eof.offset - romAnims[num_anims - 1].Offset.offset; } for (uint i = 0; i < num_anims; i++) { romAnims[i].Read(reader); } // Read shAnims.bin reader = files_array[SMem.ShAnims].reader; List <ROMShAnimation> shAnimsList = new List <ROMShAnimation>(); while (reader.BaseStream.Position < reader.BaseStream.Length) { ROMShAnimation shAnim = new ROMShAnimation(); shAnim.Init(Pointer.Current(reader)); shAnim.Read(reader, true); shAnimsList.Add(shAnim); } shAnims = shAnimsList.ToArray(); // Read cuttable.bin reader = files_array[SMem.CutTable].reader; cutTable = new ROMAnimationCutTable(); cutTable.Init(Pointer.Current(reader)); cutTable.length = (ushort)num_anims; cutTable.Read(reader, true); }
public void UpdateAnimation() { if (IsLoaded && shAnim != null && animCuts != null && animCuts.Length > 0 && channelObjects != null && subObjects != null) { if (currentFrame >= shAnim.num_frames) { currentFrame %= shAnim.num_frames; } UpdateAnimationCut(); // First pass: reset TRS for all sub objects for (int i = 0; i < channelParents[currentAnimCut].Length; i++) { channelParents[currentAnimCut][i] = false; } ROMAnimation anim = ROMStruct.Loader.romAnims[animCuts[currentAnimCut].index]; uint currentRelativeFrame = currentFrame - anim.a3d.this_start_onlyFrames; AnimOnlyFrame of = anim.onlyFrames[currentRelativeFrame]; // Create hierarchy for this frame for (int i = of.start_hierarchies_for_frame; i < of.start_hierarchies_for_frame + of.num_hierarchies_for_frame; i++) { AnimHierarchy h = anim.hierarchies[i]; if (!channelIDDictionary[currentAnimCut].ContainsKey(h.childChannelID) || !channelIDDictionary[currentAnimCut].ContainsKey(h.parentChannelID)) { continue; } List <int> ch_child_list = GetChannelByID(h.childChannelID); List <int> ch_parent_list = GetChannelByID(h.parentChannelID); foreach (int ch_child in ch_child_list) { foreach (int ch_parent in ch_parent_list) { channelObjects[currentAnimCut][ch_child].transform.SetParent(channelObjects[currentAnimCut][ch_parent].transform); channelParents[currentAnimCut][ch_child] = true; } } //channelObjects[ch_child].transform.SetParent(channelObjects[ch_parent].transform); } // Final pass int currentKFIndex = 0; for (int i = 0; i < anim.channels.Length; i++) { AnimChannel ch = anim.channels[i]; // Select keyframe AnimKeyframe kf = null; int selectedKFindex = 0; for (int k = 0; k < ch.num_keyframes; k++) { if (anim.keyframes[currentKFIndex + k].frame <= currentFrame) { kf = anim.keyframes[currentKFIndex + k]; selectedKFindex = k; } else { break; } } AnimVector pos = anim.vectors[kf.positionVector]; AnimQuaternion qua = anim.quaternions[kf.quaternion]; AnimVector scl = anim.vectors[kf.scaleVector]; AnimNumOfNTTO numOfNTTO = anim.numOfNTTO[(i * anim.a3d.num_numNTTO) + of.numOfNTTO]; AnimNTTO ntto = null; int poNum = -1; GameObject physicalObject = null; if (numOfNTTO.numOfNTTO != 0xFFFF) { poNum = numOfNTTO.numOfNTTO; ntto = anim.ntto[poNum]; physicalObject = subObjects[currentAnimCut][i][poNum]; } Vector3 vector = pos.vector; Quaternion quaternion = qua.quaternion; Vector3 scale = scl.vector; int framesSinceKF = (int)currentFrame - (int)kf.frame; AnimKeyframe nextKF = null; int framesDifference; float interpolation; if (selectedKFindex == ch.num_keyframes - 1) { nextKF = anim.keyframes[currentKFIndex]; framesDifference = anim.a3d.total_num_onlyFrames - 1 + (int)nextKF.frame - (int)kf.frame; if (framesDifference == 0) { interpolation = 0; } else { //interpolation = (float)(nextKF.interpolationFactor * (framesSinceKF / (float)framesDifference) + 1.0 * nextKF.interpolationFactor); interpolation = framesSinceKF / (float)framesDifference; } } else { nextKF = anim.keyframes[currentKFIndex + selectedKFindex + 1]; framesDifference = (int)nextKF.frame - (int)kf.frame; //interpolation = (float)(nextKF.interpolationFactor * (framesSinceKF / (float)framesDifference) + 1.0 * nextKF.interpolationFactor); interpolation = framesSinceKF / (float)framesDifference; } currentKFIndex += ch.num_keyframes; //print(interpolation); //print(a3d.vectors.Length + " - " + nextKF.positionVector); //print(perso.p3dData.family.animBank); AnimVector pos2 = anim.vectors[nextKF.positionVector]; AnimQuaternion qua2 = anim.quaternions[nextKF.quaternion]; AnimVector scl2 = anim.vectors[nextKF.scaleVector]; vector = Vector3.Lerp(pos.vector, pos2.vector, interpolation); quaternion = Quaternion.Lerp(qua.quaternion, qua2.quaternion, interpolation); scale = Vector3.Lerp(scl.vector, scl2.vector, interpolation); //float positionMultiplier = Mathf.Lerp(kf.positionMultiplier, nextKF.positionMultiplier, interpolation); if (poNum != currentActivePO[currentAnimCut][i]) { if (currentActivePO[currentAnimCut][i] == -2 && fullMorphPOs[currentAnimCut] != null && fullMorphPOs[currentAnimCut][i] != null) { foreach (GameObject morphPO in fullMorphPOs[currentAnimCut][i].Values) { if (morphPO.activeSelf) { morphPO.SetActive(false); } } } if (currentActivePO[currentAnimCut][i] >= 0 && subObjects[currentAnimCut][i][currentActivePO[currentAnimCut][i]] != null) { subObjects[currentAnimCut][i][currentActivePO[currentAnimCut][i]].SetActive(false); } currentActivePO[currentAnimCut][i] = poNum; if (physicalObject != null) { physicalObject.SetActive(true); } } if (!channelParents[currentAnimCut][i]) { channelObjects[currentAnimCut][i].transform.SetParent(transform); } channelObjects[currentAnimCut][i].transform.localPosition = vector; // * positionMultiplier; channelObjects[currentAnimCut][i].transform.localRotation = quaternion; channelObjects[currentAnimCut][i].transform.localScale = scale; if (physicalObject != null && anim.a3d.num_morphData > 0 && morphDataArray[currentAnimCut] != null && i < morphDataArray[currentAnimCut].GetLength(0) && currentRelativeFrame < morphDataArray[currentAnimCut].GetLength(1)) { AnimMorphData morphData = morphDataArray[currentAnimCut][i, currentRelativeFrame]; GeometricObject ogPO = perso.p3dData.Value.objectsTable.Value.data.Value.entries[anim.ntto[poNum].object_index].obj.Value.visual.Value; if (morphData != null && morphData.morphProgress != 0 && morphData.morphProgress != 100) { //print("morphing " + physicalObject.name); GeometricObject morphToPO = perso.p3dData.Value.objectsTable.Value.data.Value.entries[morphData.objectIndexTo].obj.Value.visual.Value; ogPO.MorphVertices(physicalObject, morphToPO, morphData.morphProgress / 100f); } else if (morphData != null && morphData.morphProgress == 100) { physicalObject.SetActive(false); GameObject c = fullMorphPOs[currentAnimCut][i][morphData.objectIndexTo]; c.transform.localScale = objectIndexScales.ContainsKey(morphData.objectIndexTo) ? objectIndexScales[morphData.objectIndexTo] : Vector3.one; c.transform.localPosition = Vector3.zero; c.transform.localRotation = Quaternion.identity; c.SetActive(true); currentActivePO[currentAnimCut][i] = -2; } else { ogPO.ResetMorph(physicalObject); } } } } }
void SwitchAnimationCut(int newAnimCut) { if (currentAnimCut != newAnimCut || forceAnimUpdate) { forceAnimUpdate = false; // Init animation this.currentAnimCut = newAnimCut; ROMAnimation anim = ROMStruct.Loader.romAnims[animCuts[currentAnimCut].index]; bool newCut = subObjects[currentAnimCut] == null; if (anim != null && newCut) { //animationSpeed = a3d.speed; // Init channels & subobjects channelObjects[currentAnimCut] = new GameObject[anim.a3d.num_channels]; currentActivePO[currentAnimCut] = new int[anim.a3d.num_channels]; channelParents[currentAnimCut] = new bool[anim.a3d.num_channels]; subObjects[currentAnimCut] = new GameObject[anim.a3d.num_channels][]; if (anim.a3d.num_morphData > 0) { fullMorphPOs[currentAnimCut] = new Dictionary <ushort, GameObject> [anim.a3d.num_channels]; } for (int i = 0; i < anim.a3d.num_channels; i++) { short id = anim.channels[i].id; channelObjects[currentAnimCut][i] = new GameObject($"[Cut {currentAnimCut}] Channel {id}"); channelObjects[currentAnimCut][i].transform.SetParent(transform); currentActivePO[currentAnimCut][i] = -1; AddChannelID(id, i); if (newCut) { subObjects[currentAnimCut][i] = new GameObject[anim.a3d.num_NTTO]; } AnimChannel ch = anim.channels[i]; List <ushort> listOfNTTOforChannel = new List <ushort>(); for (int j = 0; j < anim.onlyFrames.Length; j++) { AnimOnlyFrame of = anim.onlyFrames[j]; //print(ch.numOfNTTO + " - " + of.numOfNTTO + " - " + a3d.numOfNTTO.Length); AnimNumOfNTTO numOfNTTO = anim.numOfNTTO[(i * anim.a3d.num_numNTTO) + of.numOfNTTO]; if (!listOfNTTOforChannel.Contains(numOfNTTO.numOfNTTO) && numOfNTTO.numOfNTTO != 0xFFFF) { listOfNTTOforChannel.Add(numOfNTTO.numOfNTTO); } } if (newCut) { for (int k = 0; k < listOfNTTOforChannel.Count; k++) { int j = listOfNTTOforChannel[k]; AnimNTTO ntto = anim.ntto[j]; if (ntto.IsInvisibleNTTO) { subObjects[currentAnimCut][i][j] = new GameObject(); subObjects[currentAnimCut][i][j].transform.parent = channelObjects[currentAnimCut][i].transform; subObjects[currentAnimCut][i][j].name = "[" + j + "] Invisible NTTO"; subObjects[currentAnimCut][i][j].SetActive(false); } else { if (perso.p3dData.Value.objectsTable.Value != null && perso.p3dData.Value.objectsTable.Value.length > ntto.object_index) { ObjectsTableData.Entry entry = perso.p3dData.Value.objectsTable.Value.data.Value.entries[ntto.object_index]; PhysicalObject o = entry.obj.Value; if (o != null) { //if (o.visualSetType == 1) print(name); GameObject c = o.GetGameObject().gameObject; subObjects[currentAnimCut][i][j] = c; if (entry.scale.HasValue) { objectIndexScales[ntto.object_index] = new Vector3(entry.scale.Value.x, entry.scale.Value.z, entry.scale.Value.y); subObjects[currentAnimCut][i][j].transform.localScale = objectIndexScales[ntto.object_index]; } c.transform.parent = channelObjects[currentAnimCut][i].transform; c.name = "[" + j + "] " + c.name; c.SetActive(false); } } } } } } if (anim.a3d.num_morphData > 0 && anim.morphData != null) { morphDataArray[currentAnimCut] = new AnimMorphData[anim.channels.Length, anim.onlyFrames.Length]; // Iterate over morph data to find the correct channel and keyframe for (int i = 0; i < anim.a3d.num_morphData; i++) { AnimMorphData m = anim.morphData[i]; if (m != null) { /*print("F:" + a3d.num_onlyFrames + " - C:" + a3d.num_channels + " - CF" + (a3d.num_onlyFrames * a3d.num_channels) + " - " + * m.channel + " - " + m.frame + " - " + m.morphProgress + " - " + m.objectIndexTo + " - " + m.byte5 + " - " + m.byte6 + " - " + m.byte7);*/ int channelIndex = this.GetChannelByID(m.channel)[0]; if (channelIndex < morphDataArray[currentAnimCut].GetLength(0) && m.frame < morphDataArray[currentAnimCut].GetLength(1)) { morphDataArray[currentAnimCut][channelIndex, m.frame] = m; if (m.morphProgress == 100 && perso.p3dData.Value.objectsTable.Value != null && perso.p3dData.Value.objectsTable.Value.length > m.objectIndexTo) { if (fullMorphPOs[currentAnimCut][channelIndex] == null) { fullMorphPOs[currentAnimCut][channelIndex] = new Dictionary <ushort, GameObject>(); } if (!fullMorphPOs[currentAnimCut][channelIndex].ContainsKey(m.objectIndexTo)) { ObjectsTableData.Entry entry = perso.p3dData.Value.objectsTable.Value.data.Value.entries[m.objectIndexTo]; PhysicalObject o = entry.obj.Value; if (o != null) { //if (o.visualSetType == 1) print(name); GameObject c = o.GetGameObject().gameObject; fullMorphPOs[currentAnimCut][channelIndex][m.objectIndexTo] = c; if (entry.scale.HasValue) { objectIndexScales[m.objectIndexTo] = new Vector3(entry.scale.Value.x, entry.scale.Value.z, entry.scale.Value.y); c.transform.localScale = objectIndexScales[m.objectIndexTo]; } /*subObjects[i][j].transform.localScale = * subObjects[i][j].scaleMultiplier.HasValue ? subObjects[i][j].scaleMultiplier.Value : Vector3.one;*/ c.transform.parent = channelObjects[currentAnimCut][channelIndex].transform; c.name = "[Morph] " + c.name; c.SetActive(false); } } } } } } } else { morphDataArray = null; } // 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); } } if (channelObjects != null) { for (int i = 0; i < animCuts.Length; i++) { if (channelObjects[i] != null) { if (currentAnimCut == i) { foreach (GameObject g in channelObjects[i]) { g.SetActive(true); } } else { foreach (GameObject g in channelObjects[i]) { g.SetActive(false); } } } } } IsLoaded = true; } }