public G3D(IEnumerable <GeometryAttribute> attributes, G3dHeader?header = null, int numCornersPerFaceOverride = -1) : base(attributes, numCornersPerFaceOverride) { Header = header ?? new G3dHeader(); foreach (var attr in Attributes.ToEnumerable()) { var desc = attr.Descriptor; switch (desc.Semantic) { case Semantic.Index: if (attr.IsTypeAndAssociation <int>(Association.assoc_corner)) { Indices = Indices ?? attr.AsType <int>().Data; } if (attr.IsTypeAndAssociation <short>(Association.assoc_corner)) { Indices = Indices ?? attr.AsType <short>().Data.Select(x => (int)x); } break; case Semantic.Position: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { Vertices = Vertices ?? attr.AsType <Vector3>().Data; } if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_corner)) { Vertices = Vertices ?? attr.AsType <Vector3>().Data; // TODO: is this used? } if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_shapevertex)) { ShapeVertices = ShapeVertices ?? attr.AsType <Vector3>().Data; } break; case Semantic.Tangent: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { VertexTangents = VertexTangents ?? attr.AsType <Vector3>().Data.Select(v => v.ToVector4()); } if (attr.IsTypeAndAssociation <Vector4>(Association.assoc_vertex)) { VertexTangents = VertexTangents ?? attr.AsType <Vector4>().Data; } break; case Semantic.Uv: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { AllVertexUvs.Add(attr.AsType <Vector3>().Data.Select(uv => uv.ToVector2())); } if (attr.IsTypeAndAssociation <Vector2>(Association.assoc_vertex)) { AllVertexUvs.Add(attr.AsType <Vector2>().Data); } break; case Semantic.Color: if (desc.Association == Association.assoc_vertex) { AllVertexColors.Add(attr.AttributeToColors()); } if (desc.Association == Association.assoc_shape) { ShapeColors = ShapeColors ?? attr.AttributeToColors(); } if (desc.Association == Association.assoc_material) { MaterialColors = MaterialColors ?? attr.AttributeToColors(); } break; case Semantic.VertexOffset: if (attr.IsTypeAndAssociation <int>(Association.assoc_mesh)) { MeshVertexOffsets = MeshVertexOffsets ?? attr.AsType <int>().Data; } if (attr.IsTypeAndAssociation <int>(Association.assoc_shape)) { ShapeVertexOffsets = ShapeVertexOffsets ?? attr.AsType <int>().Data; } break; case Semantic.IndexOffset: if (attr.IsTypeAndAssociation <int>(Association.assoc_mesh)) { MeshIndexOffsets = MeshIndexOffsets ?? attr.AsType <int>().Data; } if (attr.IsTypeAndAssociation <int>(Association.assoc_submesh)) { SubmeshIndexOffsets = SubmeshIndexOffsets ?? attr.AsType <int>().Data; } break; case Semantic.Normal: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_face)) { FaceNormals = FaceNormals ?? attr.AsType <Vector3>().Data; } if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { VertexNormals = VertexNormals ?? attr.AsType <Vector3>().Data; } break; case Semantic.Material: if (attr.IsTypeAndAssociation <int>(Association.assoc_face)) { FaceMaterials = FaceMaterials ?? attr.AsType <int>().Data; } if (attr.IsTypeAndAssociation <int>(Association.assoc_submesh)) { SubmeshMaterials = SubmeshMaterials ?? attr.AsType <int>().Data; } break; case Semantic.Parent: if (attr.IsTypeAndAssociation <int>(Association.assoc_instance)) { InstanceParents = InstanceParents ?? attr.AsType <int>().Data; } break; case Semantic.Mesh: if (attr.IsTypeAndAssociation <int>(Association.assoc_instance)) { InstanceMeshes = InstanceMeshes ?? attr.AsType <int>().Data; } break; case Semantic.Transform: if (attr.IsTypeAndAssociation <Matrix4x4>(Association.assoc_instance)) { InstanceTransforms = InstanceTransforms ?? attr.AsType <Matrix4x4>().Data; } break; case Semantic.Width: if (attr.IsTypeAndAssociation <float>(Association.assoc_shape)) { ShapeWidths = ShapeWidths ?? attr.AsType <float>().Data; } break; case Semantic.Glossiness: if (attr.IsTypeAndAssociation <float>(Association.assoc_material)) { MaterialGlossiness = attr.AsType <float>().Data; } break; case Semantic.Smoothness: if (attr.IsTypeAndAssociation <float>(Association.assoc_material)) { MaterialSmoothness = attr.AsType <float>().Data; } break; case Semantic.SubMeshOffset: if (attr.IsTypeAndAssociation <int>(Association.assoc_mesh)) { MeshSubmeshOffset = attr.AsType <int>().Data; } break; } } // If no vertices are provided, we are going to generate a list of zero vertices. if (Vertices == null) { Vertices = Vector3.Zero.Repeat(0); } // If no indices are provided then we are going to have to treat the index buffer as indices if (Indices == null) { Indices = Vertices.Indices(); } // Compute face normals if possible if (FaceNormals == null && VertexNormals != null) { FaceNormals = NumFaces.Select(ComputeFaceNormal); } if (NumMeshes > 0) { // Mesh offset is the same as the offset of its first submesh. if (MeshSubmeshOffset != null) { MeshIndexOffsets = MeshSubmeshOffset.Select(submesh => SubmeshIndexOffsets[submesh]); MeshSubmeshCount = GetSubArrayCounts(MeshSubmeshOffset.Count, MeshSubmeshOffset, NumSubmeshes).Evaluate(); } if (MeshIndexOffsets != null) { MeshIndexCounts = GetSubArrayCounts(NumMeshes, MeshIndexOffsets, NumCorners); MeshVertexOffsets = MeshIndexOffsets .Zip(MeshIndexCounts, (start, count) => (start, count)) .Select(range => Indices.SubArray(range.start, range.count).Min()); } if (MeshVertexOffsets != null) { MeshVertexCounts = GetSubArrayCounts(NumMeshes, MeshVertexOffsets, NumVertices); } } if (SubmeshIndexOffsets != null) { SubmeshIndexCount = GetSubArrayCounts(SubmeshIndexOffsets.Count, SubmeshIndexOffsets, NumCorners).Evaluate(); } // Compute all meshes Meshes = NumMeshes.Select(i => new G3dMesh(this, i)); if (MaterialColors != null) { Materials = MaterialColors.Count.Select(i => new G3dMaterial(this, i)); } // Process the shape data if (ShapeVertices == null) { ShapeVertices = Vector3.Zero.Repeat(0); } if (ShapeVertexOffsets == null) { ShapeVertexOffsets = Array.Empty <int>().ToIArray(); } if (ShapeColors == null) { ShapeColors = Vector4.Zero.Repeat(0); } if (ShapeWidths == null) { ShapeWidths = Array.Empty <float>().ToIArray(); } ShapeVertexCounts = GetSubArrayCounts(NumShapes, ShapeVertexOffsets, ShapeVertices.Count); ValidateSubArrayCounts(ShapeVertexCounts, nameof(ShapeVertexCounts)); Shapes = NumShapes.Select(i => new G3dShape(this, i)); }
} // The SubGeometry associated with the index public G3D(IEnumerable <GeometryAttribute> attributes, G3dHeader?header = null, int numCornersPerFaceOverride = -1) : base(attributes, numCornersPerFaceOverride) { Header = header ?? new G3dHeader(); foreach (var attr in Attributes.ToEnumerable()) { var desc = attr.Descriptor; switch (desc.Semantic) { case Semantic.Index: if (attr.IsTypeAndAssociation <int>(Association.assoc_corner)) { Indices = Indices ?? attr.AsType <int>().Data; } if (attr.IsTypeAndAssociation <short>(Association.assoc_corner)) { Indices = Indices ?? attr.AsType <short>().Data.Select(x => (int)x); } break; case Semantic.Position: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { Vertices = Vertices ?? attr.AsType <Vector3>().Data; } if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_corner)) { Vertices = Vertices ?? attr.AsType <Vector3>().Data; } break; case Semantic.Tangent: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { VertexTangents = VertexTangents ?? attr.AsType <Vector3>().Data.Select(v => v.ToVector4()); } if (attr.IsTypeAndAssociation <Vector4>(Association.assoc_vertex)) { VertexTangents = VertexTangents ?? attr.AsType <Vector4>().Data; } break; case Semantic.Uv: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { AllVertexUvs.Add(attr.AsType <Vector3>().Data.Select(uv => uv.ToVector2())); } if (attr.IsTypeAndAssociation <Vector2>(Association.assoc_vertex)) { AllVertexUvs.Add(attr.AsType <Vector2>().Data); } break; case Semantic.Color: if (desc.Association == Association.assoc_vertex) { AllVertexColors.Add(attr.AttributeToColors()); } break; case Semantic.VertexOffset: if (attr.IsTypeAndAssociation <int>(Association.assoc_subgeometry)) { SubGeometryVertexOffsets = SubGeometryVertexOffsets ?? attr.AsType <int>().Data; } break; case Semantic.IndexOffset: if (attr.IsTypeAndAssociation <int>(Association.assoc_subgeometry)) { SubGeometryIndexOffsets = SubGeometryIndexOffsets ?? attr.AsType <int>().Data; } break; case Semantic.Normal: if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_face)) { FaceNormals = FaceNormals ?? attr.AsType <Vector3>().Data; } if (attr.IsTypeAndAssociation <Vector3>(Association.assoc_vertex)) { VertexNormals = VertexNormals ?? attr.AsType <Vector3>().Data; } break; case Semantic.MaterialId: if (attr.IsTypeAndAssociation <int>(Association.assoc_face)) { FaceMaterialIds = FaceMaterialIds ?? attr.AsType <int>().Data; } break; // NOTE: some VIMs have Group and others have GroupId case Semantic.Group: case Semantic.GroupId: if (attr.IsTypeAndAssociation <int>(Association.assoc_face)) { FaceGroups = FaceGroups ?? attr.AsType <int>().Data; } break; case Semantic.Parent: if (attr.IsTypeAndAssociation <int>(Association.assoc_instance)) { InstanceParents = InstanceParents ?? attr.AsType <int>().Data; } break; case Semantic.SubGeometry: if (attr.IsTypeAndAssociation <int>(Association.assoc_instance)) { InstanceSubGeometries = InstanceSubGeometries ?? attr.AsType <int>().Data; } break; case Semantic.Transform: if (attr.IsTypeAndAssociation <Matrix4x4>(Association.assoc_instance)) { InstanceTransforms = InstanceTransforms ?? attr.AsType <Matrix4x4>().Data; } break; } } // If no vertices are provided, we are going to generate a list of zero vertices. if (Vertices == null) { Vertices = Vector3.Zero.Repeat(0); } // If no indices are provided then we are going to have to treat the index buffer as indices if (Indices == null) { Indices = Vertices.Indices(); } // Compute face normals if possible if (FaceNormals == null && VertexNormals != null) { FaceNormals = NumFaces.Select(ComputeFaceNormal); } if (NumSubgeometries > 0) { if (Indices == null) { Debug.WriteLine($"If the number of Subgeometries is greater than zero then the number of index buffer should be present"); } if (Vertices == null) { Debug.WriteLine($"If the number of Subgeometries is greater than zero then the vertex buffer is expected"); } if (SubGeometryIndexOffsets == null) { Debug.WriteLine($"If the number of Subgeometries is greater than zero then the Subgeometries index buffer is expected "); } if (SubGeometryVertexOffsets == null) { Debug.WriteLine($"If the number of Subgeometries is greater than zero then the Subgeometries vertex buffer is expected "); } if (SubGeometryIndexCounts == null) { if (NumSubgeometries != SubGeometryIndexOffsets.Count) { throw new Exception($"Internal error: SubGeometry index offsets count {SubGeometryIndexOffsets.Count} is different than number of Subgeometries {NumSubgeometries}"); } SubGeometryIndexCounts = NumSubgeometries.Select(i => i < (NumSubgeometries - 1) ? SubGeometryIndexOffsets[i + 1] - SubGeometryIndexOffsets[i] : Indices.Count - SubGeometryIndexOffsets[i]); } for (var i = 0; i < SubGeometryIndexCounts.Count; ++i) { var n = SubGeometryIndexCounts[i]; if (n < 0) { throw new Exception($"SubGeometry {i} has negative number of indices {n}"); } if (NumCornersPerFace > 0 && (n % NumCornersPerFace) != 0) { throw new Exception($"SubGeometry {i} does not have an index buffer count {n} that is divisible by {NumCornersPerFace}"); } } if (SubGeometryVertexCounts == null) { if (NumSubgeometries != SubGeometryVertexOffsets.Count) { throw new Exception($"Internal error: SubGeometry index offsets count {SubGeometryVertexOffsets.Count} is different than number of Subgeometries {NumSubgeometries}"); } SubGeometryVertexCounts = NumSubgeometries.Select(i => i < (NumSubgeometries - 1) ? SubGeometryVertexOffsets[i + 1] - SubGeometryVertexOffsets[i] : Vertices.Count - SubGeometryVertexOffsets[i] ); } for (var i = 0; i < SubGeometryVertexCounts.Count; ++i) { var n = SubGeometryVertexCounts[i]; if (n < 0) { throw new Exception($"SubGeometry {i} has negative number of indices {n}"); } } } // Compute all of the sub-geometries SubGeometries = NumSubgeometries.Select(i => new G3dSubGeometry(this, i)); }