private static IExplicitModel BuildWallModel(List <BuildingWallSegment> wallSegments)
        {
            var vertices = new VertexPosTanNormTex[wallSegments.Count * 4];
            var indices  = new int[wallSegments.Count * 6];

            for (int i = 0; i < wallSegments.Count; i++)
            {
                var segment        = wallSegments[i].Basement;
                var height         = wallSegments[i].Height;
                var vertexOffset   = i * 4;
                var indexOffset    = i * 6;
                var tangent        = (segment.Point2 - segment.Point1).Normalize();
                var normal         = Vector3.Cross(tangent, Vector3.UnitY).Normalize();
                var texCoordWidth  = Math.Max((float)Math.Round(segment.Length / 2), 1f);
                var texCoordHeight = Math.Max((float)Math.Round(height / 2), 1f);
                vertices[vertexOffset + 0] = new VertexPosTanNormTex(segment.Point1, tangent, normal, new Vector2(0, texCoordHeight));
                vertices[vertexOffset + 1] = new VertexPosTanNormTex(segment.Point1 + height * Vector3.UnitY, tangent, normal, new Vector2(0, 0));
                vertices[vertexOffset + 2] = new VertexPosTanNormTex(segment.Point2 + height * Vector3.UnitY, tangent, normal, new Vector2(texCoordWidth, 0));
                vertices[vertexOffset + 3] = new VertexPosTanNormTex(segment.Point2, tangent, normal, new Vector2(texCoordWidth, texCoordHeight));
                indices[indexOffset + 0]   = vertexOffset;
                indices[indexOffset + 1]   = vertexOffset + 1;
                indices[indexOffset + 2]   = vertexOffset + 2;
                indices[indexOffset + 3]   = vertexOffset;
                indices[indexOffset + 4]   = vertexOffset + 2;
                indices[indexOffset + 5]   = vertexOffset + 3;
            }
            return(ExplicitModel.FromVertices(vertices, indices, ExplicitModelPrimitiveTopology.TriangleList));
        }
        public override IResource GetNew(IResourceSource resourceSource)
        {
            var cSource             = (Source)resourceSource;
            var verticalIndexOffset = 2 * cSource.HalfNumCircleSegments;
            var vertices            = new VertexPosTanNormTex[verticalIndexOffset * cSource.HalfNumCircleSegments];

            for (int i = 0; i < cSource.HalfNumCircleSegments; i++)
            {
                for (int j = 0; j < verticalIndexOffset; j++)
                {
                    var u   = (float)j / (verticalIndexOffset - 1);
                    var v   = (float)i / (cSource.HalfNumCircleSegments - 1);
                    var phi = MathHelper.TwoPi * u + MathHelper.Pi;
                    var psi = MathHelper.PiOver2 - MathHelper.Pi * v;
                    var z   = MathHelper.Cos(phi) * MathHelper.Cos(psi);
                    var x   = MathHelper.Sin(phi) * MathHelper.Cos(psi);
                    var y   = MathHelper.Sin(psi);

                    var normal   = new Vector3(x, y, z);
                    var position = normal;
                    var tangent  = Vector3.Cross(Vector3.UnitY, normal).Normalize();
                    var texcoord = new Vector2(u, v);
                    vertices[i * verticalIndexOffset + j] = new VertexPosTanNormTex(
                        position, tangent, normal, texcoord);
                }
            }
            var indexList = new List <int>();

            for (int i = 0; i < cSource.HalfNumCircleSegments - 1; i++)
            {
                for (int j = 0; j < verticalIndexOffset - 1; j++)
                {
                    var topLeftIndex = i * verticalIndexOffset + j;
                    indexList.Add(topLeftIndex);
                    indexList.Add(topLeftIndex + 1);
                    indexList.Add(topLeftIndex + 1 + verticalIndexOffset);
                    indexList.Add(topLeftIndex);
                    indexList.Add(topLeftIndex + 1 + verticalIndexOffset);
                    indexList.Add(topLeftIndex + verticalIndexOffset);
                }
            }
            var indices = indexList.ToArray();

            if (cSource.Inverse)
            {
                for (int i = 0; i < vertices.Length; i++)
                {
                    vertices[i].Normal = -vertices[i].Normal;
                }
                for (int i = 0; i < indices.Length; i += 3)
                {
                    CodingHelper.Swap(ref indices[i + 1], ref indices[i + 2]);
                }
            }
            return(ExplicitModel.FromVertices(vertices, indices, ExplicitModelPrimitiveTopology.TriangleList).WithSource(cSource));
        }
        public unsafe GlObjects GetGlObjects()
        {
            if (!dirty)
            {
                return(glObjects);
            }

            if (glObjects.Vao == null)
            {
                glObjects.Vao = infra.GlContext.Create.VertexArray();
            }

            glObjects.Vao.DisableVertexAttributesStartingFrom(0);

            var bufferUsageHint = model.Volatility == ResourceVolatility.Volatile
                ? BufferUsageHint.DynamicRead
                : BufferUsageHint.StaticRead;

            // todo: other formats
            var vertices    = new VertexPosTanNormTex[model.Positions.Length];
            var getTangent  = model.Tangents != null ? (Func <IExplicitModel, int, Vector3>)((m, i) => m.Tangents[i]) : (m, i) => Vector3.Zero;
            var getNormal   = model.Normals != null ? (Func <IExplicitModel, int, Vector3>)((m, i) => m.Normals[i]) : (m, i) => Vector3.Zero;
            var getTexCoord = model.TexCoords != null ? (Func <IExplicitModel, int, Vector2>)((m, i) => m.TexCoords[i]) : (m, i) => Vector2.Zero;

            for (var i = 0; i < vertices.Length; i++)
            {
                vertices[i] = new VertexPosTanNormTex(model.Positions[i], getTangent(model, i), getNormal(model, i), getTexCoord(model, i));
            }
            var vertexBufferSize = vertices.Length * VertexPosTanNormTex.SizeInBytes;

            if (glObjects.VertexBuffer == null || glObjects.VertexBuffer.SizeInBytes != vertexBufferSize)
            {
                glObjects.VertexBuffer?.Dispose();
                glObjects.VertexBuffer = infra.GlContext.Create.Buffer(BufferTarget.Array, vertexBufferSize, bufferUsageHint);
            }

            fixed(VertexPosTanNormTex *pVertices = vertices)
            glObjects.VertexBuffer.SetData((IntPtr)pVertices);

            var elemInfos    = VertexPosTanNormTex.GetElementsInfos(0);
            var positionInfo = elemInfos.Single(x => x.CommonSemantic == CommonVertexSemantic.Position);
            var normalInfo   = elemInfos.Single(x => x.CommonSemantic == CommonVertexSemantic.Normal);
            var tangentInfo  = elemInfos.Single(x => x.CommonSemantic == CommonVertexSemantic.Tangent);
            var texCoordInfo = elemInfos.Single(x => x.CommonSemantic == CommonVertexSemantic.TexCoord);

            glObjects.Vao.SetVertexAttributeF(0, glObjects.VertexBuffer, VertexAttributeDimension.Three, VertexAttribPointerType.Float, false, positionInfo.Stride, positionInfo.Offset);
            glObjects.Vao.SetVertexAttributeF(1, glObjects.VertexBuffer, VertexAttributeDimension.Three, VertexAttribPointerType.Float, false, normalInfo.Stride, normalInfo.Offset);
            glObjects.Vao.SetVertexAttributeF(2, glObjects.VertexBuffer, VertexAttributeDimension.Two, VertexAttribPointerType.Float, false, texCoordInfo.Stride, texCoordInfo.Offset);
            glObjects.Vao.SetVertexAttributeF(3, glObjects.VertexBuffer, VertexAttributeDimension.Three, VertexAttribPointerType.Float, false, tangentInfo.Stride, tangentInfo.Offset);

            if (!model.IndicesAreTrivial)
            {
                glObjects.SixteenBitIndices = model.MinIndexSize < sizeof(int);
                var indexSize       = glObjects.SixteenBitIndices ? sizeof(ushort) : sizeof(int);
                var indexBufferSize = model.Indices.Length * indexSize;
                if (glObjects.IndexBuffer == null || glObjects.IndexBuffer.SizeInBytes != indexBufferSize)
                {
                    glObjects.Vao.SetElementArrayBuffer(null);
                    glObjects.IndexBuffer?.Dispose();
                    glObjects.IndexBuffer = infra.GlContext.Create.Buffer(BufferTarget.ElementArray, indexBufferSize, bufferUsageHint);
                }

                if (glObjects.SixteenBitIndices)
                {
                    var indices = model.Indices.Select(x => (ushort)x).ToArray();

                    fixed(ushort *pIndices = indices)
                    glObjects.IndexBuffer.SetData((IntPtr)pIndices);
                }
                else
                {
                    fixed(int *pIndices = model.Indices)
                    glObjects.IndexBuffer.SetData((IntPtr)pIndices);
                }

                glObjects.Vao.SetElementArrayBuffer(glObjects.IndexBuffer);
            }
            else
            {
                glObjects.Vao.SetElementArrayBuffer(null);
                glObjects.IndexBuffer?.Dispose();
                glObjects.IndexBuffer = null;
            }

            dirty = false;
            return(glObjects);
        }