void            SubdivTriangle(VertexP3N3G3B3T2[] _triangle, uint _count, List <VertexP3N3G3B3T2> _vertices, List <uint> _indices)
        {
            if (_count == 0)
            {
                // Push triangle
// _triangle[0].B.x = (float) _vertices.Count;
// _triangle[1].B.x = (float) _vertices.Count+1;
// _triangle[2].B.x = (float) _vertices.Count+2;
                _indices.Add((uint)_vertices.Count);
                _vertices.Add(_triangle[0]);
                _indices.Add((uint)_vertices.Count);
                _vertices.Add(_triangle[1]);
                _indices.Add((uint)_vertices.Count);
                _vertices.Add(_triangle[2]);
                return;
            }

            VertexP3N3G3B3T2[] insideTriangle = new VertexP3N3G3B3T2[3] {
                new VertexP3N3G3B3T2()
                {
                    P = 0.5f * (_triangle[0].P + _triangle[1].P), N = 0.5f * (_triangle[0].N + _triangle[1].N), UV = 0.5f * (_triangle[0].UV + _triangle[1].UV), T = 0.5f * (_triangle[0].T + _triangle[1].T), B = 0.5f * (_triangle[0].B + _triangle[1].B)
                },
                new VertexP3N3G3B3T2()
                {
                    P = 0.5f * (_triangle[1].P + _triangle[2].P), N = 0.5f * (_triangle[1].N + _triangle[2].N), UV = 0.5f * (_triangle[1].UV + _triangle[2].UV), T = 0.5f * (_triangle[1].T + _triangle[2].T), B = 0.5f * (_triangle[1].B + _triangle[2].B)
                },
                new VertexP3N3G3B3T2()
                {
                    P = 0.5f * (_triangle[2].P + _triangle[0].P), N = 0.5f * (_triangle[2].N + _triangle[0].N), UV = 0.5f * (_triangle[2].UV + _triangle[0].UV), T = 0.5f * (_triangle[2].T + _triangle[0].T), B = 0.5f * (_triangle[2].B + _triangle[0].B)
                },
            };

            VertexP3N3G3B3T2[] temp = new VertexP3N3G3B3T2[3];
            temp[0] = _triangle[0];
            temp[1] = insideTriangle[0];
            temp[2] = insideTriangle[2];
            SubdivTriangle(temp, _count - 1, _vertices, _indices);
            temp[0] = insideTriangle[0];
            temp[1] = _triangle[1];
            temp[2] = insideTriangle[1];
            SubdivTriangle(temp, _count - 1, _vertices, _indices);
            temp[0] = insideTriangle[0];
            temp[1] = insideTriangle[1];
            temp[2] = insideTriangle[2];
            SubdivTriangle(temp, _count - 1, _vertices, _indices);
            temp[0] = insideTriangle[2];
            temp[1] = insideTriangle[1];
            temp[2] = _triangle[2];
            SubdivTriangle(temp, _count - 1, _vertices, _indices);
        }
        private void    BuildPrimitives()
        {
            {
                VertexPt4[] Vertices = new VertexPt4[4];
                Vertices[0] = new VertexPt4()
                {
                    Pt = new float4(-1, +1, 0, 1)
                };                                                                                      // Top-Left
                Vertices[1] = new VertexPt4()
                {
                    Pt = new float4(-1, -1, 0, 1)
                };                                                                                      // Bottom-Left
                Vertices[2] = new VertexPt4()
                {
                    Pt = new float4(+1, +1, 0, 1)
                };                                                                                      // Top-Right
                Vertices[3] = new VertexPt4()
                {
                    Pt = new float4(+1, -1, 0, 1)
                };                                                                                      // Bottom-Right

                ByteBuffer VerticesBuffer = VertexPt4.FromArray(Vertices);

                m_Prim_Quad = new Primitive(m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.Pt4);
            }

            {
                VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[4];
                Vertices[0] = new VertexP3N3G3B3T2()
                {
                    P = new float3(-1, +1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(0, 0)
                };                                                                                                                                                                                              // Top-Left
                Vertices[1] = new VertexP3N3G3B3T2()
                {
                    P = new float3(-1, -1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(0, 1)
                };                                                                                                                                                                                              // Bottom-Left
                Vertices[2] = new VertexP3N3G3B3T2()
                {
                    P = new float3(+1, +1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(1, 0)
                };                                                                                                                                                                                              // Top-Right
                Vertices[3] = new VertexP3N3G3B3T2()
                {
                    P = new float3(+1, -1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(1, 1)
                };                                                                                                                                                                                              // Bottom-Right

                ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray(Vertices);

                m_Prim_Rectangle = new Primitive(m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2);
            }

            {                   // Build the sphere
                const int          W        = 41;
                const int          H        = 22;
                VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[W * H];
                for (int Y = 0; Y < H; Y++)
                {
                    double Theta    = Math.PI * Y / (H - 1);
                    float  CosTheta = (float)Math.Cos(Theta);
                    float  SinTheta = (float)Math.Sin(Theta);
                    for (int X = 0; X < W; X++)
                    {
                        double Phi    = 2.0 * Math.PI * X / (W - 1);
                        float  CosPhi = (float)Math.Cos(Phi);
                        float  SinPhi = (float)Math.Sin(Phi);

                        float3 N = new float3(SinTheta * SinPhi, CosTheta, SinTheta * CosPhi);
                        float3 T = new float3(CosPhi, 0.0f, -SinPhi);
                        float3 B = N.Cross(T);

                        Vertices[W * Y + X] = new VertexP3N3G3B3T2()
                        {
                            P  = N,
                            N  = N,
                            T  = T,
                            B  = B,
                            UV = new float2(2.0f * X / W, 1.0f * Y / H)
                        };
                    }
                }

                ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray(Vertices);

                uint[] Indices    = new uint[(H - 1) * (2 * W + 2) - 2];
                int    IndexCount = 0;
                for (int Y = 0; Y < H - 1; Y++)
                {
                    for (int X = 0; X < W; X++)
                    {
                        Indices[IndexCount++] = (uint)((Y + 0) * W + X);
                        Indices[IndexCount++] = (uint)((Y + 1) * W + X);
                    }
                    if (Y < H - 2)
                    {
                        Indices[IndexCount++] = (uint)((Y + 1) * W - 1);
                        Indices[IndexCount++] = (uint)((Y + 1) * W + 0);
                    }
                }

                m_Prim_Sphere = new Primitive(m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2);
            }

            {                   // Build the cube
                float3[] Normals = new float3[6] {
                    -float3.UnitX,
                    float3.UnitX,
                    -float3.UnitY,
                    float3.UnitY,
                    -float3.UnitZ,
                    float3.UnitZ,
                };

                float3[] Tangents = new float3[6] {
                    float3.UnitZ,
                    -float3.UnitZ,
                    float3.UnitX,
                    -float3.UnitX,
                    -float3.UnitX,
                    float3.UnitX,
                };

                VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[6 * 4];
                uint[]             Indices  = new uint[2 * 6 * 3];

                for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++)
                {
                    float3 N = Normals[FaceIndex];
                    float3 T = Tangents[FaceIndex];
                    float3 B = N.Cross(T);

                    Vertices[4 * FaceIndex + 0] = new VertexP3N3G3B3T2()
                    {
                        P  = N - T + B,
                        N  = N,
                        T  = T,
                        B  = B,
                        UV = new float2(0, 0)
                    };
                    Vertices[4 * FaceIndex + 1] = new VertexP3N3G3B3T2()
                    {
                        P  = N - T - B,
                        N  = N,
                        T  = T,
                        B  = B,
                        UV = new float2(0, 1)
                    };
                    Vertices[4 * FaceIndex + 2] = new VertexP3N3G3B3T2()
                    {
                        P  = N + T - B,
                        N  = N,
                        T  = T,
                        B  = B,
                        UV = new float2(1, 1)
                    };
                    Vertices[4 * FaceIndex + 3] = new VertexP3N3G3B3T2()
                    {
                        P  = N + T + B,
                        N  = N,
                        T  = T,
                        B  = B,
                        UV = new float2(1, 0)
                    };

                    Indices[2 * 3 * FaceIndex + 0] = (uint)(4 * FaceIndex + 0);
                    Indices[2 * 3 * FaceIndex + 1] = (uint)(4 * FaceIndex + 1);
                    Indices[2 * 3 * FaceIndex + 2] = (uint)(4 * FaceIndex + 2);
                    Indices[2 * 3 * FaceIndex + 3] = (uint)(4 * FaceIndex + 0);
                    Indices[2 * 3 * FaceIndex + 4] = (uint)(4 * FaceIndex + 2);
                    Indices[2 * 3 * FaceIndex + 5] = (uint)(4 * FaceIndex + 3);
                }

                ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray(Vertices);

                m_Prim_Cube = new Primitive(m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2);
            }
        }
        Primitive       BuildTorus()
        {
            List <VertexP3N3G3B3T2> vertices = new List <VertexP3N3G3B3T2>();
            List <uint>             indices  = new List <uint>();

            // Build vertices
            VertexP3N3G3B3T2 V = new VertexP3N3G3B3T2();

            for (uint i = 0; i < SUBDIVS0; i++)
            {
                float  a0 = 2.0f * (float)Math.PI * i / SUBDIVS0;
                float3 X  = new float3((float)Math.Cos(a0), (float)Math.Sin(a0), 0.0f);
                float3 Y  = new float3(-X.y, X.x, 0.0f);
                float3 Z  = X.Cross(Y);
                float3 C  = RADIUS0 * X;                        // Center of little ring, around large ring

                for (uint j = 0; j < SUBDIVS1; j++)
                {
                    float  a1  = 2.0f * (float)Math.PI * j / SUBDIVS1;
                    float3 lsN = new float3((float)Math.Cos(a1), (float)Math.Sin(a1), 0.0f);
//					float3	lsN2 = new float3( -lsN.y, lsN.x, 0.0f );
                    float3 N = lsN.x * X + lsN.y * Z;
//					float3	N2 = lsN2.x * X + lsN2.y * Z;

                    V.P  = C + RADIUS1 * N;
                    V.N  = N;
                    V.T  = Y;
                    V.UV = new float2(4.0f * i / SUBDIVS0, 1.0f * j / SUBDIVS1);

                    vertices.Add(V);
                }
            }

            // Build indices and curvature
            uint[,]                 neighborIndices = new uint[3, 3];
            float3[]      neighbors    = new float3[8];
            List <float2> curvatures   = new List <float2>();
            float2        minCurvature = new float2(float.MaxValue, float.MaxValue);
            float2        maxCurvature = new float2(-float.MaxValue, -float.MaxValue);
            float2        avgCurvature = float2.Zero;
            int           count        = 0;

            for (uint i = 0; i < SUBDIVS0; i++)
            {
                uint Pi = (i + SUBDIVS0 - 1) % SUBDIVS0;
                uint Ni = (i + 1) % SUBDIVS0;

                uint ringCurrent  = SUBDIVS1 * i;
                uint ringPrevious = SUBDIVS1 * Pi;
                uint ringNext     = SUBDIVS1 * Ni;

                for (uint j = 0; j < SUBDIVS1; j++)
                {
                    uint Pj = (j + SUBDIVS1 - 1) % SUBDIVS1;
                    uint Nj = (j + 1) % SUBDIVS1;

                    neighborIndices[0, 0] = ringPrevious + Pj;
                    neighborIndices[0, 1] = ringPrevious + j;
                    neighborIndices[0, 2] = ringPrevious + Nj;
                    neighborIndices[1, 0] = ringCurrent + Pj;
                    neighborIndices[1, 1] = ringCurrent + j;
                    neighborIndices[1, 2] = ringCurrent + Nj;
                    neighborIndices[2, 0] = ringNext + Pj;
                    neighborIndices[2, 1] = ringNext + j;
                    neighborIndices[2, 2] = ringNext + Nj;

                    // Build 2 triangles
                    indices.Add(neighborIndices[1, 2]);
                    indices.Add(neighborIndices[1, 1]);
                    indices.Add(neighborIndices[2, 1]);
                    indices.Add(neighborIndices[1, 2]);
                    indices.Add(neighborIndices[2, 1]);
                    indices.Add(neighborIndices[2, 2]);

                    // Compute central vertex's curvature
                    VertexP3N3G3B3T2 centerVertex = vertices[(int)neighborIndices[1, 1]];
                    neighbors[0] = vertices[(int)neighborIndices[0, 0]].P;
                    neighbors[1] = vertices[(int)neighborIndices[1, 0]].P;
                    neighbors[2] = vertices[(int)neighborIndices[2, 0]].P;
                    neighbors[3] = vertices[(int)neighborIndices[0, 1]].P;
//					neighbors[] = vertices[(int) neighborIndices[1,1]].P;
                    neighbors[4] = vertices[(int)neighborIndices[2, 1]].P;
                    neighbors[5] = vertices[(int)neighborIndices[0, 2]].P;
                    neighbors[6] = vertices[(int)neighborIndices[1, 2]].P;
                    neighbors[7] = vertices[(int)neighborIndices[2, 2]].P;

                    // Single curvature
//                  float	curvature = ComputeTangentSphereRadius( centerVertex.P, centerVertex.N, neighbors, false );
//                  centerVertex.B.x = curvature;

                    // Double curvature
                    float2 curvature = ComputeTangentCurvatureRadii(centerVertex.P, centerVertex.N, centerVertex.T, neighbors, false);
                    centerVertex.B.x = curvature.x;
                    centerVertex.B.y = curvature.y;

                    vertices[(int)neighborIndices[1, 1]] = centerVertex;

                    curvatures.Add(curvature);
                    minCurvature.Min(curvature);
                    maxCurvature.Max(curvature);
                    avgCurvature += curvature;
                    count++;
                }
            }
            avgCurvature /= count;

            labelMeshInfo.Text = "Curvature Avg. = " + avgCurvature + " - Min = " + minCurvature + " - Max = " + maxCurvature;

            return(new Primitive(m_device, (uint)vertices.Count, VertexP3N3G3B3T2.FromArray(vertices.ToArray()), indices.ToArray(), Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2));
        }
        Primitive       BuildSubdividedCube()
        {
            // Default example face where B is used to stored the triangle's center and
            float3 C0 = (new float3(-1.0f, 1.0f, 1.0f) + new float3(-1.0f, -1.0f, 1.0f) + new float3(1.0f, -1.0f, 1.0f)) / 3.0f;
            float3 C1 = (new float3(-1.0f, 1.0f, 1.0f) + new float3(1.0f, -1.0f, 1.0f) + new float3(1.0f, 1.0f, 1.0f)) / 3.0f;

            VertexP3N3G3B3T2[] defaultFace = new VertexP3N3G3B3T2[6] {
                // First triangle
                new VertexP3N3G3B3T2()
                {
                    P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 0)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(-1.0f, -1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 1)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(1, 1)
                },

                // 2nd triangle
                new VertexP3N3G3B3T2()
                {
                    P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(0, 0)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 1)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(1.0f, 1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 0)
                },
            };

            float3[] faceNormals = new float3[6] {
                -float3.UnitX,
                float3.UnitX,
                -float3.UnitY,
                float3.UnitY,
                -float3.UnitZ,
                float3.UnitZ,
            };
            float3[] faceTangents = new float3[6] {
                float3.UnitZ,
                -float3.UnitZ,
                float3.UnitX,
                float3.UnitX,
                -float3.UnitX,
                float3.UnitX,
            };

            Func <float3, float3, float3, float3, float3> lambdaTransform = (float3 _P, float3 _T, float3 _B, float3 _N) => _P.x * _T + _P.y * _B + _P.z * _N;

            List <VertexP3N3G3B3T2> vertices = new List <VertexP3N3G3B3T2>();
            List <uint>             indices  = new List <uint>();

            const uint SUBVIVS_COUNT = 5;

            VertexP3N3G3B3T2[] temp = new VertexP3N3G3B3T2[3];
//			for ( int faceIndex=3; faceIndex < 4; faceIndex++ ) {
            for (int faceIndex = 0; faceIndex < 6; faceIndex++)
            {
                float3 N = faceNormals[faceIndex];
                float3 T = faceTangents[faceIndex];
                float3 B = N.Cross(T);

                for (uint triIndex = 0; triIndex < 2; triIndex++)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        VertexP3N3G3B3T2 V = defaultFace[3 * triIndex + i];
                        temp[i].P  = lambdaTransform(V.P, T, B, N);
                        temp[i].N  = lambdaTransform(V.N, T, B, N);
                        temp[i].T  = lambdaTransform(V.T, T, B, N);
                        temp[i].B  = lambdaTransform(V.B, T, B, N);
                        temp[i].UV = V.UV;
                    }

                    SubdivTriangle(temp, SUBVIVS_COUNT, vertices, indices);
                }
            }

            return(new Primitive(m_device, (uint)vertices.Count, VertexP3N3G3B3T2.FromArray(vertices.ToArray()), indices.ToArray(), Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2));
        }
        Primitive       BuildCube()
        {
            // Default example face where B is used to stored the triangle's center and
//          float3	C0 = (new float3( -1.0f,  1.0f, 1.0f ) + new float3( -1.0f, -1.0f, 1.0f ) + new float3(  1.0f, -1.0f, 1.0f )) / 3.0f;
//          float3	C1 = (new float3( -1.0f,  1.0f, 1.0f ) + new float3(  1.0f, -1.0f, 1.0f ) + new float3(  1.0f,  1.0f, 1.0f )) / 3.0f;

            float  R  = (float)Math.Sqrt(3.0f) / BEND;
            float3 C0 = new float3(R, 0, 0);
            float3 C1 = new float3(R, 0, 0);

            VertexP3N3G3B3T2[] defaultFace = new VertexP3N3G3B3T2[6] {
                // First triangle
                new VertexP3N3G3B3T2()
                {
                    P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 0)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(-1.0f, -1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 1)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(1, 1)
                },

                // 2nd triangle
                new VertexP3N3G3B3T2()
                {
                    P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(0, 0)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 1)
                },
                new VertexP3N3G3B3T2()
                {
                    P = new float3(1.0f, 1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 0)
                },
            };

            float3[] faceNormals = new float3[6] {
                -float3.UnitX,
                float3.UnitX,
                -float3.UnitY,
                float3.UnitY,
                -float3.UnitZ,
                float3.UnitZ,
            };
            float3[] faceTangents = new float3[6] {
                float3.UnitZ,
                -float3.UnitZ,
                float3.UnitX,
                float3.UnitX,
                -float3.UnitX,
                float3.UnitX,
            };

            Func <float3, float3, float3, float3, float3> lambdaTransform = (float3 _P, float3 _T, float3 _B, float3 _N) => _P.x * _T + _P.y * _B + _P.z * _N;

            VertexP3N3G3B3T2[] vertices = new VertexP3N3G3B3T2[6 * 2 * 3];
            for (int faceIndex = 0; faceIndex < 6; faceIndex++)
            {
                float3 N = faceNormals[faceIndex];
                float3 T = faceTangents[faceIndex];
                float3 B = N.Cross(T);

                for (int i = 0; i < 6; i++)
                {
                    VertexP3N3G3B3T2 V = defaultFace[i];
                    vertices[6 * faceIndex + i].P = lambdaTransform(V.P, T, B, N);
                    vertices[6 * faceIndex + i].N = lambdaTransform(V.N, T, B, N);
                    vertices[6 * faceIndex + i].T = lambdaTransform(V.T, T, B, N);
//					vertices[6*faceIndex+i].B = lambdaTransform( V.B, T, B, N );
                    vertices[6 * faceIndex + i].B  = V.B;
                    vertices[6 * faceIndex + i].UV = V.UV;
                }
            }
            uint[] indices = new uint[6 * 2 * 3] {
//              0, 1, 2, 0, 2, 3,
//              4, 5, 6, 4, 6, 7,
//              8, 9, 10, 8, 10, 11,
//              12, 13, 14, 12, 14, 15,
//              16, 17, 18, 16, 18, 19,
//              20, 21, 22, 20, 22, 23,
                0, 1, 2, 3, 4, 5,
                6, 7, 8, 9, 10, 11,
                12, 13, 14, 15, 16, 17,
                18, 19, 20, 21, 22, 23,
                24, 25, 26, 27, 28, 29,
                30, 31, 32, 33, 34, 35
            };

            return(new Primitive(m_device, (uint)vertices.Length, VertexP3N3G3B3T2.FromArray(vertices), indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2));
        }
