/// <summary> /// Applies script state and computes transformations. /// </summary> /// <param name="viewMatrix"></param> /// <param name="parentNode"></param> /// <param name="deltaTime"></param> /// <param name="matrixRotate"></param> public void PrepareForRender(Matrix4x4 viewMatrix, RenderTreeNode parentNode, int deltaTime, bool matrixRotate = false) { var frontendObjectData = FrontendObject.Data; var size = frontendObjectData.Size; var position = frontendObjectData.Position; var rotation = frontendObjectData.Rotation; var color = frontendObjectData.Color; if (frontendObjectData is ImageData imageData) { UpperLeft = imageData.UpperLeft; LowerRight = imageData.LowerRight; } if (CurrentScript != null && (CurrentScript.Length > 0 || CurrentScript.ChainedId != 0xFFFFFFFF) && CurrentScriptTime >= 0) { if (CurrentScriptTime >= CurrentScript.Length) { if ((CurrentScript.Flags & 1) == 1) { Debug.WriteLine("looping script {0:X} for object {1:X}", CurrentScript.Id, FrontendObject.NameHash); CurrentScriptTime = 0; } else if (CurrentScript.ChainedId != 0xFFFFFFFF) { var nextScript = FrontendObject.Scripts.Find(s => s.Id == CurrentScript.ChainedId) ?? throw new Exception( $"Cannot find chained script (object {FrontendObject.NameHash:X}, base script {CurrentScript.Id:X}): {CurrentScript.ChainedId:X}"); Debug.WriteLine("activating chained script for object {1:X}: {0}", nextScript.Name ?? nextScript.Id.ToString("X"), FrontendObject.NameHash); SetScript(nextScript); } } foreach (var track in CurrentScript.Tracks) { switch (track.Offset) { case 0: // Color color = TrackInterpolation.Interpolate <Color4>(track, CurrentScriptTime); break; case 7: // Position position = TrackInterpolation.Interpolate <Vector3>(track, CurrentScriptTime); break; case 10: // Rotation rotation = TrackInterpolation.Interpolate <Quaternion>(track, CurrentScriptTime); break; case 14: // Size size = TrackInterpolation.Interpolate <Vector3>(track, CurrentScriptTime); break; case 17: // UpperLeft { if (FrontendObject is IImage <ImageData> ) { UpperLeft = TrackInterpolation.Interpolate <Vector2>(track, CurrentScriptTime); } else { throw new Exception("Encountered UpperLeft track for a non-image"); } break; } case 19: // LowerRight { if (FrontendObject is IImage <ImageData> ) { LowerRight = TrackInterpolation.Interpolate <Vector2>(track, CurrentScriptTime); } else { throw new Exception("Encountered LowerRight track for a non-image"); } break; } default: throw new IndexOutOfRangeException( $"object {FrontendObject.NameHash:X} script {CurrentScript.Id:X} has unsupported track with offset {track.Offset}"); } } CurrentScriptTime += deltaTime; } var scaleMatrix = Matrix4x4.CreateScale(size.X, size.Y, size.Z); var rotateMatrix = Matrix4x4.CreateFromQuaternion(rotation); var transMatrix = Matrix4x4.CreateTranslation(position.X, position.Y, position.Z); if (matrixRotate) { ObjectMatrix = scaleMatrix * rotateMatrix * transMatrix * viewMatrix; } else { ObjectMatrix = scaleMatrix * transMatrix * viewMatrix; } ObjectRotation = rotation; ObjectColor = color; if (parentNode != null) { ObjectRotation *= parentNode.ObjectRotation; ObjectColor = Color4.Blend(ObjectColor, parentNode.ObjectColor); } }