private int GetNextGoodFrameIndex(int channel, int curFrame) { PS1AnimationChannel ch = anim.channels[channel]; PS1AnimationKeyframe frame = ch.frames[curFrame]; int skipFrames = 0; while (frame.ntto < 0) { skipFrames++; if (skipFrames >= ch.frames.Length) { break; } frame = ch.frames[(curFrame + skipFrames) % ch.frames.Length]; } if (skipFrames == ch.frames.Length) { return(-1); } return(skipFrames); }
public void UpdateAnimation() { if (IsLoaded && anim != null && channelObjects != null && subObjects != null && anim.num_frames != 0) { if (currentFrame >= anim.num_frames) { currentFrame %= anim.num_frames; } // First pass: reset TRS for all sub objects for (int i = 0; i < channelParents.Length; i++) { channelParents[i] = false; } // Create hierarchy for this frame for (int i = 0; i < anim.hierarchies.Length; i++) { PS1AnimationHierarchy h = anim.hierarchies[i]; if (h.child > anim.channels.Length || h.parent > anim.channels.Length || h.child < 1 || h.parent < 1) { continue; } int ch_child = h.child - 1; int ch_parent = h.parent - 1; channelObjects[ch_child].transform.SetParent(channelObjects[ch_parent].transform); channelObjects[ch_child].transform.localPosition = Vector3.zero; channelObjects[ch_child].transform.localRotation = Quaternion.identity; channelObjects[ch_child].transform.localScale = Vector3.one; channelParents[ch_child] = true; } PS1AnimationVector[] animPositions = h.animPositions; PS1AnimationQuaternion[] animRotations = h.animRotations; PS1AnimationVector[] animScales = h.animScales; switch (anim.file_index) { case 1: animPositions = a1h?.animPositions; animRotations = a1h?.animRotations; animScales = a1h?.animScales; break; case 2: animPositions = a2h?.animPositions; animRotations = a2h?.animRotations; animScales = a2h?.animScales; break; } int[] channelNttoChanges = new int[anim.channels.Length]; for (int i = 0; i < anim.channels.Length; i++) { PS1AnimationChannel ch = anim.channels[i]; if (ch.frames.Length < 1) { continue; } PS1AnimationKeyframe frame = ch.frames[0]; PS1AnimationKeyframe nextKF = null; int?posInd = null; int?rotInd = null; int?sclInd = null; int curChannelFrame = 0; int nextChannelFrame = 0; int curFrameIndex = 0; channelNttoChanges[i] = -1; int?previousNtto = null; 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) { if (!previousNtto.HasValue || previousNtto.Value != ch.frames[f].ntto) { channelNttoChanges[i]++; previousNtto = ch.frames[f].ntto; } frame = ch.frames[f]; curChannelFrame = nextChannelFrame; curFrameIndex = f; if (frame.pos.HasValue) { posInd = frame.pos.Value; } if (frame.rot.HasValue) { rotInd = frame.rot.Value; } if (frame.scl.HasValue) { sclInd = frame.scl.Value; } nextChannelFrame++; if (frame.extraDuration.HasValue && frame.extraDuration.Value > 0) { nextChannelFrame += frame.extraDuration.Value; } if (currentFrame < nextChannelFrame) { break; } } } 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]; } } float interpolation = 0f; if (nextKF != null) { interpolation = (currentFrame - curChannelFrame) / (float)(nextChannelFrame - curChannelFrame); } if (currentActivePO[i] == -2) { for (int j = 0; j < subObjectMorph[i].Length; j++) { GameObject morphGao = subObjectMorph[i][j]; if (morphGao != null && morphGao.activeSelf) { morphGao.SetActive(false); } } } if (!frame.ntto.HasValue || frame.ntto.Value >= 0 || frame.ntto.Value == -20) { GameObject physicalObject = null; short poNum; if (subObjectMorph?[i]?[currentFrame] != null) { physicalObject = subObjectMorph[i][currentFrame]; poNum = -2; physicalObject.SetActive(true); } else { poNum = (frame.ntto.HasValue && frame.ntto.Value != -20) ? frame.ntto.Value : (short)-1; poNum = (short)Array.IndexOf(channelNTTO[i], poNum); physicalObject = poNum >= 0 ? subObjects[i][poNum] : null; } if (poNum != currentActivePO[i]) { if (currentActivePO[i] >= 0 && subObjects[i][currentActivePO[i]] != null) { subObjects[i][currentActivePO[i]].SetActive(false); } currentActivePO[i] = poNum; if (physicalObject != null) { physicalObject.SetActive(true); } } if (physicalObject != null) { if (frame.HasFlag(PS1AnimationKeyframe.AnimationFlags.FlipX)) { physicalObject.transform.localScale = new Vector3(-1, 1, 1); } else { physicalObject.transform.localScale = Vector3.one; } } if (!channelParents[i]) { channelObjects[i].transform.SetParent(transform); } if (posInd.HasValue) { Vector3 pos = animPositions[posInd.Value].vector; if (interpolation > 0f && nextKF.pos.HasValue) { pos = Vector3.Lerp(pos, animPositions[nextKF.pos.Value].vector, interpolation); } channelObjects[i].transform.localPosition = pos; } if (rotInd.HasValue) { Quaternion rot = animRotations[rotInd.Value].quaternion; if (interpolation > 0f && nextKF.rot.HasValue) { rot = Quaternion.Lerp(rot, animRotations[nextKF.rot.Value].quaternion, interpolation); } channelObjects[i].transform.localRotation = rot; } if (sclInd.HasValue) { Vector3 scl = animScales[sclInd.Value].GetVector(factor: 256f * 16f); if (interpolation > 0f && nextKF.scl.HasValue) { scl = Vector3.Lerp(scl, animScales[nextKF.scl.Value].GetVector(factor: 256f * 16f), interpolation); } /*scl = new Vector3( * 1f / (scl.x != 0f ? scl.x : 256f), * 1f / (scl.y != 0f ? scl.y : 256f), * 1f / (scl.z != 0f ? scl.z : 256f));*/ channelObjects[i].transform.localScale = scl; } } else { switch (frame.ntto.Value) { case -13: // morph break; case -9: // 6 break; case -8: break; case -7: // 5 break; case -6: // 4 break; case -5: // 3 break; case -4: // 2 break; case -3: // sound event related. extraDuration is sound bank break; case -2: // 1 break; case -1: break; } } /*if (frame.pos.HasValue) { * channelObjects[i].transform.localPosition = h.animPositions[frame.pos.Value].vector; * } * if (frame.rot.HasValue) { * channelObjects[i].transform.localRotation = h.quaternions[frame.rot.Value].quaternion; * } * if (frame.scl.HasValue) { * Vector3 v = h.animScales[frame.scl.Value].vector; * v = new Vector3( * (1 / 256f) * (v.x != 0f ? v.x : 256f), * (1 / 256f) * (v.y != 0f ? v.y : 256f), * (1 / 256f) * (v.z != 0f ? v.z : 256f)); * channelObjects[i].transform.localScale = v; * }*/ } if (hasBones && anim.bones?.Length > 0) { for (int i = 0; i < anim.channels.Length; i++) { if (currentActivePO[i] >= 0 && subObjectBones[i][currentActivePO[i]] != null) { GameObject[] bones = subObjectBones[i][currentActivePO[i]]; int bonesIndex = Array.FindIndex(anim.bones, b => b.ind_ntto_channel == i); if (bonesIndex != -1) { int curFrameIndex = bonesIndex + (channelNttoChanges[i] >= 0 ? channelNttoChanges[i] : 0); PS1AnimationBoneChannelLinks b = anim.bones[curFrameIndex]; if (b.ind_ntto_channel != i) { print("Channel did not match!"); } ushort[] indices = b.indices; for (int j = 1; j < bones.Length; j++) { bones[j].transform.SetParent(channelObjects[indices[j - 1]].transform); bones[j].transform.localPosition = Vector3.zero; bones[j].transform.localRotation = Quaternion.identity; bones[j].transform.localScale = Vector3.one; } } } } } } }
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; } }