Exemple #6
0
/*

// 				float	Theta = 0.5 * _UV.x * PI;
// 				float3	ToLight = float3( sin( Theta ), 0, cos( Theta ) );
// 				float3	ToView = float3( -sin( Theta ), 0, cos( Theta ) );
// 
// 				float	Albedo = 0.0;
// 				const int	THETA_COUNT = 64; // * $alwaysOne; // warning X4008: floating point division by zero
// 				const float	dTheta = HALFPI / THETA_COUNT;
// 				const float	dPhi = PI / THETA_COUNT;
// 				for ( int i=0; i < THETA_COUNT; i++ )
// 				{
// 					Theta = HALFPI * (0.5 + i) / THETA_COUNT;
// 					for ( int j=0; j < THETA_COUNT; j++ )
// 					{
// 						float	Phi = PI * j / THETA_COUNT;
// 
// 						ToView = float3( sin( Theta ) * cos( Phi ), sin( Theta ) * sin( Phi ), cos( Theta ) );
// 
// 						float3	Half = normalize( ToLight + ToView );
// 
// 						// "True" and expensive evaluation of the Ward BRDF
// 						float	alpha = Roughness;
// 
// 						float	CosDelta = Half.z;	// dot( Half, _wsNormal );
// 						float	delta = acos( CosDelta );
// 						float	CosThetaL = ToLight.z;
// 						float	SinThetaL = sqrt( 1.0 - CosThetaL*CosThetaL );
// 						float	PhiL = atan2( ToLight.y, ToLight.x );
// 						float	CosThetaV = ToView.z;
// 						float	SinThetaV = sqrt( 1.0 - CosThetaV*CosThetaV );
// 						float	PhiV = atan2( ToView.y, ToView.x );
// 
// 						float	BRDF = 1.0 / square(alpha) * exp( -square( tan( delta ) / alpha ) ) * 2.0 * (1.0 + CosThetaL*CosThetaV + SinThetaL*SinThetaV*cos( PhiV - PhiL )) / pow4( CosThetaL + CosThetaV );
// 
// 						Albedo += BRDF * cos( Theta ) * sin( Theta ) * dTheta * dPhi;
// 					}
// 				}
// 
// 				Albedo *= 2.0;		// Since we integrate on half a hemisphere...
// 				Albedo *= INVPI;	// Since we forgot that in the main loop
// 
 
// ==========================================================================
// arkDebugBRDFAlbedo
// 
// 	Displays the true and theoretical BRDF albedos
// 
// ==========================================================================
//
renderProg PostFX/Debug/WardBRDFAlbedo {
	newstyle
	
	hlsl_prefix {

		#include <ward>
		
		// Displays the BRDF albedo in 2 small screen insets showing how the albedo varies with roughness
		// The albedo is computed by integrating the BRDF over an entire hemisphere of view directions for a 
		//	single light direction that varies with U in [0,1] corresponding to Theta_Light in [0,PI/2].
		//
		// The goal here is to demonstrate the albedo is correctly bounded (i.e. never > 1).
		// The especially important part is when the light is at grazing angles (i.e. U -> 1)
		//
		// Just call this function in the finalizer post-process, providing the screen UVs and the final color
		//
		void	DEBUG_DisplayWardBRDFAlbedo( float2 _UV, inout float3 _Color )
		{
			float	Roughness = lerp( 0.01, 1.0, 0.5 * (1.0 + sin( $time.x )) );	// Roughness is varying with time here...

			if ( _UV.x < 0.2 && _UV.y > 0.8 )
			{	// This version integrates the "true" BRDF
				// The formula comes from eq. (15) from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.169.9908&rep=rep1&type=pdf
				//
				_UV.x /= 0.2;
				_UV.y = (_UV.y - 0.8) / 0.2;

				float	Theta = 0.5 * _UV.x * PI;
				float3	ToLight = float3( sin( Theta ), 0, cos( Theta ) );
				float3	ToView = float3( -sin( Theta ), 0, cos( Theta ) );

				float	Albedo = 0.0;
				const int	THETA_COUNT = 64; // * $alwaysOne; // warning X4008: floating point division by zero
				const float	dTheta = HALFPI / THETA_COUNT;
				const float	dPhi = PI / THETA_COUNT;
				for ( int i=0; i < THETA_COUNT; i++ )
				{
					Theta = HALFPI * (0.5 + i) / THETA_COUNT;
					for ( int j=0; j < THETA_COUNT; j++ )
					{
						float	Phi = PI * j / THETA_COUNT;

						ToView = float3( sin( Theta ) * cos( Phi ), sin( Theta ) * sin( Phi ), cos( Theta ) );

						float3	Half = normalize( ToLight + ToView );

						// "True" and expensive evaluation of the Ward BRDF
						float	alpha = Roughness;

						float	CosDelta = Half.z;	// dot( Half, _wsNormal );
						float	delta = acos( CosDelta );
						float	CosThetaL = ToLight.z;
						float	SinThetaL = sqrt( 1.0 - CosThetaL*CosThetaL );
						float	PhiL = atan2( ToLight.y, ToLight.x );
						float	CosThetaV = ToView.z;
						float	SinThetaV = sqrt( 1.0 - CosThetaV*CosThetaV );
						float	PhiV = atan2( ToView.y, ToView.x );

						float	BRDF = 1.0 / square(alpha) * exp( -square( tan( delta ) / alpha ) ) * 2.0 * (1.0 + CosThetaL*CosThetaV + SinThetaL*SinThetaV*cos( PhiV - PhiL )) / pow4( CosThetaL + CosThetaV );

						Albedo += BRDF * cos( Theta ) * sin( Theta ) * dTheta * dPhi;
					}
				}

				Albedo *= 2.0;		// Since we integrate on half a hemisphere...
				Albedo *= INVPI;	// Since we forgot that in the main loop

				_Color = 1.0 - _UV.y < 0.8 * Albedo ? 1 : 0;

				if ( _UV.x < 0.01 )
					_Color = Roughness;
			}
			else if ( _UV.x > 0.25 && _UV.x < 0.45 && _UV.y > 0.8 )
			{	// And this version uses our implementation of the Ward BRDF
				// The formula comes from eq. (23) of the same paper.
				// Both renderings should be equal and albedo should NEVER be > 1!
				//
				_UV.x = (_UV.x - 0.25) / 0.2;
				_UV.y = (_UV.y - 0.8) / 0.2;

				WardContext	ctx;
				CreateWardContext( ctx, Roughness, 0.0, 0.0, float3( 1, 0, 0 ), float3( 0, 1, 0 ) );

				float	Theta = 0.5 * _UV.x * PI;
				float3	ToLight = float3( sin( Theta ), 0, cos( Theta ) );
				float3	ToView = float3( -sin( Theta ), 0, cos( Theta ) );

				float	Albedo = 0.0;
				const int	THETA_COUNT = 64; // * $alwaysOne;	// Allows to prevent unrolling and lenghty shader compilation!
				const float	dTheta = HALFPI / THETA_COUNT;
				const float	dPhi = PI / THETA_COUNT;
				for ( int i=0; i < THETA_COUNT; i++ )
				{
					Theta = HALFPI * (0.5 + i) / THETA_COUNT;
					for ( int j=0; j < THETA_COUNT; j++ )
					{
						float	Phi = PI * j / THETA_COUNT;

						ToView = float3( sin( Theta ) * cos( Phi ), sin( Theta ) * sin( Phi ), cos( Theta ) );
 						Albedo += ctx.ComputeWardTerm( float3( 0, 0, 1 ), ToLight, ToView ) * cos( Theta ) * sin( Theta ) * dTheta * dPhi;
					}
				}

				Albedo *= 2.0;		// Since we integrate on half a hemisphere...
				Albedo *= INVPI;	// Since we forgot that in the main loop

				_Color = (1.0 - _UV.y < 0.8 * Albedo ? 1 : 0) * float3( 1, 0, 0 );

				if ( _UV.x < 0.01 )
					_Color = Roughness;
			}
		}
	}
}

 * /


/*

		struct WardContext
		{
			float3	anisoTangentDivRoughness;
			float3	anisoBitangentDivRoughness;
			float	specularNormalization;
			float	isotropicRoughness;				// Original normalized isotropic roughness
//			float	diffuseRoughness;				// ARKANE: bmayaux (2013-10-14) Disney diffuse roughness Fresnel term
		
			// Computes the Ward normal distribution
			//	_wsNormal, surface normal
			//	_wsToLight, world space light vector
			//	_wsView, world space view vector
			//	
			float	ComputeWardTerm( float3 _wsNormal, float3 _wsToLight, float3 _wsView )
			{
				float3	Half = _wsToLight + _wsView; 
//				Half = normalize(Half); // not normalized on purpose, HdotH would be 1 if normalized, nonsense
			
				float	HdotN = dot( Half, _wsNormal );
						HdotN = max( 1e-4f, HdotN );
				float	invHdotN_2 = 1.0 / square( HdotN );
				float	invHdotN_4 = square( invHdotN_2 );
			
				float	HdotT = dot( Half, anisoTangentDivRoughness );
				float	HdotB = dot( Half, anisoBitangentDivRoughness );
				float	HdotH = dot( Half, Half );
			
				float	exponent = -invHdotN_2 * (square( HdotT ) + square( HdotB ));

 				return specularNormalization * exp( exponent ) * HdotH * invHdotN_4;
			}
		};

		void	CreateWardContext( out WardContext wardContext, float _Roughness, float _Anisotropy, float _AnisotropyAngle, float3 _Tangent, float3 _BiTangent )
		{
			// Tweak roughness so the user feels it's a linear parameter
			float	Roughness = _Roughness * _Roughness;	// Roughness squared seems to give a nice linear feel...
															// People at Disney seem to agree with me! (cf §5.4 http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v2.pdf)

			// Keep an average normalized roughness value for other people who require it (e.g. IBL)
			wardContext.isotropicRoughness = Roughness;

			// Build anisotropic roughness along tangent/bitangent
			float2	anisotropicRoughness = float2( Roughness, Roughness * saturate( 1.0 - _Anisotropy ) );

			anisotropicRoughness = max( 0.01, anisotropicRoughness );	// Make sure we don't go below 0.01 otherwise specularity is unnatural for our poor lights (only IBL with many samples would solve that!)

			// Tangent/Ax, Bitangent/Ay
			float2	sinCosAnisotropy;
			sincos( _AnisotropyAngle, sinCosAnisotropy.x, sinCosAnisotropy.y );

			float2	invRoughness = 1.0 / (1e-5 + anisotropicRoughness);
			wardContext.anisoTangentDivRoughness = (sinCosAnisotropy.y * _Tangent + sinCosAnisotropy.x * _BiTangent) * invRoughness.x;
			wardContext.anisoBitangentDivRoughness = (sinCosAnisotropy.y * _BiTangent - sinCosAnisotropy.x * _Tangent) * invRoughness.y;

			wardContext.specularNormalization = INVPI * invRoughness.x * invRoughness.y;

// ARKANE: bmayaux (2014-02-05) Sheen is not tied to roughness anymore (for better or for worse?) so it doesn't need to be tied to Ward anymore either!
// 			// ARKANE: bmayaux (2013-10-14) Disney diffuse roughness Fresnel term
// 			// Diffuse roughness starts increasing for ward roughness > 0.35 and reaches 1 for ward roughness = 0.85
// 			//	this to make sure only very diffuse and rough objects have a sheen...
// //			wardContext.diffuseRoughness = _Sheen * saturate( 2.0 * _Roughness - 0.7 );
// 			wardContext.diffuseRoughness = _Sheen;	// Use direct sheen value, at the risk of making it strange on smooth materials...
		}

*/

		#endregion

		#region Primitives

		private void	BuildPrimitives()
		{
			{
				VertexPt4[]	Vertices = new VertexPt4[4];
				Vertices[0] = new VertexPt4() { Pt = new float4( -1, +1, 0, 1 ) };	// Top-Left
				Vertices[1] = new VertexPt4() { Pt = new float4( -1, -1, 0, 1 ) };	// Bottom-Left
				Vertices[2] = new VertexPt4() { Pt = new float4( +1, +1, 0, 1 ) };	// Top-Right
				Vertices[3] = new VertexPt4() { Pt = new float4( +1, -1, 0, 1 ) };	// Bottom-Right

				ByteBuffer	VerticesBuffer = VertexPt4.FromArray( Vertices );

				m_Prim_Quad = new Primitive( m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.Pt4 );
			}

			{
				VertexP3N3G3B3T2[]	Vertices = new VertexP3N3G3B3T2[4];
				Vertices[0] = new VertexP3N3G3B3T2() { P = new float3( -1, +1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 0, 0 ) };	// Top-Left
				Vertices[1] = new VertexP3N3G3B3T2() { P = new float3( -1, -1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 0, 1 ) };	// Bottom-Left
				Vertices[2] = new VertexP3N3G3B3T2() { P = new float3( +1, +1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 1, 0 ) };	// Top-Right
				Vertices[3] = new VertexP3N3G3B3T2() { P = new float3( +1, -1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 1, 1 ) };	// Bottom-Right

				ByteBuffer	VerticesBuffer = VertexP3N3G3B3T2.FromArray( Vertices );

				m_Prim_Rectangle = new Primitive( m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2 );
			}

			{	// Build the sphere
				const int	W = 41;
				const int	H = 22;
				VertexP3N3G3B3T2[]	Vertices = new VertexP3N3G3B3T2[W*H];
				for ( int Y=0; Y < H; Y++ ) {
					double	Theta = Math.PI * Y / (H-1);
					float	CosTheta = (float) Math.Cos( Theta );
					float	SinTheta = (float) Math.Sin( Theta );
					for ( int X=0; X < W; X++ ) {
						double	Phi = 2.0 * Math.PI * X / (W-1);
						float	CosPhi = (float) Math.Cos( Phi );
						float	SinPhi = (float) Math.Sin( Phi );

						float3	N = new float3( SinTheta * SinPhi, CosTheta, SinTheta * CosPhi );
						float3	T = new float3( CosPhi, 0.0f, -SinPhi );
						float3	B = N.Cross( T );

						Vertices[W*Y+X] = new VertexP3N3G3B3T2() {
							P = N,
							N = N,
							T = T,
							B = B,
							UV = new float2( 2.0f * X / W, 1.0f * Y / H )
						};
					}
				}

				ByteBuffer	VerticesBuffer = VertexP3N3G3B3T2.FromArray( Vertices );

				uint[]		Indices = new uint[(H-1) * (2*W+2)-2];
				int			IndexCount = 0;
				for ( int Y=0; Y < H-1; Y++ ) {
					for ( int X=0; X < W; X++ ) {
						Indices[IndexCount++] = (uint) ((Y+0) * W + X);
						Indices[IndexCount++] = (uint) ((Y+1) * W + X);
					}
					if ( Y < H-2 ) {
						Indices[IndexCount++] = (uint) ((Y+1) * W - 1);
						Indices[IndexCount++] = (uint) ((Y+1) * W + 0);
					}
				}

				m_Prim_Sphere = new Primitive( m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2 );
			}

			{	// Build the cube
				float3[]	Normals = new float3[6] {
					-float3.UnitX,
					float3.UnitX,
					-float3.UnitY,
					float3.UnitY,
					-float3.UnitZ,
					float3.UnitZ,
				};

				float3[]	Tangents = new float3[6] {
					float3.UnitZ,
					-float3.UnitZ,
					float3.UnitX,
					-float3.UnitX,
					-float3.UnitX,
					float3.UnitX,
				};

				VertexP3N3G3B3T2[]	Vertices = new VertexP3N3G3B3T2[6*4];
				uint[]		Indices = new uint[2*6*3];

				for ( int FaceIndex=0; FaceIndex < 6; FaceIndex++ ) {
					float3	N = Normals[FaceIndex];
					float3	T = Tangents[FaceIndex];
					float3	B = N.Cross( T );

					Vertices[4*FaceIndex+0] = new VertexP3N3G3B3T2() {
						P = N - T + B,
						N = N,
						T = T,
						B = B,
						UV = new float2( 0, 0 )
					};
					Vertices[4*FaceIndex+1] = new VertexP3N3G3B3T2() {
						P = N - T - B,
						N = N,
						T = T,
						B = B,
						UV = new float2( 0, 1 )
					};
					Vertices[4*FaceIndex+2] = new VertexP3N3G3B3T2() {
						P = N + T - B,
						N = N,
						T = T,
						B = B,
						UV = new float2( 1, 1 )
					};
					Vertices[4*FaceIndex+3] = new VertexP3N3G3B3T2() {
						P = N + T + B,
						N = N,
						T = T,
						B = B,
						UV = new float2( 1, 0 )
					};

					Indices[2*3*FaceIndex+0] = (uint) (4*FaceIndex+0);
					Indices[2*3*FaceIndex+1] = (uint) (4*FaceIndex+1);
					Indices[2*3*FaceIndex+2] = (uint) (4*FaceIndex+2);
					Indices[2*3*FaceIndex+3] = (uint) (4*FaceIndex+0);
					Indices[2*3*FaceIndex+4] = (uint) (4*FaceIndex+2);
					Indices[2*3*FaceIndex+5] = (uint) (4*FaceIndex+3);
				}

				ByteBuffer	VerticesBuffer = VertexP3N3G3B3T2.FromArray( Vertices );

				m_Prim_Cube = new Primitive( m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2 );
			}
		}