Example #1
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 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);
            }
        }
        /// <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);
        }
Example #4
0
        public static void DrawNormals(Node node, int meshIndex, Mesh mesh, CpuSkinningEvaluator skinner, float invGlobalScale, Matrix4 transform)
        {
            if (!mesh.HasNormals)
            {
                return;
            }

            // The normal directions are transformed using the transpose(inverse(transform)).
            // This ensures correct direction is used when non-uniform scaling is present.
            Matrix4 normalMatrix = transform;

            normalMatrix.Invert();
            normalMatrix.Transpose();

            // Scale by scene size because the scene will be resized to fit
            // the unit box, but the normals should have a fixed length.
            var scale = invGlobalScale * 0.05f;

            GL.Begin(BeginMode.Lines);

            GL.Disable(EnableCap.Lighting);
            GL.Disable(EnableCap.Texture2D);
            GL.Enable(EnableCap.ColorMaterial);

            GL.Color4(new Color4(0.0f, 1.0f, 0.0f, 1.0f));

            for (uint i = 0; i < mesh.VertexCount; ++i)
            {
                Vector3 v;
                if (skinner != null && mesh.HasBones)
                {
                    skinner.GetTransformedVertexPosition(node, mesh, i, out v);
                }
                else
                {
                    v = AssimpToOpenTk.FromVector(mesh.Vertices[(int)i]);
                }
                v = Vector4.Transform(new Vector4(v, 1.0f), transform).Xyz; // Skip dividing by W component. It should always be 1, here.

                Vector3 n;
                if (skinner != null)
                {
                    skinner.GetTransformedVertexNormal(node, mesh, i, out n);
                }
                else
                {
                    n = AssimpToOpenTk.FromVector(mesh.Normals[(int)i]);
                }
                n = Vector4.Transform(new Vector4(n, 0.0f), normalMatrix).Xyz; // Toss the W component. It is non-sensical for normals.
                n.Normalize();

                GL.Vertex3(v);
                GL.Vertex3(v + n * scale);
            }
            GL.End();

            GL.Disable(EnableCap.ColorMaterial);
        }
Example #5
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);
            }
        }
Example #7
0
        public static void DrawNormals(Node node, int meshIndex, Mesh mesh, CpuSkinningEvaluator skinner, float invGlobalScale)
        {
            if (!mesh.HasNormals)
            {
                return;
            }
            // Scale by scene size because the scene will be resized to fit
            // the unit box but the normals should have a fixed length
            var scale = invGlobalScale * 0.05f;

            GL.Begin(BeginMode.Lines);

            GL.Disable(EnableCap.Lighting);
            GL.Disable(EnableCap.Texture2D);
            GL.Enable(EnableCap.ColorMaterial);

            GL.Color4(new Color4(0.0f, 1.0f, 0.0f, 1.0f));

            for (uint i = 0; i < mesh.VertexCount; ++i)
            {
                Vector3 v;
                if (skinner != null && mesh.HasBones)
                {
                    skinner.GetTransformedVertexPosition(node, meshIndex, i, out v);
                }
                else
                {
                    v = AssimpToOpenTk.FromVector(mesh.Vertices[(int)i]);
                }

                Vector3 n;
                if (skinner != null)
                {
                    skinner.GetTransformedVertexNormal(node, meshIndex, i, out n);
                }
                else
                {
                    n = AssimpToOpenTk.FromVector(mesh.Normals[(int)i]);
                }

                GL.Vertex3(v);
                GL.Vertex3(v + n * scale);
            }
            GL.End();

            GL.Disable(EnableCap.ColorMaterial);
        }
            /// <summary>
            /// Internal method to (re-)cache all transformed vertex positions and normals
            /// </summary>
            private void Cache()
            {
                var boneMatrices = _scene.SceneAnimator.GetBoneMatricesForMesh(_lastNode, _source);

                // update entire mesh
                for (int i = 0; i < _cachedPositions.Length; ++i)
                {
                    var v = AssimpToOpenTk.FromVector(_source.Vertices[i]);
                    EvaluateBoneInfluences(ref v, (uint)i, boneMatrices, out _cachedPositions[i]);
                }
                for (int i = 0; i < _cachedNormals.Length; ++i)
                {
                    var n = AssimpToOpenTk.FromVector(_source.Normals[i]);
                    EvaluateBoneInfluences(ref n, (uint)i, boneMatrices, out _cachedNormals[i], true);
                }

                _dirty = false;
            }
Example #9
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);
        }
