public bool Raycast(FRay ray, out float closestDistance) { // Convert the ray to local space of this node since all of our raycasts are local. FRay localRay = WMath.TransformRay(ray, Transform.Position, Transform.LocalScale, Transform.Rotation.Inverted()); bool bHit = false; if (m_actorMesh != null) { bHit = m_actorMesh.Raycast(localRay, out closestDistance, true); } else { bHit = WMath.RayIntersectsAABB(localRay, m_objRender.GetAABB().Min, m_objRender.GetAABB().Max, out closestDistance); } if (bHit) { // Convert the hit point back to world space... Vector3 localHitPoint = localRay.Origin + (localRay.Direction * closestDistance); localHitPoint = Vector3.Transform(localHitPoint + Transform.Position, Transform.Rotation); // Now get the distance from the original ray origin and the new worldspace hit point. closestDistance = (localHitPoint - ray.Origin).Length; } return(bHit); }
private void DoOrbitcamUpdate(float deltaTime) { float orbitSensitivityScale = 0.02f; // Angle Axis expects rads, but we have... pixels, so we just move it way down by a factor. Quaternion deltaRot = Quaternion.FromAxisAngle(new Vector3(1, 0, 0), -WInput.MouseDelta.Y * MouseSensitivity * deltaTime * orbitSensitivityScale) * Quaternion.FromAxisAngle(new Vector3(0, 1, 0), -WInput.MouseDelta.X * MouseSensitivity * deltaTime * orbitSensitivityScale); Transform.Rotation *= deltaRot; Vector3 moveDir = Vector3.Zero; if (WInput.GetKey(System.Windows.Input.Key.Q)) { moveDir -= Vector3.UnitY; } if (WInput.GetKey(System.Windows.Input.Key.E)) { moveDir += Vector3.UnitY; } if (moveDir.Length > 0) { moveDir.Normalize(); m_orbitPivot += moveDir * (MoveSpeed / 2) * deltaTime; } m_orbitCameraDistance += -WInput.MouseScrollDelta * 50 * deltaTime; m_orbitCameraDistance = WMath.Clamp(m_orbitCameraDistance, 100, 10000); Transform.Position = m_orbitPivot + Vector3.Transform(new Vector3(0, 0, m_orbitCameraDistance), Transform.Rotation); }
private void DoFlycamUpdate(float deltaTime) { Vector3 moveDir = Vector3.Zero; if (WInput.GetKey(System.Windows.Input.Key.W)) { moveDir -= Vector3.UnitZ; } if (WInput.GetKey(System.Windows.Input.Key.S)) { moveDir += Vector3.UnitZ; } if (WInput.GetKey(System.Windows.Input.Key.D)) { moveDir += Vector3.UnitX; } if (WInput.GetKey(System.Windows.Input.Key.A)) { moveDir -= Vector3.UnitX; } // If they're holding down the shift key adjust their FOV when they scroll, otherwise adjust move speed. MoveSpeed += WInput.MouseScrollDelta * 100 * deltaTime; MoveSpeed = WMath.Clamp(MoveSpeed, 100, 8000); if (WInput.GetMouseButton(1)) { Rotate(deltaTime, WInput.MouseDelta.X, WInput.MouseDelta.Y); } float moveSpeed = WInput.GetKey(System.Windows.Input.Key.LeftShift) ? MoveSpeed * 3f : MoveSpeed; // Make it relative to the current rotation. moveDir = Vector3.Transform(moveDir, Transform.Rotation); // Do Q and E after we transform the moveDir so they're always in worldspace. if (WInput.GetKey(System.Windows.Input.Key.Q)) { moveDir -= Vector3.UnitY; } if (WInput.GetKey(System.Windows.Input.Key.E)) { moveDir += Vector3.UnitY; } // Normalize the move direction moveDir.NormalizeFast(); // Early out if we're not moving this frame. if (moveDir.LengthFast < 0.1f) { return; } Transform.Position += Vector3.Multiply(moveDir, moveSpeed * deltaTime); }
public bool Raycast(FRay ray, out float closestDistance) { // Convert the ray to local space of this node since all of our raycasts are local. FRay localRay; if (DisableRotationAndScaleForRaycasting) { localRay = WMath.TransformRay(ray, Transform.Position, Vector3.One, Quaternion.Identity); } else { localRay = WMath.TransformRay(ray, Transform.Position, VisualScale, Transform.Rotation.Inverted().ToSinglePrecision()); } closestDistance = float.MaxValue; bool bHit = false; if (m_actorMeshes.Count > 0) { foreach (var actor_mesh in m_actorMeshes) { bHit = actor_mesh.Raycast(localRay, out closestDistance, true); if (bHit) { break; } } } else if (m_objRender != null) { if (m_objRender.FaceCullingEnabled && m_objRender.GetAABB().Contains(localRay.Origin)) { // If the camera is inside an OBJ render that has backface culling on, the actor won't actually be visible, so don't select it. return(false); } bHit = WMath.RayIntersectsAABB(localRay, m_objRender.GetAABB().Min, m_objRender.GetAABB().Max, out closestDistance); if (bHit) { // Convert the hit point back to world space... Vector3 localHitPoint = localRay.Origin + (localRay.Direction * closestDistance); Vector3 globalHitPoint = Transform.Position + Vector3.Transform(localHitPoint, Transform.Rotation.ToSinglePrecision()); // Now get the distance from the original ray origin and the new worldspace hit point. closestDistance = (globalHitPoint - ray.Origin).Length; } } return(bHit); }
void IRenderable.Draw(WSceneView view) { Vector3 scale = Transform.LocalScale; Quaternion rotation = Transform.Rotation; Vector3 translation = Transform.Position; if (RoomTransform != null) { rotation = Quaternion.FromAxisAngle(Vector3.UnitY, WMath.DegreesToRadians(RoomTransform.YRotation)); translation = new Vector3(RoomTransform.Translation.X, 0, RoomTransform.Translation.Y); } Matrix4 trs = Matrix4.CreateScale(scale) * Matrix4.CreateFromQuaternion(rotation) * Matrix4.CreateTranslation(translation); foreach (var mesh in m_roomModels) { mesh.Render(view.ViewMatrix, view.ProjMatrix, trs); } }
public static float FindQuaternionTwist(this Quaternion quat, Vector3 axis) { axis.Normalize(); // Get the plane the axis is a normal of Vector3 orthoNormal1, orthoNormal2; WMath.FindOrthoNormals(axis, out orthoNormal1, out orthoNormal2); Vector3 transformed = Vector3.Transform(orthoNormal1, quat); // Project transformed vector onto a plane Vector3 flattened = transformed - (Vector3.Dot(transformed, axis) * axis); flattened.Normalize(); // Get the angle between the original vector and projected transform to get angle around normal float a = (float)Math.Acos((double)Vector3.Dot(orthoNormal1, flattened)); return(WMath.RadiansToDegrees(a)); }
/// <summary> /// Create a Quaternion from Euler Angles. These should be in degrees in [-180, 180] space. /// </summary> public static Quaternion FromEulerAngles(this Quaternion quat, Vector3 eulerAngles) { eulerAngles.X = WMath.DegreesToRadians(eulerAngles.X); eulerAngles.Y = WMath.DegreesToRadians(eulerAngles.Y); eulerAngles.Z = WMath.DegreesToRadians(eulerAngles.Z); double c1 = Math.Cos(eulerAngles.Y / 2f); double s1 = Math.Sin(eulerAngles.Y / 2f); double c2 = Math.Cos(eulerAngles.X / 2f); double s2 = Math.Sin(eulerAngles.X / 2f); double c3 = Math.Cos(eulerAngles.Z / 2f); double s3 = Math.Sin(eulerAngles.Z / 2f); double c1c2 = c1 * c2; double s1s2 = s1 * s2; float w = (float)(c1c2 * c3 - s1s2 * s3); float x = (float)(c1c2 * s3 + s1s2 * c3); float y = (float)(s1 * c2 * c3 + c1 * s2 * s3); float z = (float)(c1 * s2 * c3 - s1 * c2 * s3); return(new Quaternion(x, y, z, w)); }
public LightingPalette Lerp(float t, bool presetA = true) { LightingPalette[] palette = presetA ? TimePresetA : TimePresetB; // Generate a new LightingPalette which is the interpolated values of things. t = WMath.Clamp(t, 0, 1); float scaledT = t * (palette.Length - 1); int lowerIndex = (int)scaledT; int upperIndex = (int)(scaledT + 1f); float newT = scaledT - (int)scaledT; //Console.WriteLine("t: {0} scaledT: {1} lIndex: {2} uIndex: {3} newT: {4}", t, scaledT, lowerIndex, upperIndex, newT); if (upperIndex == palette.Length) { upperIndex = lowerIndex; } LightingPalette interpPalette = new LightingPalette(); interpPalette.Shadow = WLinearColor.Lerp(palette[lowerIndex].Shadow, palette[upperIndex].Shadow, newT); interpPalette.ActorAmbient = WLinearColor.Lerp(palette[lowerIndex].ActorAmbient, palette[upperIndex].ActorAmbient, newT); interpPalette.RoomLight = WLinearColor.Lerp(palette[lowerIndex].RoomLight, palette[upperIndex].RoomLight, newT); interpPalette.RoomAmbient = WLinearColor.Lerp(palette[lowerIndex].RoomAmbient, palette[upperIndex].RoomAmbient, newT); interpPalette.WaveColor = WLinearColor.Lerp(palette[lowerIndex].WaveColor, palette[upperIndex].WaveColor, newT); interpPalette.OceanColor = WLinearColor.Lerp(palette[lowerIndex].OceanColor, palette[upperIndex].OceanColor, newT); interpPalette.UnknownWhite1 = WLinearColor.Lerp(palette[lowerIndex].UnknownWhite1, palette[upperIndex].UnknownWhite1, newT); interpPalette.UnknownWhite2 = WLinearColor.Lerp(palette[lowerIndex].UnknownWhite2, palette[upperIndex].UnknownWhite2, newT); interpPalette.Doorway = WLinearColor.Lerp(palette[lowerIndex].Doorway, palette[upperIndex].Doorway, newT); interpPalette.UnknownColor3 = WLinearColor.Lerp(palette[lowerIndex].UnknownColor3, palette[upperIndex].UnknownColor3, newT); interpPalette.Skybox = LightingSkyboxColors.Lerp(palette[lowerIndex].Skybox, palette[upperIndex].Skybox, newT); interpPalette.Fog = WLinearColor.Lerp(palette[lowerIndex].Fog, palette[upperIndex].Fog, newT); interpPalette.FogNearPlane = WMath.Lerp(palette[lowerIndex].FogNearPlane, palette[upperIndex].FogNearPlane, newT); interpPalette.FogFarPlane = WMath.Lerp(palette[lowerIndex].FogFarPlane, palette[upperIndex].FogFarPlane, newT); return(interpPalette); }
public Vector3 GetCenter() { List <J3DNode> m_roomModelNodes = GetChildrenOfType <J3DNode>(); Vector3 roomOffset = Vector3.Zero; if (m_roomModelNodes.Count > 0) { roomOffset += m_roomModelNodes[0].Model.BoundingSphere.Center; } if (RoomTransform != null) { roomOffset += new Vector3(RoomTransform.Translation.X, 0, RoomTransform.Translation.Y); float angle = WMath.DegreesToRadians(-RoomTransform.YRotation); float origX = roomOffset.X; float origZ = roomOffset.Z; roomOffset.X = (float)(origX * Math.Cos(angle) - origZ * Math.Sin(angle)); roomOffset.Z = (float)(origX * Math.Sin(angle) + origZ * Math.Cos(angle)); } return(roomOffset); }
public static Quaternion FromEulerAnglesRobust(this Quaternion quat, Vector3 eulerAngles, string rotationOrder, bool usesX, bool usesY, bool usesZ) { quat = Quaternion.Identity; foreach (var axis in rotationOrder) { int axisIndex = "XYZ".IndexOf(axis); if (new[] { usesX, usesY, usesZ }[axisIndex]) { float thisAxisRot = new[] { eulerAngles.X, eulerAngles.Y, eulerAngles.Z }[axisIndex]; Vector3 axisUnitVector = new Vector3(axis == 'X' ? 1 : 0, axis == 'Y' ? 1 : 0, axis == 'Z' ? 1 : 0); Quaternion thisAxisRotQ = Quaternion.FromAxisAngle(axisUnitVector, WMath.DegreesToRadians(thisAxisRot)); quat *= thisAxisRotQ; } ; } return(quat); }
public void ExportToStream(EndianBinaryWriter writer, WScene scene) { // Build a dictionary which lists unique FourCC's and a list of all relevant actors. var actorCategories = new Dictionary <FourCC, List <SerializableDOMNode> >(); foreach (var child in scene) { var groupNode = child as WDOMGroupNode; if (groupNode == null) { continue; } // If this is an ACTR, SCOB, or TRES group node, we have to dig into it to get the layers. if (groupNode.FourCC == FourCC.ACTR || groupNode.FourCC == FourCC.SCOB || groupNode.FourCC == FourCC.TRES) { foreach (var layer in groupNode.Children) { foreach (var obj in layer.Children) { var actor = obj as SerializableDOMNode; if (actor != null) { AddObjectToDictionary(actor, actorCategories); } } } } else { foreach (var obj in groupNode.Children) { var actor = obj as SerializableDOMNode; if (actor != null) { AddObjectToDictionary(actor, actorCategories); } } } } // Create a chunk header for each one. var chunkHeaders = new List <ChunkHeader>(); foreach (var kvp in actorCategories) { ChunkHeader header = new ChunkHeader(); header.FourCC = kvp.Key; header.ElementCount = kvp.Value.Count; chunkHeaders.Add(header); } long chunkStart = writer.BaseStream.Position; // Write the Header writer.Write(chunkHeaders.Count); for (int i = 0; i < chunkHeaders.Count; i++) { writer.Write((int)0); // Dummy Placeholder values for the Chunk Header. writer.Write((int)0); writer.Write((int)0); } // For each chunk, write the data for that chunk. Before writing the data, get the current offset and update the header. List <SerializableDOMNode>[] dictionaryData = new List <SerializableDOMNode> [actorCategories.Count]; actorCategories.Values.CopyTo(dictionaryData, 0); for (int i = 0; i < chunkHeaders.Count; i++) { ChunkHeader header = chunkHeaders[i]; chunkHeaders[i] = new ChunkHeader(header.FourCC, header.ElementCount, (int)(writer.BaseStream.Position - chunkStart)); List <SerializableDOMNode> actors = dictionaryData[i]; foreach (var actor in actors) { MapActorDescriptor template = Globals.ActorDescriptors.Find(x => x.FourCC == actor.FourCC); if (template == null) { Console.WriteLine("Unsupported FourCC (\"{0}\") for exporting!", actor.FourCC); continue; } actor.PreSave(); actor.Save(writer); //WriteActorToChunk(actor, template, writer); } } // Now that we've written every actor to file we can go back and re-write the headers now that we know their offsets. writer.BaseStream.Position = chunkStart + 0x4; // 0x4 is the offset to the Chunk Headers foreach (var header in chunkHeaders) { writer.WriteFixedString(FourCCConversion.GetStringFromEnum(header.FourCC), 4); // FourCC writer.Write(header.ElementCount); // Number of Entries writer.Write(header.ChunkOffset); // Offset from start of file. } // Seek to the end of the file, and then pad us to 32-byte alignment. writer.BaseStream.Seek(0, SeekOrigin.End); int delta = WMath.Pad32Delta(writer.BaseStream.Position); for (int i = 0; i < delta; i++) { writer.Write(0xFF); } }
public List <WRoomTransform> GetRoomTransformTable() { List <WRoomTransform> roomTransforms = new List <WRoomTransform>(); int multIndex = m_chunkList.FindIndex(x => x.FourCC == FourCC.MULT); if (multIndex >= 0) { ChunkHeader rtbl = m_chunkList[multIndex]; m_reader.BaseStream.Position = rtbl.ChunkOffset; for (int i = 0; i < rtbl.ElementCount; i++) { WRoomTransform roomTransform = new WRoomTransform(new Vector2(m_reader.ReadSingle(), m_reader.ReadSingle()), WMath.RotationShortToFloat(m_reader.ReadInt16()), m_reader.ReadByte(), m_reader.ReadByte()); roomTransforms.Add(roomTransform); } } return(roomTransforms); }
public bool TransformFromInput(FRay ray, WSceneView view) { if (m_mode != FTransformMode.Translation) { WrapCursor(); } // Store the cursor position in viewport coordinates. Vector2 screenDimensions = App.GetScreenGeometry(); Vector2 cursorPos = App.GetCursorPosition(); Vector2 mouseCoords = new Vector2(((2f * cursorPos.X) / screenDimensions.X) - 1f, (1f - ((2f * cursorPos.Y) / screenDimensions.Y))); //[-1,1] range bool shiftPressed = WInput.GetKey(Key.LeftShift) || WInput.GetKey(Key.RightShift); if (m_mode == FTransformMode.Translation) { // Create a Translation Plane Vector3 axisA, axisB; if (GetNumSelectedAxes() == 1) { if (m_selectedAxes == FSelectedAxes.X) { axisB = Vector3.UnitX; } else if (m_selectedAxes == FSelectedAxes.Y) { axisB = Vector3.UnitY; } else { axisB = Vector3.UnitZ; } Vector3 dirToCamera = (m_position - view.GetCameraPos()).Normalized(); axisA = Vector3.Cross(axisB, dirToCamera); } else { axisA = ContainsAxis(m_selectedAxes, FSelectedAxes.X) ? Vector3.UnitX : Vector3.UnitZ; axisB = ContainsAxis(m_selectedAxes, FSelectedAxes.Y) ? Vector3.UnitY : Vector3.UnitZ; } Vector3 planeNormal = Vector3.Cross(axisA, axisB).Normalized(); m_translationPlane = new FPlane(planeNormal, m_position); float intersectDist; if (m_translationPlane.RayIntersectsPlane(ray, out intersectDist)) { Vector3 hitPos = ray.Origin + (ray.Direction * intersectDist); Vector3 localDelta = Vector3.Transform(hitPos - m_position, m_rotation.Inverted()); // Calculate a new position Vector3 newPos = m_position; if (ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { newPos += Vector3.Transform(Vector3.UnitX, m_rotation) * localDelta.X; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { newPos += Vector3.Transform(Vector3.UnitY, m_rotation) * localDelta.Y; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Z)) { newPos += Vector3.Transform(Vector3.UnitZ, m_rotation) * localDelta.Z; } if (shiftPressed) { // Round to nearest 100 unit increment while shift is held down. newPos.X = (float)Math.Round(newPos.X / 100f) * 100f; newPos.Y = (float)Math.Round(newPos.Y / 100f) * 100f; newPos.Z = (float)Math.Round(newPos.Z / 100f) * 100f; } // Check the new location to see if it's skyrocked off into the distance due to near-plane raytracing issues. Vector3 newPosDirToCamera = (newPos - view.GetCameraPos()).Normalized(); float dot = Math.Abs(Vector3.Dot(planeNormal, newPosDirToCamera)); //Console.WriteLine("hitPos: {0} localOffset: {1} newPos: {2}, dotResult: {3}", hitPos, localOffset, newPos, dot); if (dot < 0.02f) { return(false); } // This is used to set the offset to the gizmo the mouse cursor is from the origin of the gizmo on the first frame // that you click on the gizmo. if (!m_hasSetMouseOffset) { m_translateOffset = m_position - newPos; m_deltaTranslation = Vector3.Zero; m_hasSetMouseOffset = true; return(false); } // Apply Translation m_deltaTranslation = Vector3.Transform(newPos - m_position + m_translateOffset, m_rotation.Inverted()); if (!ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { m_deltaTranslation.X = 0f; } if (!ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { m_deltaTranslation.Y = 0f; } if (!ContainsAxis(m_selectedAxes, FSelectedAxes.Z)) { m_deltaTranslation.Z = 0f; } m_totalTranslation += m_deltaTranslation; m_position += Vector3.Transform(m_deltaTranslation, m_rotation); if (!m_hasTransformed && (m_deltaTranslation != Vector3.Zero)) { m_hasTransformed = true; } return(m_hasTransformed); } else { // Our raycast missed the plane m_deltaTranslation = Vector3.Zero; return(false); } } else if (m_mode == FTransformMode.Rotation) { Vector3 rotationAxis; if (m_selectedAxes == FSelectedAxes.X) { rotationAxis = Vector3.UnitX; } else if (m_selectedAxes == FSelectedAxes.Y) { rotationAxis = Vector3.UnitY; } else { rotationAxis = Vector3.UnitZ; } // Convert these from [0-1] to [-1, 1] to match our mouse coords. Vector2 lineOrigin = (view.UnprojectWorldToViewport(m_hitPoint) * 2) - Vector2.One; Vector2 lineEnd = (view.UnprojectWorldToViewport(m_hitPoint + m_moveDir) * 2) - Vector2.One; lineOrigin.Y = -lineOrigin.Y; lineEnd.Y = -lineEnd.Y; Vector2 lineDir = (lineEnd - lineOrigin).Normalized(); float rotAmount = Vector2.Dot(lineDir, mouseCoords + m_wrapOffset - lineOrigin) * 180f; if (float.IsNaN(rotAmount)) { Console.WriteLine("rotAmountNaN!"); return(false); } if (!m_hasSetMouseOffset) { m_rotateOffset = -rotAmount; m_deltaRotation = Quaternion.Identity; m_hasSetMouseOffset = true; return(false); } // Apply Rotation rotAmount += m_rotateOffset; if (shiftPressed) { // Round to nearest 45 degree increment while shift is held down. rotAmount = (float)Math.Round(rotAmount / 45f) * 45f; } Quaternion oldRot = m_currentRotation; m_currentRotation = Quaternion.FromAxisAngle(rotationAxis, WMath.DegreesToRadians(rotAmount)); m_deltaRotation = m_currentRotation * oldRot.Inverted(); if (m_transformSpace == FTransformSpace.Local) { m_rotation *= m_deltaRotation; } // Add to Total Rotation recorded for UI. if (m_selectedAxes == FSelectedAxes.X) { m_totalRotation.X = rotAmount; } else if (m_selectedAxes == FSelectedAxes.Y) { m_totalRotation.Y = rotAmount; } else { m_totalRotation.Z = rotAmount; } if (!m_hasTransformed && rotAmount != 0f) { m_hasTransformed = true; } return(m_hasTransformed); } else if (m_mode == FTransformMode.Scale) { // Create a line in screen space. // Convert these from [0-1] to [-1, 1] to match our mouse coords. Vector2 lineOrigin = (view.UnprojectWorldToViewport(m_position) * 2) - Vector2.One; lineOrigin.Y = -lineOrigin.Y; // Determine the appropriate world space directoin using the selected axes and then conver this for use with // screen-space controlls. This has to be done every frame because the axes can be flipped while the gizmo // is transforming, so we can't pre-calculate this. Vector3 dirX = Vector3.Transform(mFlipScaleX ? -Vector3.UnitX : Vector3.UnitX, m_rotation); Vector3 dirY = Vector3.Transform(mFlipScaleY ? -Vector3.UnitY : Vector3.UnitY, m_rotation); Vector3 dirZ = Vector3.Transform(mFlipScaleZ ? -Vector3.UnitZ : Vector3.UnitZ, m_rotation); Vector2 lineDir; // If there is only one axis, then the world space direction is the selected axis. if (GetNumSelectedAxes() == 1) { Vector3 worldDir; if (ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { worldDir = dirX; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { worldDir = dirY; } else { worldDir = dirZ; } Vector2 worldPoint = (view.UnprojectWorldToViewport(m_position + worldDir) * 2) - Vector2.One; worldPoint.Y = -lineOrigin.Y; lineDir = (worldPoint - lineOrigin).Normalized(); } // If there's two axii selected, then convert both to screen space and average them out to get the line direction. else if (GetNumSelectedAxes() == 2) { Vector3 axisA = ContainsAxis(m_selectedAxes, FSelectedAxes.X) ? dirX : dirY; Vector3 axisB = ContainsAxis(m_selectedAxes, FSelectedAxes.Z) ? dirZ : dirY; Vector2 screenA = (view.UnprojectWorldToViewport(m_position + axisA) * 2) - Vector2.One; screenA.Y = -screenA.Y; Vector2 screenB = (view.UnprojectWorldToViewport(m_position + axisB) * 2) - Vector2.One; screenB.Y = -screenB.Y; screenA = (screenA - lineOrigin).Normalized(); screenB = (screenB - lineOrigin).Normalized(); lineDir = ((screenA + screenB) / 2f).Normalized(); } // There's three axis, just use up. else { lineDir = Vector2.UnitY; } float scaleAmount = Vector2.Dot(lineDir, mouseCoords + m_wrapOffset - lineOrigin) * 5f; if (shiftPressed) { // Round to nearest whole number scale while shift is held down. scaleAmount = (float)Math.Round(scaleAmount); } // Set their initial offset if we haven't already if (!m_hasSetMouseOffset) { m_scaleOffset = -scaleAmount; m_deltaScale = Vector3.One; m_hasSetMouseOffset = true; return(false); } // Apply the scale scaleAmount = scaleAmount + m_scaleOffset + 1f; // A multiplier is applied to the scale amount if it's less than one to prevent it dropping into the negatives. // ??? if (scaleAmount < 1f) { scaleAmount = 1f / (-(scaleAmount - 1f) + 1f); } Vector3 oldScale = m_totalScale; m_totalScale = Vector3.One; if (ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { m_totalScale.X = scaleAmount; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { m_totalScale.Y = scaleAmount; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Z)) { m_totalScale.Z = scaleAmount; } m_deltaScale = new Vector3(m_totalScale.X / oldScale.X, m_totalScale.Y / oldScale.Y, m_totalScale.Z / oldScale.Z); if (!m_hasTransformed && (scaleAmount != 1f)) { m_hasTransformed = true; } return(m_hasTransformed); } return(false); }
public bool CheckSelectedAxes(FRay ray) { // Convert the ray into local space so we can use axis-aligned checks, this solves the checking problem // when the gizmo is rotated due to being in Local mode. FRay localRay = new FRay(); localRay.Direction = Vector3.Transform(ray.Direction, m_rotation.Inverted()); localRay.Origin = Vector3.Transform(ray.Origin - m_position, m_rotation.Inverted()); //m_lineBatcher.DrawLine(localRay.Origin, localRay.Origin + (localRay.Direction * 10000), WLinearColor.White, 25, 5); List <AxisDistanceResult> results = new List <AxisDistanceResult>(); if (m_mode == FTransformMode.Translation) { FAABox[] translationAABB = GetAABBBoundsForMode(FTransformMode.Translation); for (int i = 0; i < translationAABB.Length; i++) { float intersectDist; if (WMath.RayIntersectsAABB(localRay, translationAABB[i].Min, translationAABB[i].Max, out intersectDist)) { results.Add(new AxisDistanceResult((FSelectedAxes)(i + 1), intersectDist)); } } } else if (m_mode == FTransformMode.Rotation) { // We'll use a combination of AABB and Distance checks to give us the quarter-circles we need. FAABox[] rotationAABB = GetAABBBoundsForMode(FTransformMode.Rotation); float screenScale = 0f; for (int i = 0; i < 3; i++) { screenScale += m_scale[i]; } screenScale /= 3f; for (int i = 0; i < rotationAABB.Length; i++) { float intersectDist; if (WMath.RayIntersectsAABB(localRay, rotationAABB[i].Min, rotationAABB[i].Max, out intersectDist)) { Vector3 intersectPoint = localRay.Origin + (localRay.Direction * intersectDist); // Convert this aabb check into a radius check so we clip it by the semi-circles // that the rotation tool actually is. if (intersectPoint.Length > 105f * screenScale) { continue; } results.Add(new AxisDistanceResult((FSelectedAxes)(i + 1), intersectDist)); } } } else if (m_mode == FTransformMode.Scale) { FAABox[] scaleAABB = GetAABBBoundsForMode(FTransformMode.Scale); for (int i = 0; i < scaleAABB.Length; i++) { float intersectDist; if (WMath.RayIntersectsAABB(localRay, scaleAABB[i].Min, scaleAABB[i].Max, out intersectDist)) { // Special-case here to give the center scale point overriding priority. Because we intersected // it, we can just override its distance to zero to make it clickable through the other bounding boxes. if ((FSelectedAxes)i + 1 == FSelectedAxes.All) { intersectDist = 0f; } results.Add(new AxisDistanceResult((FSelectedAxes)(i + 1), intersectDist)); } } } if (results.Count == 0) { m_selectedAxes = FSelectedAxes.None; return(false); } // If we get an intersection, sort them by the closest intersection distance. results.Sort((x, y) => x.Distance.CompareTo(y.Distance)); m_selectedAxes = results[0].Axis; // Store where the mouse hit on the first frame in world space. This means converting the ray back to worldspace. Vector3 localHitPoint = localRay.Origin + (localRay.Direction * results[0].Distance); m_hitPoint = Vector3.Transform(localHitPoint, m_rotation) + m_position; return(true); }
private void WriteActorToChunk(WActorNode actor, MapActorDescriptor template, EndianBinaryWriter writer) { // Just convert their rotation to Euler Angles now instead of doing it in parts later. Vector3 eulerRot = actor.Transform.Rotation.ToEulerAngles(); foreach (var field in template.Fields) { IPropertyValue propValue = actor.Properties.Find(x => x.Name == field.FieldName); if (field.FieldName == "Position") { propValue = new TVector3PropertyValue(actor.Transform.Position, "Position"); } else if (field.FieldName == "X Rotation") { short xRotShort = WMath.RotationFloatToShort(eulerRot.X); propValue = new TShortPropertyValue(xRotShort, "X Rotation"); } else if (field.FieldName == "Y Rotation") { short yRotShort = WMath.RotationFloatToShort(eulerRot.Y); propValue = new TShortPropertyValue(yRotShort, "Y Rotation"); } else if (field.FieldName == "Z Rotation") { short zRotShort = WMath.RotationFloatToShort(eulerRot.Z); propValue = new TShortPropertyValue(zRotShort, "Z Rotation"); } else if (field.FieldName == "X Scale") { float xScale = actor.Transform.LocalScale.X; propValue = new TBytePropertyValue((byte)(xScale * 10), "X Scale"); } else if (field.FieldName == "Y Scale") { float yScale = actor.Transform.LocalScale.Y; propValue = new TBytePropertyValue((byte)(yScale * 10), "Y Scale"); } else if (field.FieldName == "Z Scale") { float zScale = actor.Transform.LocalScale.Z; propValue = new TBytePropertyValue((byte)(zScale * 10), "Z Scale"); } switch (field.FieldType) { case PropertyValueType.Byte: writer.Write((byte)propValue.GetValue()); break; case PropertyValueType.Bool: writer.Write((bool)propValue.GetValue()); break; case PropertyValueType.Short: writer.Write((short)propValue.GetValue()); break; case PropertyValueType.Int: writer.Write((int)propValue.GetValue()); break; case PropertyValueType.Float: writer.Write((float)propValue.GetValue()); break; case PropertyValueType.String: writer.Write(System.Text.Encoding.ASCII.GetBytes((string)propValue.GetValue())); break; case PropertyValueType.FixedLengthString: string fixedLenStr = (string)propValue.GetValue(); for (int i = 0; i < field.Length; i++) { writer.Write(i < fixedLenStr.Length ? (byte)fixedLenStr[i] : (byte)0); } //writer.WriteFixedString((string)propValue.GetValue(), (int)field.Length); break; break; case PropertyValueType.Vector2: Vector2 vec2Val = (Vector2)propValue.GetValue(); writer.Write(vec2Val.X); writer.Write(vec2Val.Y); break; case PropertyValueType.Vector3: Vector3 vec3Val = (Vector3)propValue.GetValue(); writer.Write(vec3Val.X); writer.Write(vec3Val.Y); writer.Write(vec3Val.Z); break; case PropertyValueType.XRotation: case PropertyValueType.YRotation: case PropertyValueType.ZRotation: writer.Write((short)propValue.GetValue()); break; case PropertyValueType.Color24: WLinearColor color24 = (WLinearColor)propValue.GetValue(); writer.Write((byte)color24.R); writer.Write((byte)color24.G); writer.Write((byte)color24.B); break; case PropertyValueType.Color32: WLinearColor color32 = (WLinearColor)propValue.GetValue(); writer.Write((byte)color32.R); writer.Write((byte)color32.G); writer.Write((byte)color32.B); writer.Write((byte)color32.A); break; default: Console.WriteLine("Unsupported PropertyValueType: {0}", field.FieldType); break; } } }
public void SetRoomTransform(WRoomTransform roomTransform) { List <J3DNode> m_roomModelNodes = GetChildrenOfType <J3DNode>(); RoomTransform = roomTransform; foreach (J3DNode j3d_node in m_roomModelNodes) { j3d_node.Transform.Position = new Vector3(RoomTransform.Translation.X, 0, RoomTransform.Translation.Y); j3d_node.Transform.LocalRotation = Quaterniond.FromAxisAngle(Vector3d.UnitY, WMath.DegreesToRadians(RoomTransform.YRotation)); } }
public EnvironmentLightingPalette Lerp(float t, bool presetA = true) { // Generate a new LightingPalette which is the interpolated values of things. t = WMath.Clamp(t, 0, 1); float scaledT = t * (6 - 1); int lowerIndex = (int)scaledT; int upperIndex = (int)(scaledT + 1f); float newT = scaledT - (int)scaledT; EnvironmentLightingPalette palette_a = null; EnvironmentLightingPalette palette_b = null; if (upperIndex == 6) { upperIndex = lowerIndex; } switch ((TimeOfDay)lowerIndex) { case TimeOfDay.Dawn: palette_a = Dawn; break; case TimeOfDay.Morning: palette_a = Morning; break; case TimeOfDay.Noon: palette_a = Noon; break; case TimeOfDay.Afternoon: palette_a = Afternoon; break; case TimeOfDay.Dusk: palette_a = Dusk; break; case TimeOfDay.Night: palette_a = Night; break; } switch ((TimeOfDay)upperIndex) { case TimeOfDay.Dawn: palette_b = Dawn; break; case TimeOfDay.Morning: palette_b = Morning; break; case TimeOfDay.Noon: palette_b = Noon; break; case TimeOfDay.Afternoon: palette_b = Afternoon; break; case TimeOfDay.Dusk: palette_b = Dusk; break; case TimeOfDay.Night: palette_b = Night; break; } //Console.WriteLine("t: {0} scaledT: {1} lIndex: {2} uIndex: {3} newT: {4}", t, scaledT, lowerIndex, upperIndex, newT); EnvironmentLightingPalette interpPalette = new EnvironmentLightingPalette(); interpPalette.ShadowColor = WLinearColor.Lerp(palette_a.ShadowColor, palette_b.ShadowColor, newT); interpPalette.ActorAmbientColor = WLinearColor.Lerp(palette_a.ActorAmbientColor, palette_b.ActorAmbientColor, newT); interpPalette.RoomLightColor = WLinearColor.Lerp(palette_a.RoomLightColor, palette_b.RoomLightColor, newT); interpPalette.RoomAmbientColor = WLinearColor.Lerp(palette_a.RoomAmbientColor, palette_b.RoomAmbientColor, newT); interpPalette.WaveColor = WLinearColor.Lerp(palette_a.WaveColor, palette_b.WaveColor, newT); interpPalette.OceanColor = WLinearColor.Lerp(palette_a.OceanColor, palette_b.OceanColor, newT); interpPalette.UnknownWhite1 = WLinearColor.Lerp(palette_a.UnknownWhite1, palette_b.UnknownWhite1, newT); interpPalette.UnknownWhite2 = WLinearColor.Lerp(palette_a.UnknownWhite2, palette_b.UnknownWhite2, newT); interpPalette.DoorBackfill = WLinearColor.Lerp(palette_a.DoorBackfill, palette_b.DoorBackfill, newT); interpPalette.Unknown3 = WLinearColor.Lerp(palette_a.Unknown3, palette_b.Unknown3, newT); interpPalette.SkyboxPalette = EnvironmentLightingSkyboxPalette.Lerp(palette_a.SkyboxPalette, palette_b.SkyboxPalette, newT); interpPalette.FogColor = WLinearColor.Lerp(palette_a.FogColor, palette_b.FogColor, newT); interpPalette.FogNearPlane = WMath.Lerp(palette_a.FogNearPlane, palette_b.FogNearPlane, newT); interpPalette.FogFarPlane = WMath.Lerp(palette_a.FogFarPlane, palette_b.FogFarPlane, newT); return(interpPalette); }
public static WLinearColor Lerp(WLinearColor a, WLinearColor b, float t) { t = WMath.Clamp(t, 0, 1); return(new WLinearColor((1 - t) * a.R + t * b.R, (1 - t) * a.G + t * b.G, (1 - t) * a.B + t * b.B, (1 - t) * a.A + t * b.A)); }
/// <summary> /// Convert a Quaternion to all possible ways it can be represented as Euler Angles. /// Returns the angles in [-180, 180] space in degrees. /// </summary> public static List <Vector3> ToEulerAnglesRobust(this Quaterniond quat, string rotationOrder) { var representations = new List <Vector3>(); var qx = quat.X; var qy = quat.Y; var qz = quat.Z; var qw = quat.W; var mat = Matrix3d.CreateFromQuaternion(quat).Inverted(); double x, y, z; switch (rotationOrder) { case "ZYX": y = Math.Asin(-Math.Min(1, Math.Max(-1, mat.M31))); if (Math.Abs(mat.M31) < 0.999999) { x = Math.Atan2(mat.M32, mat.M33); z = Math.Atan2(mat.M21, mat.M11); } else { x = Math.Atan2(-mat.M12, mat.M22); z = 0f; } representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); y = CopySign(Math.PI, y) - y; x = x - CopySign(Math.PI, x); z = z - CopySign(Math.PI, z); representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); break; case "YXZ": x = Math.Asin(-Math.Min(1, Math.Max(-1, mat.M23))); if (Math.Abs(mat.M23) < 0.999999) { y = Math.Atan2(mat.M13, mat.M33); z = Math.Atan2(mat.M21, mat.M22); } else { y = Math.Atan2(-mat.M31, mat.M11); z = 0f; } representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); x = CopySign(Math.PI, x) - x; y = y - CopySign(Math.PI, y); z = z - CopySign(Math.PI, z); representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); break; default: throw new NotImplementedException($"Quaternion to euler rotation conversion not implemented for rotation order: {rotationOrder}"); } return(representations); }
/// <summary> /// Convert a Quaternion to all possible ways it can be represented as Euler Angles. /// Returns the angles in [-180, 180] space in degrees. /// </summary> public static List <Vector3> ToEulerAnglesRobust(this Quaternion quat, string rotationOrder) { var representations = new List <Vector3>(); var qx = quat.X; var qy = quat.Y; var qz = quat.Z; var qw = quat.W; // Convert the quaternion to a 3x3 matrix. // We don't use OpenTK's Matrix3 class because it stores the values as single-precision floats, which loses information. // By manually calculating the matrix elements as doubles, we can get the maximum amount of accuracy. double m11 = 1.0 - 2.0 * (qy * qy + qz * qz); double m12 = 2.0 * (qx * qy - qz * qw); double m13 = 2.0 * (qx * qz + qy * qw); double m21 = 2.0 * (qx * qy + qz * qw); double m22 = 1.0 - 2.0 * (qx * qx + qz * qz); double m23 = 2.0 * (qy * qz - qx * qw); double m31 = 2.0 * (qx * qz - qy * qw); double m32 = 2.0 * (qy * qz + qx * qw); double m33 = 1.0 - 2.0 * (qx * qx + qy * qy); double x, y, z; switch (rotationOrder) { case "ZYX": y = Math.Asin(-Math.Min(1, Math.Max(-1, m31))); if (Math.Abs(m31) < 0.999999) { x = Math.Atan2(m32, m33); z = Math.Atan2(m21, m11); } else { x = Math.Atan2(-m12, m22); z = 0f; } representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); y = CopySign(Math.PI, y) - y; x = x - CopySign(Math.PI, x); z = z - CopySign(Math.PI, z); representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); break; case "YXZ": x = Math.Asin(-Math.Min(1, Math.Max(-1, m23))); if (Math.Abs(m23) < 0.999999) { y = Math.Atan2(m13, m33); z = Math.Atan2(m21, m22); } else { y = Math.Atan2(-m31, m11); z = 0f; } representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); x = CopySign(Math.PI, x) - x; y = y - CopySign(Math.PI, y); z = z - CopySign(Math.PI, z); representations.Add(new Vector3( WMath.RadiansToDegrees((float)x), WMath.RadiansToDegrees((float)y), WMath.RadiansToDegrees((float)z) )); break; default: throw new NotImplementedException($"Quaternion to euler rotation conversion not implemented for rotation order: {rotationOrder}"); } return(representations); }
public void ExportToStream(EndianBinaryWriter writer, WScene scene) { // Build a dictionary which lists unique FourCC's and a list of all relevant actors. var actorCategories = new Dictionary <string, List <WActorNode> >(); foreach (var child in scene) { WActorNode actor = child as WActorNode; if (actor != null) { string fixedFourCC = ChunkHeader.LayerToFourCC(actor.FourCC, actor.Layer); if (!actorCategories.ContainsKey(fixedFourCC)) { actorCategories[fixedFourCC] = new List <WActorNode>(); } actorCategories[fixedFourCC].Add(actor); } } // Create a chunk header for each one. var chunkHeaders = new List <ChunkHeader>(); foreach (var kvp in actorCategories) { ChunkHeader header = new ChunkHeader(); header.FourCC = kvp.Key; header.ElementCount = kvp.Value.Count; chunkHeaders.Add(header); } long chunkStart = writer.BaseStream.Position; // Write the Header writer.Write(chunkHeaders.Count); for (int i = 0; i < chunkHeaders.Count; i++) { writer.Write((int)0); // Dummy Placeholder values for the Chunk Header. writer.Write((int)0); writer.Write((int)0); } // For each chunk, write the data for that chunk. Before writing the data, get the current offset and update the header. List <WActorNode>[] dictionaryData = new List <WActorNode> [actorCategories.Count]; actorCategories.Values.CopyTo(dictionaryData, 0); for (int i = 0; i < chunkHeaders.Count; i++) { ChunkHeader header = chunkHeaders[i]; chunkHeaders[i] = new ChunkHeader(header.FourCC, header.ElementCount, (int)(writer.BaseStream.Position - chunkStart)); List <WActorNode> actors = dictionaryData[i]; foreach (var actor in actors) { MapActorDescriptor template = m_sActorDescriptors.Find(x => x.FourCC == actor.FourCC); if (template == null) { Console.WriteLine("Unsupported FourCC (\"{0}\") for exporting!", actor.FourCC); continue; } WriteActorToChunk(actor, template, writer); } } // Now that we've written every actor to file we can go back and re-write the headers now that we know their offsets. writer.BaseStream.Position = chunkStart + 0x4; // 0x4 is the offset to the Chunk Headers foreach (var header in chunkHeaders) { writer.WriteFixedString(header.FourCC, 4); // FourCC writer.Write(header.ElementCount); // Number of Entries writer.Write(header.ChunkOffset); // Offset from start of file. } // Seek to the end of the file, and then pad us to 32-byte alignment. writer.BaseStream.Seek(0, SeekOrigin.End); int delta = WMath.Pad32Delta(writer.BaseStream.Position); for (int i = 0; i < delta; i++) { writer.Write((byte)0xFF); } }
/// <summary> /// Convert a Quaternion to Euler Angles. Returns the angles in [-180, 180] space in degrees. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="quat"></param> /// <returns></returns> public static Vector3 ToEulerAngles(this Quaternion quat) { return(new Vector3(WMath.RadiansToDegrees(PitchFromQuat(quat)), WMath.RadiansToDegrees(YawFromQuat(quat)), WMath.RadiansToDegrees(RollFromQuat(quat)))); }
public void DecrementSize() { m_gizmoSize = WMath.Clamp(m_gizmoSize - 0.05f, 0.05f, float.MaxValue); }
public void Rotate(Vector3 axis, float angleInDegrees) { Quaterniond rotQuat = Quaterniond.FromAxisAngle((Vector3d)axis, WMath.DegreesToRadians(angleInDegrees)); Rotation = rotQuat * Rotation; }
private WActorNode LoadActorFromChunk(string fourCC, MapActorDescriptor template) { var newActor = new WActorNode(fourCC, m_world); List <IPropertyValue> actorProperties = new List <IPropertyValue>(); foreach (var field in template.Fields) { IPropertyValue propValue = null; switch (field.FieldType) { case PropertyValueType.Byte: propValue = new TBytePropertyValue(m_reader.ReadByte(), field.FieldName); break; case PropertyValueType.Bool: propValue = new TBoolPropertyValue(m_reader.ReadBoolean(), field.FieldName); break; case PropertyValueType.Short: propValue = new TShortPropertyValue(m_reader.ReadInt16(), field.FieldName); break; case PropertyValueType.Int: propValue = new TIntPropertyValue(m_reader.ReadInt32(), field.FieldName); break; case PropertyValueType.Float: propValue = new TFloatPropertyValue(m_reader.ReadSingle(), field.FieldName); break; case PropertyValueType.FixedLengthString: case PropertyValueType.String: string stringVal = (field.Length == 0) ? m_reader.ReadStringUntil('\0') : m_reader.ReadString(field.Length); stringVal = stringVal.Trim(new[] { '\0' }); propValue = new TStringPropertyValue(stringVal, field.FieldName); break; case PropertyValueType.Vector2: propValue = new TVector2PropertyValue(new OpenTK.Vector2(m_reader.ReadSingle(), m_reader.ReadSingle()), field.FieldName); break; case PropertyValueType.Vector3: propValue = new TVector3PropertyValue(new OpenTK.Vector3(m_reader.ReadSingle(), m_reader.ReadSingle(), m_reader.ReadSingle()), field.FieldName); break; case PropertyValueType.XRotation: case PropertyValueType.YRotation: case PropertyValueType.ZRotation: propValue = new TShortPropertyValue(m_reader.ReadInt16(), field.FieldName); break; case PropertyValueType.Color24: propValue = new TLinearColorPropertyValue(new WLinearColor(m_reader.ReadByte() / 255f, m_reader.ReadByte() / 255f, m_reader.ReadByte() / 255f), field.FieldName); break; case PropertyValueType.Color32: propValue = new TLinearColorPropertyValue(new WLinearColor(m_reader.ReadByte() / 255f, m_reader.ReadByte() / 255f, m_reader.ReadByte() / 255f, m_reader.ReadByte() / 255f), field.FieldName); break; default: Console.WriteLine("Unsupported PropertyValueType: {0}", field.FieldType); break; } propValue.SetUndoStack(m_world.UndoStack); actorProperties.Add(propValue); } // Now that we have loaded all properties out of it, we need to post-process them. IPropertyValue positionProperty = actorProperties.Find(x => x.Name == "Position"); IPropertyValue xRotProperty = actorProperties.Find(x => x.Name == "X Rotation"); IPropertyValue yRotProperty = actorProperties.Find(x => x.Name == "Y Rotation"); IPropertyValue zRotProperty = actorProperties.Find(x => x.Name == "Z Rotation"); IPropertyValue xScaleProperty = actorProperties.Find(x => x.Name == "X Scale"); IPropertyValue yScaleProperty = actorProperties.Find(x => x.Name == "Y Scale"); IPropertyValue zScaleProperty = actorProperties.Find(x => x.Name == "Z Scale"); // Remove these properties from the actor so they don't get added to the UI. actorProperties.Remove(positionProperty); actorProperties.Remove(xRotProperty); actorProperties.Remove(yRotProperty); actorProperties.Remove(zRotProperty); actorProperties.Remove(xScaleProperty); actorProperties.Remove(yScaleProperty); actorProperties.Remove(zScaleProperty); if (positionProperty != null) { newActor.Transform.Position = (Vector3)positionProperty.GetValue(); } float xRot = 0, yRot = 0, zRot = 0; if (xRotProperty != null) { xRot = WMath.RotationShortToFloat((short)xRotProperty.GetValue()); } if (yRotProperty != null) { yRot = WMath.RotationShortToFloat((short)yRotProperty.GetValue()); } if (zRotProperty != null) { zRot = WMath.RotationShortToFloat((short)zRotProperty.GetValue()); } // Build rotation with ZYX order. Quaternion xRotQ = Quaternion.FromAxisAngle(new Vector3(1, 0, 0), WMath.DegreesToRadians(xRot)); Quaternion yRotQ = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), WMath.DegreesToRadians(yRot)); Quaternion zRotQ = Quaternion.FromAxisAngle(new Vector3(0, 0, 1), WMath.DegreesToRadians(zRot)); newActor.Transform.Rotation = zRotQ * yRotQ * xRotQ; float xScale = 1, yScale = 1, zScale = 1; if (xScaleProperty != null) { xScale = ((byte)xScaleProperty.GetValue()) / 10f; } if (yScaleProperty != null) { yScale = ((byte)yScaleProperty.GetValue()) / 10f; } if (zScaleProperty != null) { zScale = ((byte)zScaleProperty.GetValue()) / 10f; } newActor.Transform.LocalScale = new Vector3(xScale, yScale, zScale); newActor.Properties.AddRange(actorProperties); newActor.PostFinishedLoad(); return(newActor); }