Exemplo n.º 1
0
        /// <summary>
        /// Recursive rendering function for semi-transparent (i.e. alpha-blended) meshes.
        ///
        /// Alpha blending is not globally on, meshes need to do that on their own.
        ///
        /// This render function is called _after_ solid geometry has been drawn, so the
        /// relative order between transparent and opaque geometry is maintained. There
        /// is no further ordering within the alpha rendering pass.
        /// </summary>
        /// <param name="node">Current node</param>
        /// <param name="visibleNodes">Set of visible meshes</param>
        /// <param name="flags">Rendering flags</param>
        /// <param name="animated">Play animation?</param>
        private void RecursiveRenderWithAlpha(Node node, Dictionary <Node, List <Mesh> > visibleNodes,
                                              RenderFlags flags,
                                              bool animated)
        {
            Matrix4 m;

            if (animated)
            {
                Owner.SceneAnimator.GetLocalTransform(node, out m);
            }
            else
            {
                m = AssimpToOpenTk.FromMatrix(node.Transform);
            }
            // TODO for some reason, all OpenTk matrices need a ^T - clarify our conventions somewhere
            m.Transpose();

            // the following permutations could be compacted into one big loop with lots of
            // condition magic, but at the cost of readability and also performance.
            // we therefore keep it redundant and stupid.
            if (node.HasMeshes)
            {
                DrawAlphaMeshes(node, visibleNodes, flags, animated);
            }


            for (var i = 0; i < node.ChildCount; i++)
            {
                RecursiveRenderWithAlpha(node.Children[i], visibleNodes, flags, animated);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Helper for ComputeBoundingBox(out Vector3 sceneMin, out Vector3 sceneMax)
        /// </summary>
        /// <param name="node"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <param name="trafo"></param>
        private void ComputeBoundingBox(Node node, ref Vector3 min, ref Vector3 max, ref Matrix4 trafo)
        {
            if (node.HasMeshes)
            {
                foreach (var mesh in node.MeshIndices.Select(index => _raw.Meshes[index]))
                {
                    for (var i = 0; i < mesh.VertexCount; i++)
                    {
                        var tmp = AssimpToOpenTk.FromVector(mesh.Vertices[i]);
                        Vector3.Transform(ref tmp, ref trafo, out tmp);

                        min.X = Math.Min(min.X, tmp.X);
                        min.Y = Math.Min(min.Y, tmp.Y);
                        min.Z = Math.Min(min.Z, tmp.Z);

                        max.X = Math.Max(max.X, tmp.X);
                        max.Y = Math.Max(max.Y, tmp.Y);
                        max.Z = Math.Max(max.Z, tmp.Z);
                    }
                }
            }

            for (var i = 0; i < node.ChildCount; i++)
            {
                var prev = trafo;
                var mat  = AssimpToOpenTk.FromMatrix(node.Children[i].Transform);
                mat.Transpose();
                Matrix4.Mult(ref mat, ref prev, out prev);

                ComputeBoundingBox(node.Children[i], ref min, ref max, ref prev);
            }
        }
        /// <summary>
        /// Recursive rendering function
        /// </summary>
        /// <param name="node">Current node</param>
        /// <param name="visibleMeshesByNode"> </param>
        /// <param name="flags">Rendering flags</param>
        /// <param name="animated">Play animation?</param>
        /// <returns>whether there is any need to do a second render pass with alpha blending enabled</returns>
        protected bool RecursiveRender(Node node,
                                       Dictionary <Node, List <Mesh> > visibleMeshesByNode,
                                       RenderFlags flags, bool animated, int currDispList)
        {
            var needAlpha = false;

            Matrix4 m;

            if (animated)
            {
                Owner.SceneAnimator.GetLocalTransform(node, out m);
            }
            else
            {
                m = AssimpToOpenTk.FromMatrix(node.Transform);
            }
            // TODO for some reason, all OpenTk matrices need a ^T - we should clarify our conventions somewhere
            RenderControl.GLError("A11");
            m.Transpose();
            PushWorld(ref m);
            RenderControl.GLError("B11");

            if ((node.HasMeshes) && (currDispList == GetDispList(node.Name)))
            {
                needAlpha = DrawOpaqueMeshes(node, visibleMeshesByNode, flags, animated);
            }

            for (var i = 0; i < node.ChildCount; i++)
            {
                needAlpha = RecursiveRender(node.Children[i], visibleMeshesByNode, flags, animated, currDispList) || needAlpha;
            }
            RenderControl.GLError("C11");
            PopWorld();
            return(needAlpha);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Calculates the smallest AABB that encloses the scene.
        /// </summary>
        /// <param name="sceneMin"></param>
        /// <param name="sceneMax"></param>
        /// <param name="sceneCenter"> </param>
        /// <param name="node"> </param>
        /// <param name="omitNodeTrafo"> </param>
        private void ComputeBoundingBox(out Vector3 sceneMin, out Vector3 sceneMax, out Vector3 sceneCenter,
                                        Node node = null,
                                        bool omitRootNodeTrafo = false)
        {
            sceneMin = new Vector3(1e10f, 1e10f, 1e10f);
            sceneMax = new Vector3(-1e10f, -1e10f, -1e10f);
            var trafo = omitRootNodeTrafo ? Matrix4.Identity : AssimpToOpenTk.FromMatrix((node ?? _raw.RootNode).Transform);

            trafo.Transpose();

            ComputeBoundingBox(node ?? _raw.RootNode, ref sceneMin, ref sceneMax, ref trafo);
            sceneCenter = (sceneMin + sceneMax) / 2.0f;
        }
        /// <summary>
        /// Recursive render function for drawing normals with a constant size.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visibleMeshesByNode"></param>
        /// <param name="flags"></param>
        /// <param name="invGlobalScale"></param>
        /// <param name="animated"></param>
        /// <param name="transform"></param>
        private void RecursiveRenderNormals(Node node, Dictionary <Node, List <Mesh> > visibleMeshesByNode, RenderFlags flags,
                                            float invGlobalScale,
                                            bool animated,
                                            Matrix4 transform, int currDispList)
        {
            // TODO unify our use of OpenTK and Assimp matrices
            Matrix4 mConv;

            if (animated)
            {
                Owner.SceneAnimator.GetLocalTransform(node, out mConv);
            }
            else
            {
                Matrix4x4 m = node.Transform;
                mConv = AssimpToOpenTk.FromMatrix(ref m);
            }

            mConv.Transpose();

            // The normal's position and direction are transformed differently, so we manually track the transform.
            transform = mConv * transform;

            if (flags.HasFlag(RenderFlags.ShowNormals))
            {
                List <Mesh> meshList = null;
                if (node.HasMeshes &&
                    (visibleMeshesByNode == null || visibleMeshesByNode.TryGetValue(node, out meshList)))
                {
                    foreach (var index in node.MeshIndices)
                    {
                        var mesh = Owner.Raw.Meshes[index];
                        if (meshList != null && !meshList.Contains(mesh))
                        {
                            continue;
                        }
                        if (currDispList == GetDispList(node.Name))
                        {
                            OverlayNormals.DrawNormals(node, index, mesh, mesh.HasBones && animated ? Skinner : null, invGlobalScale, transform);
                        }
                    }
                }
            }

            for (int i = 0; i < node.ChildCount; i++)
            {
                RecursiveRenderNormals(node.Children[i], visibleMeshesByNode, flags, invGlobalScale, animated, transform, currDispList);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Build a transformation matrix from rotation, scaling and translation components.
        /// The transformation order is scaling, rotation, translation (left to right).
        /// </summary>
        /// <param name="presentRotation"></param>
        /// <param name="presentScaling"></param>
        /// <param name="presentPosition"></param>
        /// <param name="outMatrix"></param>
        private static void BuildTransform(ref Assimp.Quaternion presentRotation, ref Vector3D presentScaling,
                                           ref Vector3D presentPosition, out Matrix4 outMatrix)
        {
            // build a transformation matrix from it
            var mat = new Matrix4x4(presentRotation.GetMatrix());

            mat.A1 *= presentScaling.X;
            mat.B1 *= presentScaling.X;
            mat.C1 *= presentScaling.X;
            mat.A2 *= presentScaling.Y;
            mat.B2 *= presentScaling.Y;
            mat.C2 *= presentScaling.Y;
            mat.A3 *= presentScaling.Z;
            mat.B3 *= presentScaling.Z;
            mat.C3 *= presentScaling.Z;
            mat.A4  = presentPosition.X;
            mat.B4  = presentPosition.Y;
            mat.C4  = presentPosition.Z;

            outMatrix = AssimpToOpenTk.FromMatrix(ref mat);
        }
Exemplo n.º 7
0
        private NodeState CreateNodeTree(Node rootNode, NodeState parent)
        {
            var outNode = new NodeState {
                LocalTransform = AssimpToOpenTk.FromMatrix(rootNode.Transform)
            };

            outNode.Parent = parent;

            // calculate transforms
            outNode.GlobalTransform = parent != null ? parent.GlobalTransform * outNode.LocalTransform : outNode.LocalTransform;

            // populate by-name map to quickly map nodes to their state
            _nodeStateByName[rootNode.Name] = outNode;

            // find the index of the animation track affecting this node, if any
            outNode.ChannelIndex = -1;
            if (ActiveAnimation != -1)
            {
                var channels = _raw.Animations[ActiveAnimation].NodeAnimationChannels;
                for (int i = 0; i < channels.Count; ++i)
                {
                    if (channels[i].NodeName != rootNode.Name)
                    {
                        continue;
                    }
                    outNode.ChannelIndex = i;
                    break;
                }
            }

            outNode.Children = new NodeState[rootNode.ChildCount];

            // recursively add up children
            for (int i = 0; i < rootNode.ChildCount; ++i)
            {
                outNode.Children[i] = CreateNodeTree(rootNode.Children[i], outNode);
            }

            return(outNode);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Recursive rendering function for opaque meshes that also checks whether there
        /// is any need for a second rendering pass to draw semi-transparent meshes.
        /// </summary>
        /// <param name="node">Current node</param>
        /// <param name="visibleMeshesByNode"> </param>
        /// <param name="flags">Rendering flags</param>
        /// <param name="animated">Play animation?</param>
        /// <returns>whether there is any need to do a second render pass with alpha blending enabled</returns>
        private bool RecursiveRender(Node node,
                                     Dictionary <Node, List <Mesh> > visibleMeshesByNode,
                                     RenderFlags flags,
                                     bool animated,
                                     ref Matrix4 world)
        {
            var needAlpha = false;

            Matrix4 m;

            if (animated)
            {
                Owner.SceneAnimator.GetLocalTransform(node, out m);
            }
            else
            {
                m = AssimpToOpenTk.FromMatrix(node.Transform);
            }
            // TODO for some reason, all OpenTk matrices need a ^T - we should clarify our conventions somewhere
            m.Transpose();

            var newWorld = world * m;

            // the following permutations could be compacted into one big loop with lots of
            // condition magic, but at the cost of readability and also performance.
            // we therefore keep it redundant and stupid.
            if (node.HasMeshes)
            {
                needAlpha = DrawOpaqueMeshes(node, visibleMeshesByNode, flags, animated);
            }


            for (var i = 0; i < node.ChildCount; i++)
            {
                needAlpha = RecursiveRender(node.Children[i], visibleMeshesByNode, flags, animated, ref newWorld) || needAlpha;
            }
            return(needAlpha);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Recursive rendering function
        /// </summary>
        /// <param name="node">Current node</param>
        /// <param name="visibleMeshesByNode"> </param>
        /// <param name="flags">Rendering flags</param>
        /// <param name="animated">Play animation?</param>
        /// <returns>whether there is any need to do a second render pass with alpha blending enabled</returns>
        private bool RecursiveRender(Node node,
                                     Dictionary <Node, List <Mesh> > visibleMeshesByNode,
                                     RenderFlags flags, bool animated)
        {
            var needAlpha = false;

            Matrix4 m;

            if (animated)
            {
                Owner.SceneAnimator.GetLocalTransform(node, out m);
            }
            else
            {
                m = AssimpToOpenTk.FromMatrix(node.Transform);
            }
            // TODO for some reason, all OpenTk matrices need a ^T - we should clarify our conventions somewhere
            m.Transpose();

            GL.PushMatrix();
            GL.MultMatrix(ref m);


            if (node.HasMeshes)
            {
                needAlpha = DrawOpaqueMeshes(node, visibleMeshesByNode, flags, animated);
            }


            for (var i = 0; i < node.ChildCount; i++)
            {
                needAlpha = RecursiveRender(node.Children[i], visibleMeshesByNode, flags, animated) || needAlpha;
            }

            GL.PopMatrix();
            return(needAlpha);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Set the pivot point for the scene to be at the world position
        /// of a given node.
        /// </summary>
        /// <param name="node">Node to place the pivot at. Pass null to reset the
        /// pivot to be the (natural) center of the scene</param>
        /// <param name="realCenter">Whether to compute the real center of the node (by getting
        /// the mid point of the geometric bounds or whether the origin of the local coordinate
        /// space is used.</param>
        public void SetPivot(Node node, bool realCenter = true)
        {
            if (node == null)
            {
                _pivot = realCenter ? _sceneCenter : Vector3.Zero;
                return;
            }

            var v = Vector3.Zero;

            if (realCenter)
            {
                Vector3 t1, t2;
                ComputeBoundingBox(out t1, out t2, out v, node, true);
            }

            do
            {
                var trafo = AssimpToOpenTk.FromMatrix(node.Transform);
                trafo.Transpose();
                Vector3.Transform(ref v, ref trafo, out v);
            } while ((node = node.Parent) != null);
            _pivot = v;
        }
        /// <summary>
        /// Recursive render function for drawing opaque geometry with no scaling
        /// in the transformation chain. This is used for overlays, such as drawing
        /// the skeleton.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visibleMeshesByNode"></param>
        /// <param name="flags"></param>
        /// <param name="invGlobalScale"></param>
        /// <param name="animated"></param>
        private void RecursiveRenderNoScale(Node node, Dictionary <Node, List <Mesh> > visibleMeshesByNode, RenderFlags flags,
                                            float invGlobalScale,
                                            bool animated, int currDispList)
        {
            // TODO unify our use of OpenTK and Assimp matrices
            Matrix4x4 m;
            Matrix4   mConv;

            if (animated)
            {
                Owner.SceneAnimator.GetLocalTransform(node, out mConv);
                OpenTkToAssimp.FromMatrix(ref mConv, out m);
            }
            else
            {
                m = node.Transform;
            }

            // get rid of the scaling part of the matrix
            // TODO this can be done faster and Decompose() doesn't handle
            // non positively semi-definite matrices correctly anyway.

            Vector3D scaling;

            Assimp.Quaternion rotation;
            Vector3D          translation;

            m.Decompose(out scaling, out rotation, out translation);

            rotation.Normalize();

            m     = new Matrix4x4(rotation.GetMatrix()) * Matrix4x4.FromTranslation(translation);
            mConv = AssimpToOpenTk.FromMatrix(ref m);
            mConv.Transpose();

            if (flags.HasFlag(RenderFlags.ShowSkeleton))
            {
                var highlight = false;
                if (visibleMeshesByNode != null)
                {
                    List <Mesh> meshList;
                    if (visibleMeshesByNode.TryGetValue(node, out meshList) && meshList == null)
                    {
                        // If the user hovers over a node in the tab view, all of its descendants
                        // are added to the visible set as well. This is not the intended
                        // behavior for skeleton joints, though! Here we only want to show the
                        // joint corresponding to the node being hovered over.

                        // Therefore, only highlight nodes whose parents either don't exist
                        // or are not in the visible set.
                        if (node.Parent == null || !visibleMeshesByNode.TryGetValue(node.Parent, out meshList) || meshList != null)
                        {
                            highlight = true;
                        }
                    }
                }
                OverlaySkeleton.DrawSkeletonBone(node, invGlobalScale, highlight);
            }

            GL.PushMatrix();
            GL.MultMatrix(ref mConv);
            for (int i = 0; i < node.ChildCount; i++)
            {
                RecursiveRenderNoScale(node.Children[i], visibleMeshesByNode, flags, invGlobalScale, animated, currDispList);
            }
            GL.PopMatrix();
        }
Exemplo n.º 12
0
        /// <summary>
        /// Obtain the bone matrices for a given node mesh index at the
        /// current time. Calling this is costly, redundant invocations
        /// should thus be avoided.
        /// </summary>
        /// <param name="node">Node for which to query bone matrices</param>
        /// <param name="mesh">Mesh for which to query bone matrices. Must be
        ///    one of the meshes attached to the node.</param>
        /// <returns>For each bone of the mesh the bone transformation
        /// matrix. The returned array is only valid for the rest of
        /// the frame or till the next call to GetBoneMatricesForMesh().
        /// It may contain more entries than the mesh has bones, the extra entries
        /// should be ignored in this case.</returns>
        public Matrix4[] GetBoneMatricesForMesh(Node node, Mesh mesh)
        {
            Debug.Assert(node != null);
            Debug.Assert(mesh != null);

            // calculate the mesh's inverse global transform
            Matrix4 globalInverseMeshTransform;

            GetGlobalTransform(node, out globalInverseMeshTransform);
            globalInverseMeshTransform.Invert();

            // Bone matrices transform from mesh coordinates in bind pose to mesh coordinates in skinned pose
            // Therefore the formula is offsetMatrix * currentGlobalTransform * inverseCurrentMeshTransform
            for (int a = 0; a < mesh.BoneCount; ++a)
            {
                var bone = mesh.Bones[a];

                Matrix4 currentGlobalTransform;
                GetGlobalTransform(bone.Name, out currentGlobalTransform);
                _boneMatrices[a] = globalInverseMeshTransform * currentGlobalTransform * AssimpToOpenTk.FromMatrix(bone.OffsetMatrix);
                // TODO for some reason, all OpenTk matrices need a ^T - clarify our conventions somewhere
                _boneMatrices[a].Transpose();
            }
            return(_boneMatrices);
        }