Example #10
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);
        }
        /// <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);
        }
Example #12
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);
        }
Example #13
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;
        }
        public static void DrawBoundingBox(Node node, int meshIndex, Mesh mesh, CpuSkinningEvaluator skinner)
        {
            GL.Disable(EnableCap.Lighting);
            GL.Disable(EnableCap.Texture2D);
            GL.Enable(EnableCap.ColorMaterial);

            GL.Color4(new Color4(1.0f, 0.0f, 0.0f, 1.0f));

            var min = new Vector3(1e10f, 1e10f, 1e10f);
            var max = new Vector3(-1e10f, -1e10f, -1e10f);

            for (uint i = 0; i < mesh.VertexCount; ++i)
            {
                Vector3 tmp;
                if (skinner != null && mesh.HasBones)
                {
                    skinner.GetTransformedVertexPosition(node, mesh, i, out tmp);
                }
                else
                {
                    tmp = AssimpToOpenTk.FromVector(mesh.Vertices[(int)i]);
                }

                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);
            }

            GL.Begin(BeginMode.LineLoop);
            GL.Vertex3(min);
            GL.Vertex3(new Vector3(min.X, max.Y, min.Z));
            GL.Vertex3(new Vector3(min.X, max.Y, max.Z));
            GL.Vertex3(new Vector3(min.X, min.Y, max.Z));
            GL.End();

            GL.Begin(BeginMode.LineLoop);
            GL.Vertex3(new Vector3(max.X, min.Y, min.Z));
            GL.Vertex3(new Vector3(max.X, max.Y, min.Z));
            GL.Vertex3(new Vector3(max.X, max.Y, max.Z));
            GL.Vertex3(new Vector3(max.X, min.Y, max.Z));
            GL.End();

            GL.Begin(BeginMode.Lines);
            GL.Vertex3(min);
            GL.Vertex3(new Vector3(max.X, min.Y, min.Z));

            GL.Vertex3(new Vector3(min.X, max.Y, min.Z));
            GL.Vertex3(new Vector3(max.X, max.Y, min.Z));

            GL.Vertex3(new Vector3(min.X, max.Y, max.Z));
            GL.Vertex3(new Vector3(max.X, max.Y, max.Z));

            GL.Vertex3(new Vector3(min.X, min.Y, max.Z));
            GL.Vertex3(new Vector3(max.X, min.Y, max.Z));
            GL.End();

            GL.Disable(EnableCap.ColorMaterial);
        }
