public override void Render(ViewControl vc) { Matrix4F normWorld = GetManipulatorMatrix(); if (normWorld == null) { return; } Camera camera = vc.Camera; float s; Util.CalcAxisLengths(camera, normWorld.Translation, out s); m_translatorControl.Render(normWorld, s); Matrix4F sc = new Matrix4F(); Vec3F pos = normWorld.Translation; s /= 12.0f; sc.Scale(s); Matrix4F bl = new Matrix4F(); Util.CreateBillboard(bl, pos, camera.WorldEye, camera.Up, camera.LookAt); Matrix4F recXform = new Matrix4F(); Matrix4F.Multiply(sc, bl, recXform); Util3D.DrawPivot(recXform, Color.Yellow); }
public override void Render(DeviceContext graphics, ref Matrix4F matrix, Vector3D renderOrigin, Entity entity, AvatarController avatarController, Timestep renderTimestep, RenderMode renderMode) { if (entity.Logic is BorderEntityLogic borderLogic) { if (!ClientContext.PlayerFacade.IsLocalPlayer(borderLogic.Owner)) { return; } var cube = borderLogic.Cube; if (cube != null) { var boxMatrix = Matrix.CreateFromYawPitchRoll(0, 0, 0).ToMatrix4F(); var boxMatrix1 = Matrix4F.Multiply( boxMatrix.Translate((cube.OrigStart.ToVector3F() - renderOrigin.ToVector3F()) + borderLogic.Selection1.Components.Get <TileEntityComponent>().TileOffset), matrix); borderLogic.Selection1.Icon.Matrix() .Scale(borderLogic.Selection1.Components.Get <TileEntityComponent>().Scale) .Render(graphics, ref boxMatrix1); var boxMatrix2 = Matrix4F.Multiply(boxMatrix.Translate( (cube.OrigEnd.ToVector3F() - renderOrigin.ToVector3F()) + borderLogic.Selection2.Components .Get <TileEntityComponent>().TileOffset), matrix); borderLogic.Selection2.Icon.Matrix().Scale(borderLogic.Selection2.Components.Get <TileEntityComponent>().Scale).Render(graphics, ref boxMatrix2); } } }
/// <summary> /// Render the item icons /// </summary> /// <param name="graphics"></param> /// <param name="matrix"></param> /// <param name="renderOrigin"></param> /// <param name="avatar"></param> /// <param name="avatarPainter"></param> /// <param name="universe"></param> public void DrawItemVoxels(DeviceContext graphics, Matrix4F matrix, Entity avatar) { if (!this.IsShowing) { return; } graphics.PushRenderState(); Matrix4F projectionMatrix = graphics.GetProjectionMatrix(); Matrix4F overlayMatrix = graphics.GetOverlayMatrix(); graphics.PushShader(); graphics.SetShader(graphics.GetShader("VoxelOverlayStipple")); double rotation = ClientContext.OverlayController.IsMenuOpen() ? 0 : (DateTime.UtcNow - new DateTime(0L)).TotalMilliseconds * 0.001 % 6.2831853071795862; int activeIndex = avatar.Inventory.ActiveItemIndex(); for (int i = 0; i < 10; i++) { ItemStack item = avatar.Inventory.GetHotbarItem(i); if (item.Item != Item.NullItem) { Vector2F p = HotbarManager.Instance.Controller.GetSlotPosition(i); p *= Constants.UIZoomFactor; Vector2F pp = graphics.ScreenPosToProjectionPos(p); Matrix4F movedMatrix2 = Matrix4F.CreateTranslation(new Vector3F(pp.X, pp.Y, 0f)); movedMatrix2 = Matrix4F.Multiply(overlayMatrix, movedMatrix2); graphics.SetProjectionMatrix(movedMatrix2); Vector2I viewPortSize = graphics.GetViewPortSize(); float itemScale = Constants.ItemRenderingScale; itemScale *= Constants.UIZoomFactor / ((float)viewPortSize.Y / Constants.ViewPortScaleThreshold.Y); if (i == activeIndex) { graphics.SetShader(graphics.GetShader("VoxelOverlay")); } ClientContext.ItemRendererManager.RenderIcon(item.Item, graphics, Matrix4F.CreateScale(itemScale).Rotate((float)rotation, Vector3F.Up).Rotate(-0.35f, Vector3F.Left) .Translate(new Vector3F(0f, 0f, -0.2f)) .Multiply(matrix)); if (i == activeIndex) { graphics.SetShader(graphics.GetShader("VoxelOverlayStipple")); } } } graphics.PopShader(); graphics.SetProjectionMatrix(projectionMatrix); graphics.PopRenderState(); }
/// <summary> /// Adjusts child transform, making it the concatenation with its parent's transform. /// Is recursive, looking for parents that also implement IRenderableNode.</summary> /// <param name="parent">Parent node</param> /// <param name="child">Child node</param> public static void RemoveChild(ITransformable parent, ITransformable child) { Path <DomNode> path = new Path <DomNode>(parent.Cast <DomNode>().GetPath()); Matrix4F parentMatrix = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F childMatrix = child.Transform; Matrix4F newChildMatrix = Matrix4F.Multiply(childMatrix, parentMatrix); Vec3F newTranslation = child.Translation; parentMatrix.Transform(ref newTranslation); Vec3F newRotation = new Vec3F(); newChildMatrix.GetEulerAngles(out newRotation.X, out newRotation.Y, out newRotation.Z); child.Rotation = newRotation; Vec3F newScale = newChildMatrix.GetScale(); child.Scale = newScale; // We can compose together all of the separate transformations now. Matrix4F newTransform = CalcTransform( newTranslation, newRotation, newScale, child.ScalePivot, child.ScalePivotTranslation, child.RotatePivot, child.RotatePivotTranslation); // However, the composed matrix may not equal newChildMatrix due to rotating // or scaling around a pivot. In the general case, it may be impossible to // decompose newChildMatrix into all of these separate components. For example, // a sheer transformation cannot be reproduced by a single rotation and scale. // But for common cases, only the translation is out-of-sync now, so apply a fix. Vec3F desiredTranslation = newChildMatrix.Translation; Vec3F currentTranslation = newTransform.Translation; Vec3F fixupTranslation = desiredTranslation - currentTranslation; Matrix4F fixupTransform = new Matrix4F(fixupTranslation); newTransform.Mul(newTransform, fixupTransform); // Save the fix and the final transform. child.Translation = newTranslation + fixupTranslation; child.Transform = newTransform; }
public override void Render(DeviceContext graphics, ref Matrix4F matrix, Vector3D renderOrigin, Entity entity, AvatarController avatarController, Timestep renderTimestep, RenderMode renderMode) { if (!_botTileCode.IsNullOrEmpty()) { if (entity.Logic is BotEntityLogic logic) { var pos = entity.Physics.Position.ToVector3F(); if (pos.Y + _add >= pos.Y + 0.125) { _up = false; } if (pos.Y + _add <= pos.Y + 0) { _up = true; } _add = _up ? _add + 0.0015F : _add - 0.0015F; pos.Y += _add; var botMatrix = Matrix.CreateFromYawPitchRoll(MathHelper.ToRadians(logic.Rotation), 0, 0).ToMatrix4F(); botMatrix = Matrix4F.Multiply(botMatrix.Translate(pos.X - (float)renderOrigin.X, pos.Y - (float)renderOrigin.Y, pos.Z - (float)renderOrigin.Z), matrix); _bot.Render(graphics, ref botMatrix); var bladeMatrix = Matrix .CreateFromYawPitchRoll( (float)((DateTime.UtcNow - _created).TotalMilliseconds * 0.003 % (2 * Math.PI)), 0, 0) .ToMatrix4F(); for (var i = 0; i < logic.BotComponent.BladeLocations.Count; i++) { var currentPosition = pos + logic.BotComponent.BladeLocations[i].Item1.ToVector3F(); var currentBladeMatrix = Matrix4F.Multiply(bladeMatrix.Translate( currentPosition.X - (float)renderOrigin.X, currentPosition.Y - (float)renderOrigin.Y, currentPosition.Z - (float)renderOrigin.Z), matrix); _bladeTile.Configuration.Icon.Matrix().Scale(logic.BotComponent.BladeLocations[i].Item2).Render(graphics, ref currentBladeMatrix); } } } }
/// <summary> /// Ungroups the specified gameobjects.</summary> public IEnumerable <ITransformable> Ungroup(IEnumerable <object> gobs) { if (!CanUngroup(gobs)) { return(EmptyArray <ITransformable> .Instance); } List <ITransformable> ungrouplist = new List <ITransformable>(); foreach (var group in gobs.AsIEnumerable <ITransformableGroup>()) { // try to move the children into the parent of the group var node = group.As <DomNode>(); if (node == null) { continue; } var insertParent = node.Lineage.Skip(1).AsIEnumerable <IHierarchical>().FirstOrDefault(); if (insertParent == null) { continue; } var groupTransform = group.Transform; var objects = new List <ITransformable>(group.Objects); // (copy, because we'll remove as we go through) foreach (var child in objects) { if (insertParent.AddChild(child)) { child.Transform = Matrix4F.Multiply(child.Transform, groupTransform); ungrouplist.Add(child); } } if (group.Objects.Count == 0) { group.Cast <DomNode>().RemoveFromParent(); } } return(ungrouplist); }
/// <summary> /// Traverses the specified graph path</summary> /// <param name="graphPath">The graph path</param> /// <param name="action">The render action</param> /// <param name="camera">The camera</param> /// <param name="list">The list</param> /// <returns></returns> public override TraverseState Traverse(Stack <SceneNode> graphPath, IRenderAction action, Camera camera, ICollection <TraverseNode> list) { // Get the "top matrix" before we push a new matrix on to it, just in case we need // it for the bounding box test. Matrix4F parentToWorld = action.TopMatrix; // Push matrix onto the matrix stack even if we're not visible because this class // implements the marker interface ISetsLocalTransform. action.PushMatrix(m_node.Transform, true); // If node is invisible then cull if (!m_node.Visible) { return(TraverseState.Cull); } TraverseState dResult = action.TraverseState; if (dResult == TraverseState.None) { // Test if bounding sphere is contained in frustum if (s_enableVFCull) { // Construct bounding box Box box = new Box(); box.Extend(m_node.BoundingBox); // Transform the bounding box into view space Matrix4F localToView = Matrix4F.Multiply(parentToWorld, camera.ViewMatrix); box.Transform(localToView); if (!camera.Frustum.Contains(box)) { dResult = TraverseState.Cull; } } } return(dResult); }
public override void Render(ViewControl vc) { BasicRendererFlags solid = BasicRendererFlags.Solid | BasicRendererFlags.DisableDepthTest; BasicRendererFlags wire = BasicRendererFlags.WireFrame | BasicRendererFlags.DisableDepthTest; Matrix4F normWorld = GetManipulatorMatrix(); if (normWorld == null) { return; } Camera camera = vc.Camera; Vec3F pos = normWorld.Translation; float s; Util.CalcAxisLengths(vc.Camera, pos, out s); Vec3F sv = new Vec3F(s, s, s); Vec3F axscale = new Vec3F(Math.Abs(s * m_scale.X), Math.Abs(s * m_scale.Y), Math.Abs(s * m_scale.Z)); Color xcolor = (m_hitRegion == HitRegion.XAxis || m_hitRegion == HitRegion.FreeRect) ? Color.Gold : Color.Red; Color ycolor = (m_hitRegion == HitRegion.YAxis || m_hitRegion == HitRegion.FreeRect) ? Color.Gold : Color.Green; Color Zcolor = (m_hitRegion == HitRegion.ZAxis || m_hitRegion == HitRegion.FreeRect) ? Color.Gold : Color.Blue; Color freeRect = (m_hitRegion == HitRegion.FreeRect) ? Color.Gold : Color.White; Matrix4F scale = new Matrix4F(); scale.Scale(axscale); Matrix4F xform = scale * normWorld; Util3D.RenderFlag = wire; Util3D.DrawX(xform, xcolor); Util3D.DrawY(xform, ycolor); Util3D.DrawZ(xform, Zcolor); Vec3F rectScale = sv * FreeRectRatio; scale.Scale(rectScale); Matrix4F b = Util.CreateBillboard(pos, camera.WorldEye, camera.Up, camera.LookAt); Matrix4F recXform = Matrix4F.Multiply(scale, b); Util3D.DrawRect(recXform, freeRect); Vec3F handle = sv * HandleRatio; float handleWidth = handle.X / 2; scale.Scale(handle); Matrix4F trans = new Matrix4F(); trans.Translation = new Vec3F(axscale.X - handleWidth, 0, 0); xform = scale * trans * normWorld; Util3D.RenderFlag = solid; Util3D.DrawCube(xform, xcolor); trans.Translation = new Vec3F(0, axscale.Y - handleWidth, 0); xform = scale * trans * normWorld; Util3D.DrawCube(xform, ycolor); trans.Translation = new Vec3F(0, 0, axscale.Z - handleWidth); xform = scale * trans * normWorld; Util3D.DrawCube(xform, Zcolor); }
public void Render(Camera cam) { GameEngine.SetRendererFlag(BasicRendererFlags.WireFrame); IGrid grid = this.As <IGrid>(); if (grid.Visible == false) { return; } float s = grid.Size; Matrix4F scale = new Matrix4F(); scale.Scale(new Vec3F(s, s, s)); Matrix4F gridXform = new Matrix4F(); if (cam.Frustum.IsOrtho) { float dist = cam.ViewMatrix.Translation.Z; ViewTypes vt = cam.ViewType; if (vt == ViewTypes.Top) { gridXform.Translation = new Vec3F(0, dist, 0); } else if (vt == ViewTypes.Bottom) { gridXform.Translation = new Vec3F(0, -dist, 0); } else if (vt == ViewTypes.Right) { gridXform.RotZ(MathHelper.PiOver2); gridXform.Translation = new Vec3F(dist, 0, 0); } else if (vt == ViewTypes.Left) { gridXform.RotZ(MathHelper.PiOver2); gridXform.Translation = new Vec3F(-dist, 0, 0); } else if (vt == ViewTypes.Front) { gridXform.RotX(MathHelper.PiOver2); gridXform.Translation = new Vec3F(0, 0, dist); } else if (vt == ViewTypes.Back) { gridXform.RotX(MathHelper.PiOver2); gridXform.Translation = new Vec3F(0, 0, -dist); } gridXform.Mul(scale, gridXform); } else { Matrix4F trans = new Matrix4F(); trans.Translation = new Vec3F(0, grid.Height, 0); gridXform = Matrix4F.Multiply(scale, trans); } GameEngine.DrawPrimitive(PrimitiveType.LineList, m_gridVBId, 0, m_gridVertexCount, Color.LightGray, gridXform); }
private void RenderProperties(IEnumerable <object> objects, bool renderCaption, bool renderBound, bool renderPivot) { bool renderAny = renderCaption || renderBound || renderPivot; if (renderAny == false) { return; } Util3D.RenderFlag = BasicRendererFlags.WireFrame; Matrix4F vp = Camera.ViewMatrix * Camera.ProjectionMatrix; foreach (object obj in objects) { IBoundable bnode = obj.As <IBoundable>(); if (bnode == null || bnode.BoundingBox.IsEmpty || obj.Is <IGameObjectFolder>()) { continue; } INameable nnode = obj.As <INameable>(); ITransformable trans = obj.As <ITransformable>(); if (renderBound) { Util3D.DrawAABB(bnode.BoundingBox); } if (renderCaption && nnode != null) { Vec3F topCenter = bnode.BoundingBox.Center; topCenter.Y = bnode.BoundingBox.Max.Y; Point pt = Project(vp, topCenter); GameEngine.DrawText2D(nnode.Name, Util3D.CaptionFont, pt.X, pt.Y, Color.White); } } if (renderPivot) { Util3D.RenderFlag = BasicRendererFlags.WireFrame | BasicRendererFlags.DisableDepthTest; // create few temp matrics to Matrix4F toWorld = new Matrix4F(); Matrix4F PV = new Matrix4F(); Matrix4F sc = new Matrix4F(); Matrix4F bl = new Matrix4F(); Matrix4F recXform = new Matrix4F(); foreach (object obj in objects) { ITransformable trans = obj.As <ITransformable>(); IBoundable bnode = obj.As <IBoundable>(); if (trans == null || bnode == null || bnode.BoundingBox.IsEmpty || obj.Is <IGameObjectFolder>()) { continue; } Path <DomNode> path = new Path <DomNode>(trans.Cast <DomNode>().GetPath()); toWorld.Set(Vec3F.ZeroVector); TransformUtils.CalcPathTransform(toWorld, path, path.Count - 1); // Offset by pivot PV.Set(trans.Pivot); toWorld.Mul(PV, toWorld); Vec3F pos = toWorld.Translation; float s; Util.CalcAxisLengths(Camera, pos, out s); s /= 12.0f; sc.Scale(s); Util.CreateBillboard(bl, pos, Camera.WorldEye, Camera.Up, Camera.LookAt); Matrix4F.Multiply(sc, bl, recXform); Util3D.DrawPivot(recXform, Color.Yellow); } } }
/// <summary> /// Groups the specified GameObjects</summary> /// <param name="gobs">GameObjects to be grouped</param> /// <remarks>Creates a new GameObjectGroup and moves all /// the GameObjects into it.</remarks> public ITransformableGroup Group(IEnumerable <object> gobs) { // extra check. if (!CanGroup(gobs)) { return(null); } IGameObjectFolder root = null; var gameObjects = new List <ITransformable>(); foreach (var gameObject in gobs) { ITransformable trans = gameObject.As <ITransformable>(); if (trans == null) { continue; } var node = gameObject.As <DomNode>(); if (node == null) { continue; } gameObjects.Add(trans); var root1 = GetObjectFolder(node); if (root != null && root != root1) { return(null); } root = root1; } // sort from shallowest in the tree to deepest. Then remove any nodes that // are already children of other nodes in the list gameObjects.Sort( (lhs, rhs) => { return(lhs.As <DomNode>().Lineage.Count().CompareTo(rhs.As <DomNode>().Lineage.Count())); }); // awkward iteration here... Maybe there's a better way to traverse this list..? for (int c = 0; c < gameObjects.Count;) { var n = gameObjects[c].As <DomNode>(); bool remove = false; for (int c2 = 0; c2 < c; ++c2) { if (n.IsDescendantOf(gameObjects[c2].As <DomNode>())) { remove = true; break; } } if (remove) { gameObjects.RemoveAt(c); } else { ++c; } } // finally, we must have at least 2 valid objects to perform the grouping operation if (gameObjects.Count < 2) { return(null); } AABB groupBox = new AABB(); foreach (var gameObject in gameObjects.AsIEnumerable <IBoundable>()) { groupBox.Extend(gameObject.BoundingBox); } var group = root.CreateGroup(); if (group == null) { return(null); } group.As <DomNode>().InitializeExtensions(); // try to add the group to the parent of the shallowest item var groupParent = gameObjects[0].As <DomNode>().Parent.AsAll <IHierarchical>(); bool addedToGroupParent = false; foreach (var h in groupParent) { if (h.AddChild(group)) { addedToGroupParent = true; break; } } if (!addedToGroupParent) { return(null); } // arrange the transform for the group so that it's origin is in the center of the objects var groupParentTransform = new Matrix4F(); if (groupParent.Is <ITransformable>()) { groupParentTransform = groupParent.As <ITransformable>().LocalToWorld; } Matrix4F invgroupParentTransform = new Matrix4F(); invgroupParentTransform.Invert(groupParentTransform); group.Transform = Matrix4F.Multiply(new Matrix4F(groupBox.Center), invgroupParentTransform); Matrix4F invWorld = new Matrix4F(); invWorld.Invert(group.Transform); // now try to actually add the objects to the group var hier = group.AsAll <IHierarchical>(); foreach (var gameObject in gameObjects) { Matrix4F oldWorld = gameObject.LocalToWorld; bool addedToGroup = false; foreach (var h in hier) { if (h.AddChild(gameObject)) { addedToGroup = true; break; } } if (addedToGroup) { gameObject.Transform = Matrix4F.Multiply(oldWorld, invWorld); } } return(group); }
void Update(Component_Skeleton skeleton, Component_SkeletonAnimationTrack animationTrack, double time) { if (time != calculatedForTime) { //update general data calculatedForTime = time; bones = skeleton.GetBones(); //calculate bone transforms if (boneTransforms == null || boneTransforms.Length != bones.Length) { boneTransforms = new Component_SkeletonAnimationTrack.CalculateBoneTransformsItem[bones.Length]; } animationTrack?.CalculateBoneTransforms(time, boneTransforms); CalculateBoneTransforms?.Invoke(this, boneTransforms); //calculate transformMatrixRelativeToSkin, transformRelativeToSkin, boneGlobalTransforms, hasScale if (transformMatrixRelativeToSkin == null || transformMatrixRelativeToSkin.Length != bones.Length) { transformMatrixRelativeToSkin = new Matrix4F[bones.Length]; } if (transformRelativeToSkin == null || transformRelativeToSkin.Length != bones.Length) { transformRelativeToSkin = new Component_SkeletonAnimationTrack.CalculateBoneTransformsItem[bones.Length]; } if (boneGlobalTransforms == null || boneGlobalTransforms.Length != bones.Length) { boneGlobalTransforms = new BoneGlobalTransformItem[bones.Length]; } var matrixZero = Matrix4F.Zero; for (int i = 0; i < bones.Length; i++) { boneGlobalTransforms[i] = new BoneGlobalTransformItem(false, ref matrixZero); } foreach (var b in bones) { GetBoneGlobalTransformRecursive(skeleton, b); } hasScale = false; for (int i = 0; i < bones.Length; i++) { var bone = bones[i]; Matrix4F m; if (bone != null) { Matrix4F.Multiply(ref GetBoneGlobalTransformRecursive(skeleton, bone), ref bone.GetTransformMatrixInverse(), out m); } else { m = Matrix4F.Identity; } transformMatrixRelativeToSkin[i] = m; m.Decompose(out Vector3F t, out QuaternionF r, out Vector3F s); transformRelativeToSkin[i] = new Component_SkeletonAnimationTrack.CalculateBoneTransformsItem { Position = t, Rotation = r, Scale = s }; //if the scale differs from 1.0 more than this value, then the scaling is present and DualQuaternionSkinning can not be used. const float EpsilonForScale = 1e-3f; if (Math.Abs(1.0f - s.X) > EpsilonForScale || Math.Abs(1.0f - s.Y) > EpsilonForScale || Math.Abs(1.0f - s.Y) > EpsilonForScale) { hasScale = true; } } } }
/// <summary> /// Adjusts child transform, making it relative to new parent node's transform. /// Is recursive, looking for parents that also implement IRenderableNode.</summary> /// <param name="parent">Parent node</param> /// <param name="child">Child node</param> public static void AddChild(ITransformable parent, ITransformable child) { Path <DomNode> path = new Path <DomNode>(parent.Cast <DomNode>().GetPath()); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); // We want 'child' to appear in the same place in the world after adding to 'parent'. // local-point * original-local-to-world = world-point // new-local-point * new-local-to-parent * parent-to-world = world-point // ==> new-local-to-parent * parent-to-world = original-local-to-world // (multiply both sides by inverse of parent-to-world; call it world-to-parent) // ==> new-local-to-parent = original-local-to-world * world-to-parent Matrix4F worldToParent = new Matrix4F(); worldToParent.Invert(parentToWorld); Matrix4F originalLocalToWorld = child.Transform; Matrix4F newLocalToParent = Matrix4F.Multiply(originalLocalToWorld, worldToParent); // The translation component of newLocalToParent consists of pivot translation // as well as the child.Translation. So, start with the original child.Translation // and transform it into our new space. Vec3F newTranslation = child.Translation; worldToParent.Transform(ref newTranslation); // There's only one way of getting rotation info, so get it straight from matrix. Vec3F newRotation = new Vec3F(); newLocalToParent.GetEulerAngles(out newRotation.X, out newRotation.Y, out newRotation.Z); child.Rotation = newRotation; // Likewise with scale. Vec3F newScale = newLocalToParent.GetScale(); child.Scale = newScale; // We can compose together all of the separate transformations now. Matrix4F newTransform = CalcTransform( newTranslation, newRotation, newScale, child.ScalePivot, child.ScalePivotTranslation, child.RotatePivot, child.RotatePivotTranslation); // However, the composed matrix may not equal newLocalToParent due to rotating // or scaling around a pivot. In the general case, it may be impossible to // decompose newLocalToParent into all of these separate components. For example, // a sheer transformation cannot be reproduced by a single rotation and scale. // But for common cases, only the translation is out-of-sync now, so apply a fix. Vec3F desiredTranslation = newLocalToParent.Translation; Vec3F currentTranslation = newTransform.Translation; Vec3F fixupTranslation = desiredTranslation - currentTranslation; Matrix4F fixupTransform = new Matrix4F(fixupTranslation); newTransform.Mul(newTransform, fixupTransform); // Save the fix and the final transform. Storing the fix in RotatePivotTranslation // is done elsewhere, as well. child.Translation = newTranslation + fixupTranslation; child.Transform = newTransform; }
/// <summary> /// Dispatches untyped items. Replaces DispatchNotTyped(). To get the same behavior as /// the old DispatchNotTyped(), set the TypeFilter property to null prior to calling.</summary> /// <param name="traverseList">The traverse list</param> /// <param name="camera">The camera</param> protected void DispatchTraverseList(ICollection <TraverseNode> traverseList, Camera camera) { // Prepare for geometric picking -- create the ray in world space and reset geometric hit-list. // First create the ray in viewing coordinates and transform to world coordinates. float nx = (m_x / (float)m_width) - 0.5f; //normalized x float ny = 0.5f - (m_y / (float)m_height); //normalized y Ray3F rayWorld = camera.CreateRay(nx, ny); Matrix4F worldToView = camera.ViewMatrix; Matrix4F viewToWorld = new Matrix4F(); viewToWorld.Invert(worldToView); rayWorld.Transform(viewToWorld); ClearHitList(); // for geometric picking. will be cleared for each HitRecord. List <uint> userData = new List <uint>(1); // Dispatch traverse list int index = 0; foreach (TraverseNode node in traverseList) { // First test for filtering. IRenderObject renderObject = node.RenderObject; if (FilterByType(renderObject)) { IIntersectable intersectable = renderObject.GetIntersectable(); IGeometricPick geometricPick = intersectable as IGeometricPick; if (geometricPick != null) { // Picking by geometry. Matrix4F objToWorld = new Matrix4F(node.Transform); Matrix4F worldToObj = new Matrix4F(); worldToObj.Invert(objToWorld); Matrix4F viewToObj = Matrix4F.Multiply(viewToWorld, worldToObj); if (m_frustumPick) { //The pick frustum is in view space. Transform to world space then object space. Frustum frustumObj = new Frustum(m_viewFrust0); frustumObj.Transform(viewToObj); //Multi-pick. Get everything in the pick frustum (m_viewFrust0). Vec3F eyeObj; worldToObj.Transform(camera.Eye, out eyeObj); userData.Clear(); if (geometricPick.IntersectFrustum(frustumObj, eyeObj, node.RenderState, userData)) { // Prepare a multi-pick HitRecord, as if OpenGL had calculated this. HitRecord hit = new HitRecord( node.GraphPath, renderObject, objToWorld, userData.ToArray()); m_geoHitList.Add(hit); } } else { //Single pick. We care about distance from camera eye. //Make a copy of the ray in world-space and tranform it to object space. Ray3F rayObj = rayWorld; //remember, Ray3F is a value type, not a reference type. rayObj.Transform(worldToObj); // Do the intersection test in object space. userData.Clear(); Vec3F intersectionPt, surfaceNormal; Vec3F nearestVert; bool intersected; intersected = geometricPick.IntersectRay( rayObj, camera, node.RenderState, objToWorld, this, out intersectionPt, out nearestVert, out surfaceNormal, userData); if (intersected) { // Transform to world space and then to screen space. objToWorld.Transform(intersectionPt, out intersectionPt); objToWorld.Transform(nearestVert, out nearestVert); // Prepare a single-pick HitRecord, as if OpenGL had calculated this. HitRecord hit = new HitRecord( node.GraphPath, renderObject, objToWorld, userData.ToArray()); // This is the one difference from OpenGL pick. We have the world pt already. hit.WorldIntersection = intersectionPt; hit.NearestVert = nearestVert; // Another difference is that it's possible to get the surface normal. if (surfaceNormal != Vec3F.ZeroVector) { objToWorld.TransformNormal(surfaceNormal, out surfaceNormal); surfaceNormal.Normalize(); hit.Normal = surfaceNormal; } m_geoHitList.Add(hit); } } } else { // Picking by "rendering", using OpenGL pick. PushMatrix(node.Transform, false); Gl.glPushName(index); IRenderPick pickInterface = renderObject as IRenderPick; if (pickInterface != null) { pickInterface.PickDispatch(node.GraphPath, node.RenderState, this, camera); } else { renderObject.Dispatch(node.GraphPath, node.RenderState, this, camera); } Gl.glPopName(); PopMatrix(); } } index++; } }