Example #15
0
        private void ApplyFixedFunctionMaterial(Mesh mesh, Material mat, bool textured, bool shaded)
        {
            shaded = shaded && (mesh == null || mesh.HasNormals);
            if (shaded)
            {
                GL.Enable(EnableCap.Lighting);
            }
            else
            {
                GL.Disable(EnableCap.Lighting);
            }

            var hasColors = mesh != null && mesh.HasVertexColors(0);

            if (hasColors)
            {
                GL.Enable(EnableCap.ColorMaterial);
                GL.ColorMaterial(MaterialFace.FrontAndBack, ColorMaterialParameter.AmbientAndDiffuse);
            }
            else
            {
                GL.Disable(EnableCap.ColorMaterial);
            }

            // note: keep semantics of hasAlpha consistent with IsAlphaMaterial()
            var hasAlpha   = false;
            var hasTexture = false;

            // note: keep this up-to-date with the code in UploadTextures()
            if (textured && mat.GetMaterialTextureCount(TextureType.Diffuse) > 0)
            {
                hasTexture = true;

                TextureSlot tex;
                mat.GetMaterialTexture(TextureType.Diffuse, 0, out tex);
                var gtex = _scene.TextureSet.GetOriginalOrReplacement(tex.FilePath);

                //hasAlpha = hasAlpha || gtex.HasAlpha == Texture.AlphaState.HasAlpha;
                hasAlpha = hasAlpha || mat.HasTextureOpacity;

                if (gtex.State == Texture.TextureState.GlTextureCreated)
                {
                    GL.ActiveTexture(TextureUnit.Texture0);
                    gtex.BindGlTexture();

                    GL.Enable(EnableCap.Texture2D);
                }
                else
                {
                    GL.Disable(EnableCap.Texture2D);
                }
            }
            else
            {
                GL.Disable(EnableCap.Texture2D);
            }

            GL.Enable(EnableCap.Normalize);

            var alpha = 1.0f;

            // Assimp always return true with obj/mtl
            // suppress zero opacity, this is likely wrong input data
            // But this may cause alpha 0.0f not working
            if (mat.HasOpacity && mat.Opacity > AlphaSuppressionThreshold && mat.Opacity != 1.0f)
            {
                alpha = mat.Opacity;
            }

            var    color = new Color4(.5f, .5f, .5f, 1.0f);
            Color4 c;

            if (mat.HasColorDiffuse && mat.Name != "DefaultMaterial")
            {
                color = AssimpToOpenTk.FromColor(mat.ColorDiffuse);
                if (color.A < AlphaSuppressionThreshold) // s.a.
                {
                    color.A = 1.0f;
                }
            }
            color.A *= alpha;
            hasAlpha = hasAlpha || color.A < 1.0f;

            if (shaded)
            {
                // if the material has a texture but the diffuse color texture is all black,
                // then heuristically assume that this is an import/export flaw and substitute
                // white.
                if (hasTexture && color.R < 1e-3f && color.G < 1e-3f && color.B < 1e-3f)
                {
                    GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new Color4(1.0f, 1.0f, 1.0f, alpha));
                }
                else
                {
                    GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, color);
                }


                //color = new Color4(0, 0, 0, 1.0f);
                //color = new Color4(1.0f, 1.0f, 1.0f, 1.0f);
                color = new Color4(0.5f, 0.5f, 0.5f, 1.0f);
                if (mat.HasColorSpecular && mat.Name != "DefaultMaterial")
                {
                    c = AssimpToOpenTk.FromColor(mat.ColorSpecular);
                    if (c.R + c.G + c.B > 0.0f)
                    {
                        color = c;
                    }
                }
                color.A *= alpha;
                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, color);

                // Assimp alaways returns HasColorAmbient with {0,0,0,1} when source file has no setting.
                // This makes ambient light becomes all black
                // (The same issue of diffuse color has been fixed above)
                // workaround: Shift color range from 1~0 to 1~(min ambient)
                float minAmb = 0.2f;
                color = new Color4(minAmb, minAmb, minAmb, 1.0f);
                //color = new Color4(.2f, .2f, .2f, 1.0f);
                if (mat.HasColorAmbient)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorAmbient);
                    float shiftC(float oc) => oc * (1.0f - minAmb) + minAmb;

                    color.R = shiftC(color.R);
                    color.G = shiftC(color.G);
                    color.B = shiftC(color.B);
                }
                color.A *= alpha;
                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, color);

                color = new Color4(0, 0, 0, 1.0f);
                if (mat.HasColorEmissive)
                {
                    c = AssimpToOpenTk.FromColor(mat.ColorEmissive);
                    if (c.R + c.G + c.B > 0.0f)
                    {
                        color = c;
                    }
                }
                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, color);

                // Assimp seems convert obj/mtl Ns value to Shininess and the strength value is not set
                float shininess = 1;
                float strength  = 1;
                if (mat.HasShininess && mat.Shininess > 0.0f)
                {
                    shininess = mat.Shininess;
                }
                // todo: I don't even remember how shininess strength was supposed to be handled in assimp
                if (mat.HasShininessStrength && mat.ShininessStrength > 0.0f)
                {
                    strength = mat.ShininessStrength;
                }

                var exp = shininess * strength;
                if (exp >= 128.0f) // 128 is the maximum exponent as per the Gl spec
                {
                    exp = 128.0f;
                }

                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, exp);
            }
            else if (!hasColors)
            {
                GL.Color3(color.R, color.G, color.B);
            }

            if (hasAlpha)
            {
                GL.Enable(EnableCap.Blend);
                GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
                GL.DepthMask(false);
            }
            else
            {
                GL.Disable(EnableCap.Blend);
                GL.DepthMask(true);
            }
        }
Example #16
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);
        }
Example #17
0
        public override void ApplyMaterial(Mesh mesh, Material mat, bool textured, bool shaded, bool twoSided)
        {
            RenderControl.GLError("StartMaterialMapper");
            ShaderGen.GenFlags flags = 0;
            var hasAlpha             = false;
            var hasTexture           = false;

            // note: keep this up-to-date with the code in MaterialMapper.UploadTextures()
            for (int i = 0; i < Renderer.modernGLUsedTextureTypeCount; i++)
            {
                TextureType currTextureType = (TextureType)((int)TextureType.Diffuse + i);
                GL.ActiveTexture((TextureUnit)((int)TextureUnit.Texture0 + i));
                GL.BindTexture(TextureTarget.Texture2D, Renderer.modernGLTextureType[i]); //we use own texture always, even when textures off to supply preset values
                if (textured && mat.GetMaterialTextureCount(currTextureType) > 0)
                {
                    hasTexture = true;
                    //flags |= ShaderGen.GenFlags.Texture; flag not used, we always have some texture
                    TextureSlot tex;
                    mat.GetMaterialTexture(currTextureType, 0, out tex);
                    var gtex = _scene.TextureSet.GetOriginalOrReplacement(tex.FilePath);
                    hasAlpha = hasAlpha || gtex.HasAlpha == Texture.AlphaState.HasAlpha;
                    if (gtex.State == Texture.TextureState.GlTextureCreated)
                    {
                        gtex.BindGlTexture();
                    }
                }
            }
            GL.ActiveTexture(TextureUnit.Texture0);
            RenderControl.GLError("EndTextureSettings");
            if (shaded)
            {
                flags |= ShaderGen.GenFlags.Lighting;
            }
            var hasColors = mesh != null && mesh.HasVertexColors(0);

            if (hasColors)
            {
                flags |= ShaderGen.GenFlags.VertexColor;
            }
            if (_UseSceneLights)
            {
                flags |= ShaderGen.GenFlags.PhongSpecularShading;
            }
            if ((mat.IsTwoSided) || (twoSided))
            {
                flags |= ShaderGen.GenFlags.TwoSide;
            }
            Shader shader = _shaderGen.GenerateOrGetFromCache(flags, _LightCount > 0 ? _LightCount : 1);

            shader.BindIfNecessary();
            Matrix4 curView = Matrix4.CreateScale(_scene.Scale) * _View;

            shader.SetMat4("WorldViewProjection", _World * curView * _Perspective);
            Matrix4 cameraPos      = _View.ClearRotation();
            Matrix4 cameraRotation = _View.ClearTranslation();
            Matrix4 cam            = Matrix4.Identity;

            cam = cameraPos * cameraRotation;
            Vector3 cameraPosition = -cam.ExtractTranslation() / _scene.Scale;//does not work for orbitcontroller

            //            cameraPosition = new Vector3(200,100,-100); //1m = 100units and positive
            cameraPosition.Z = -cameraPosition.Z;
            shader.SetVec3("CameraPosition", -cameraPosition);
            shader.SetMat4("World", _World);
            shader.SetMat4("WorldView", _World * curView); //_world* curView keeps light source at "fixed" position during rotating of the model
            shader.SetFloat("SceneBrightness", _SceneBrightness);
            shader.SetFloat("Material.diffuse", 0);
            shader.SetFloat("Material.ambient", 1);
            shader.SetFloat("Material.specular", 2);
            shader.SetFloat("Material.emissive", 3);
            shader.SetFloat("Material.height", 4);
            shader.SetFloat("Material.normal", 5);
            shader.SetLights(_GLLights, _LightCount);
            RenderControl.GLError("UniformSettings");

            // note: keep semantics of hasAlpha consistent with IsAlphaMaterial()
            var alpha = 1.0f;

            if (mat.HasOpacity)
            {
                alpha = mat.Opacity;
                if (alpha < AlphaSuppressionThreshold) // suppress zero opacity, this is likely wrong input data
                {
                    alpha = 1.0f;
                }
            }
            var color = new Color4(.8f, .8f, .8f, 1.0f);

            if (mat.HasColorDiffuse)
            {
                color = AssimpToOpenTk.FromColor(mat.ColorDiffuse);
                if (color.A < AlphaSuppressionThreshold) // s.a.
                {
                    color.A = 1.0f;
                }
            }
            color.A *= alpha;
            hasAlpha = hasAlpha || color.A < 1.0f;

            if (shaded)
            {
                // if the material has a texture but the diffuse color texture is all black,
                // then heuristically assume that this is an import/export flaw and substitute
                // white.
                if (hasTexture && color.R < 1e-3f && color.G < 1e-3f && color.B < 1e-3f)
                {
                    color = Color4.White;
                }
                shader.SetCol4("MaterialDiffuse_Alpha", color);

                color = new Color4(0, 0, 0, 1.0f);
                if (mat.HasColorSpecular)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorSpecular);
                }
                shader.SetCol4("MaterialSpecular", color);

                color = new Color4(.2f, .2f, .2f, 1.0f);
                if (mat.HasColorAmbient)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorAmbient);
                }
                shader.SetCol4("MaterialAmbient", color);

                color = new Color4(0, 0, 0, 1.0f);
                if (mat.HasColorEmissive)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorEmissive);
                }
                shader.SetCol4("MaterialEmissive", color);

                float shininess = 1;
                float strength  = 1;
                if (mat.HasShininess)
                {
                    shininess = mat.Shininess;
                }
                // todo: I don't even remember how shininess strength was supposed to be handled in assimp .. Scales the specular color of the material.Not implemented here.
                if (mat.HasShininessStrength)
                {
                    strength = mat.ShininessStrength;
                }
                var exp = shininess;
                if (exp >= 128.0f) // 128 is the maximum exponent as per the Gl spec
                {
                    exp = 128.0f;
                }
                shader.SetFloat("MaterialShininess", exp);
                //Shininess may be at mat.ColorSpecular.a too..?? but in FBX is
            }
            else if (!hasColors)
            {
                shader.SetCol4("MaterialDiffuse_Alpha", color);
            }

            if (hasAlpha)
            {
                GL.Enable(EnableCap.Blend);
                GL.BlendFuncSeparate(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha, BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One);
                GL.DepthMask(false);
            }
            else
            {
                GL.DepthMask(true);
                GL.Disable(EnableCap.Blend);
            }
            RenderControl.GLError("EndMaterialMapper");
        }
        /// <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();
        }
        /// <summary>
        /// Draw a mesh using either its given material or a transparent "ghost" material.
        /// </summary>
        /// <param name="node">Current node</param>
        /// <param name="animated">Specifies whether animations should be played</param>
        /// <param name="showGhost">Indicates whether to substitute the mesh' material with a
        /// "ghost" surrogate material that allows looking through the geometry.</param>
        /// <param name="index">Mesh index in the scene</param>
        /// <param name="mesh">Mesh instance</param>
        /// <param name="flags"> </param>
        /// <returns></returns>
        protected override bool InternDrawMesh(Node node, bool animated, bool showGhost, int index, Mesh mesh, RenderFlags flags)
        {
            if (showGhost)
            {
                Owner.MaterialMapper.ApplyGhostMaterial(mesh, Owner.Raw.Materials[mesh.MaterialIndex],
                                                        flags.HasFlag(RenderFlags.Shaded), flags.HasFlag(RenderFlags.ForceTwoSidedLighting));
            }
            else
            {
                Owner.MaterialMapper.ApplyMaterial(mesh, Owner.Raw.Materials[mesh.MaterialIndex],
                                                   flags.HasFlag(RenderFlags.Textured),
                                                   flags.HasFlag(RenderFlags.Shaded), flags.HasFlag(RenderFlags.ForceTwoSidedLighting));
            }

            if (GraphicsSettings.Default.BackFaceCulling)
            {
                GL.FrontFace(FrontFaceDirection.Ccw);
                GL.CullFace(CullFaceMode.Back);
                GL.Enable(EnableCap.CullFace);
            }
            else
            {
                GL.Disable(EnableCap.CullFace);
            }

            var hasColors    = mesh.HasVertexColors(0);
            var hasTexCoords = mesh.HasTextureCoords(0);

            var skinning = mesh.HasBones && animated;

            foreach (var face in mesh.Faces)
            {
                BeginMode faceMode;
                switch (face.IndexCount)
                {
                case 1:
                    faceMode = BeginMode.Points;
                    break;

                case 2:
                    faceMode = BeginMode.Lines;
                    break;

                case 3:
                    faceMode = BeginMode.Triangles;
                    break;

                default:
                    faceMode = BeginMode.Polygon;
                    break;
                }

                GL.Begin(faceMode);
                for (var i = 0; i < face.IndexCount; i++)
                {
                    var indice = face.Indices[i];
                    if (hasColors)
                    {
                        var vertColor = AssimpToOpenTk.FromColor(mesh.VertexColorChannels[0][indice]);
                        GL.Color4(vertColor);
                    }
                    if (mesh.HasNormals)
                    {
                        Vector3 normal;
                        if (skinning)
                        {
                            Skinner.GetTransformedVertexNormal(node, mesh, (uint)indice, out normal);
                        }
                        else
                        {
                            normal = AssimpToOpenTk.FromVector(mesh.Normals[indice]);
                        }

                        GL.Normal3(normal);
                    }
                    if (hasTexCoords)
                    {
                        var uvw = AssimpToOpenTk.FromVector(mesh.TextureCoordinateChannels[0][indice]);
                        GL.TexCoord2(uvw.X, 1 - uvw.Y);
                    }

                    Vector3 pos;
                    if (skinning)
                    {
                        Skinner.GetTransformedVertexPosition(node, mesh, (uint)indice, out pos);
                    }
                    else
                    {
                        pos = AssimpToOpenTk.FromVector(mesh.Vertices[indice]);
                    }
                    GL.Vertex3(pos);
                }
                GL.End();
            }
            GL.Disable(EnableCap.CullFace);
            return(skinning);
        }
        private void ApplyFixedFunctionMaterial(Mesh mesh, Material mat, bool textured, bool shaded)
        {
            shaded = shaded && (mesh == null || mesh.HasNormals);
            if (shaded)
            {
                GL.Enable(EnableCap.Lighting);
            }
            else
            {
                GL.Disable(EnableCap.Lighting);
            }

            var hasColors = mesh != null && mesh.HasVertexColors(0);

            if (hasColors)
            {
                GL.Enable(EnableCap.ColorMaterial);
                GL.ColorMaterial(MaterialFace.FrontAndBack, ColorMaterialParameter.AmbientAndDiffuse);
            }
            else
            {
                GL.Disable(EnableCap.ColorMaterial);
            }

            // note: keep semantics of hasAlpha consistent with IsAlphaMaterial()
            var hasAlpha   = false;
            var hasTexture = false;

            // note: keep this up-to-date with the code in UploadTextures()
            if (textured && mat.GetMaterialTextureCount(TextureType.Diffuse) > 0)
            {
                hasTexture = true;

                TextureSlot tex;
                mat.GetMaterialTexture(TextureType.Diffuse, 0, out tex);
                var gtex = _scene.TextureSet.GetOriginalOrReplacement(tex.FilePath);

                hasAlpha = hasAlpha || gtex.HasAlpha == Texture.AlphaState.HasAlpha;

                if (gtex.State == Texture.TextureState.GlTextureCreated)
                {
                    GL.ActiveTexture(TextureUnit.Texture0);
                    gtex.BindGlTexture();

                    GL.Enable(EnableCap.Texture2D);
                }
                else
                {
                    GL.Disable(EnableCap.Texture2D);
                }
            }
            else
            {
                GL.Disable(EnableCap.Texture2D);
            }

            GL.Enable(EnableCap.Normalize);

            var alpha = 1.0f;

            if (mat.HasOpacity)
            {
                alpha = mat.Opacity;
                if (alpha < AlphaSuppressionThreshold) // suppress zero opacity, this is likely wrong input data
                {
                    alpha = 1.0f;
                }
            }

            var color = new Color4(.8f, .8f, .8f, 1.0f);

            if (mat.HasColorDiffuse)
            {
                color = AssimpToOpenTk.FromColor(mat.ColorDiffuse);
                if (color.A < AlphaSuppressionThreshold) // s.a.
                {
                    color.A = 1.0f;
                }
            }
            color.A *= alpha;
            hasAlpha = hasAlpha || color.A < 1.0f;

            if (shaded)
            {
                // if the material has a texture but the diffuse color texture is all black,
                // then heuristically assume that this is an import/export flaw and substitute
                // white.
                if (hasTexture && color.R < 1e-3f && color.G < 1e-3f && color.B < 1e-3f)
                {
                    GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, Color4.White);
                }
                else
                {
                    GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, color);
                }

                color = new Color4(0, 0, 0, 1.0f);
                if (mat.HasColorSpecular)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorSpecular);
                }
                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, color);

                color = new Color4(.2f, .2f, .2f, 1.0f);
                if (mat.HasColorAmbient)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorAmbient);
                }
                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, color);

                color = new Color4(0, 0, 0, 1.0f);
                if (mat.HasColorEmissive)
                {
                    color = AssimpToOpenTk.FromColor(mat.ColorEmissive);
                }
                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, color);

                float shininess = 1;
                float strength  = 1;
                if (mat.HasShininess)
                {
                    shininess = mat.Shininess;
                }
                // todo: I don't even remember how shininess strength was supposed to be handled in assimp
                if (mat.HasShininessStrength)
                {
                    strength = mat.ShininessStrength;
                }

                var exp = shininess * strength;
                if (exp >= 128.0f) // 128 is the maximum exponent as per the Gl spec
                {
                    exp = 128.0f;
                }

                GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, exp);
            }
            else if (!hasColors)
            {
                GL.Color3(color.R, color.G, color.B);
            }

            if (hasAlpha)
            {
                GL.Enable(EnableCap.Blend);
                GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
                GL.DepthMask(false);
            }
            else
            {
                GL.Disable(EnableCap.Blend);
                GL.DepthMask(true);
            }
        }