Convert WaveFront to Speckle

Hi team, I have issue with convert format .obj mesh to speckle, it show me some thing wrong, I hope can hear some assistant idea of team about that :

This is Class read file .obj to speckle mesh

using System;
using System.Collections.Generic;
using System.IO;

namespace ForgeToolkit.WaveFront
{
    public class WaveFront
    {
        public List<MeshGroup> MeshGroups { get; } = new List<MeshGroup>();
        public List<Material> Materials { get; } = new List<Material>();

        public void Load(string filePath)
        {
            try
            {
                // get file mtl inside
                string mtlFileName = null;
                using (StreamReader reader = new StreamReader(filePath))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        line = line.Trim();
                        if (string.IsNullOrEmpty(line) || line[0] == '#')
                            continue;

                        string[] parts = line.Split(' ');

                        if (parts[0] == "mtllib")
                        {
                            // Material library reference
                            mtlFileName = parts[1];
                            break;
                        }
                    }
                }

                if (!string.IsNullOrEmpty(mtlFileName))
                {
                    // Load the associated .mtl file
                    LoadMaterialFile(Path.Combine(Path.GetDirectoryName(filePath), mtlFileName));
                }
                using (StreamReader reader = new StreamReader(filePath))
                {
                    string line;
                    MeshGroup currentGroup = null;
                    Material currentMaterial = null; // Track the current material for assignment

                    while ((line = reader.ReadLine()) != null)
                    {
                        line = line.Trim();
                        if (string.IsNullOrEmpty(line) || line[0] == '#')
                            continue;

                        string[] parts = line.Split(' ');

                        if (parts[0] == "v")
                        {
                            // Vertex line
                            if (parts.Length >= 4 &&
                                float.TryParse(parts[1], out float x) &&
                                float.TryParse(parts[2], out float y) &&
                                float.TryParse(parts[3], out float z))
                            {
                                if (currentGroup != null)
                                {
                                    currentGroup.Vertices.Add(new Vector3(x, y, z));
                                }
                            }
                        }
                        else if (parts[0] == "f")
                        {
                            // Face line
                            if (parts.Length >= 4)
                            {
                                List<int> vertexIndices = new List<int>();
                                for (int i = 1; i < parts.Length; i++)
                                {
                                    string[] vertexInfo = parts[i].Split('/');
                                    if (int.TryParse(vertexInfo[0], out int vertexIndex))
                                    {
                                        vertexIndices.Add(vertexIndex);
                                    }
                                }

                                if (vertexIndices.Count >= 3)
                                {
                                    if (currentGroup != null)
                                    {
                                        currentGroup.Faces.Add(new Face(vertexIndices[0], vertexIndices[1],
                                            vertexIndices[2]));
                                        currentGroup.Material = currentMaterial; // Assign the current material
                                    }
                                }
                            }
                        }
                        else if (parts[0] == "vc")
                        {
                            // Vertex color line (custom extension; adjust this line to match your OBJ format)
                            if (parts.Length >= 4 &&
                                float.TryParse(parts[1], out float r) &&
                                float.TryParse(parts[2], out float g) &&
                                float.TryParse(parts[3], out float b))
                            {
                                if (currentGroup != null)
                                {
                                    currentGroup.VertexColors.Add(new Color(r, g, b));
                                }
                            }
                        }
                        else if (parts[0] == "g")
                        {
                            // Group statement
                            string groupName = string.Join(" ", parts, 1, parts.Length - 1).Trim();
                            currentGroup = new MeshGroup(groupName);
                            MeshGroups.Add(currentGroup);
                            currentMaterial = null; // Reset the current material
                        }
                        else if (parts[0] == "usemtl")
                        {
                            // Use material statement
                            string materialName = string.Join(" ", parts, 1, parts.Length - 1).Trim();
                            currentMaterial = Materials.Find(m => m.Name == materialName);
                        }
                    }
                }


            }
            catch (Exception ex)
            {
                Console.WriteLine("Error reading OBJ file: " + ex.Message);
            }
        }

        private void LoadMaterialFile(string mtlFilePath)
        {
            try
            {
                using (StreamReader reader = new StreamReader(mtlFilePath))
                {
                    string line;
                    Material currentMaterial = null;

                    while ((line = reader.ReadLine()) != null)
                    {
                        line = line.Trim();
                        if (string.IsNullOrEmpty(line) || line[0] == '#')
                            continue;

                        string[] parts = line.Split(' ');

                        if (parts[0] == "newmtl")
                        {
                            // New material definition
                            string materialName = string.Join(" ", parts, 1, parts.Length - 1).Trim();
                            currentMaterial = new Material(materialName);
                            Materials.Add(currentMaterial);
                        }
                        else if (parts[0] == "Kd" && currentMaterial != null)
                        {
                            // Diffuse color (adjust based on your .mtl format)
                            if (parts.Length >= 4 &&
                                float.TryParse(parts[1], out float r) &&
                                float.TryParse(parts[2], out float g) &&
                                float.TryParse(parts[3], out float b))
                            {
                                currentMaterial.Color = new Color(r, g, b);
                            }
                        }
                        // Add more material properties parsing as needed (e.g., specular color, ambient color, etc.)
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error reading .mtl file: " + ex.Message);
            }
        }
    }

    public class Vector3
    {
        public float X { get; }
        public float Y { get; }
        public float Z { get; }

        public Vector3(float x, float y, float z)
        {
            X = x;
            Y = y;
            Z = z;
        }
    }

    public class Face
    {
        public int V1 { get; }
        public int V2 { get; }
        public int V3 { get; }

        public Face(int v1, int v2, int v3)
        {
            V1 = v1;
            V2 = v2;
            V3 = v3;
        }
    }

    public class Color
    {
        public float R { get; }
        public float G { get; }
        public float B { get; }

        public Color(float r, float g, float b)
        {
            R = r;
            G = g;
            B = b;
        }
    }

    public class Material
    {
        public string Name { get; }
        public int Id { get; set; }
        public Color Color { get; set; } // Adjust based on your .mtl format

        public Material(string name)
        {
            Name = name;
            if (name.Contains("."))
            {
                string[] parts = name.Split('.');
                if (parts.Length == 2 && int.TryParse(parts[1], out int id))
                {
                    Id = id;
                }
            }
            else
            {
                Id = -1;
            }
        }
    }

    public class MeshGroup
    {
        public string Name { get; }
        public int Id { get; set; }
        public List<Vector3> Vertices { get; } = new List<Vector3>();
        public List<Face> Faces { get; } = new List<Face>();
        public List<Color> VertexColors { get; } = new List<Color>();
        public Material Material { get; set; }

        public MeshGroup(string name)
        {
            Name = name;
            if (name.Contains("."))
            {
                string[] parts = name.Split('.');
                if (parts.Length == 2 && int.TryParse(parts[1], out int id))
                {
                    Id = id;
                }
            }
            else
            {
                Id = -1;
            }
        }
    }
}

And I tried to send it to speckle :


WaveFront waveFront = new WaveFront();
waveFront.Load(objPath);
List<MeshGroup> meshGroups = waveFront.MeshGroups;
Console.WriteLine("Done with ReadTriangles");
List<Mesh> baseMeshes = new List<Mesh>();
foreach (MeshGroup meshGroup in meshGroups)
{
    Mesh mesh = MeshGroupToSpeckleMesh(meshGroup);
    baseMeshes.Add(mesh);
}
// send list baseMeshes to speckle
Base data = new Base();
data["@baseMeshes"] = baseMeshes;

string streamId = "db5902deb6";
string send = await Helpers.Send(streamId, data, "test obj").ConfigureAwait(false);
Console.WriteLine("Done with Send Data To Speckle");
Objects.Geometry.Mesh MeshGroupToSpeckleMesh(MeshGroup meshGroup)
{
    Mesh mesh = new Mesh();
    mesh.vertices = meshGroup.Vertices.SelectMany(v => new double[] { v.X, v.Y, v.Z }).ToList();
    mesh.faces = meshGroup.Faces.SelectMany(f => new int[] { f.V1, f.V2, f.V3 }).ToList();
    mesh.colors = ToSpeckleColor(meshGroup.Material.Color);
    mesh.id = meshGroup.Id.ToString();
    mesh.units = Units.Feet;
    return mesh;
}
List<int> ToSpeckleColor(Color color)
{
    int r = (int) color.R;
    int g = (int) color.G;
    int b = (int) color.B;
    return new List<int> { r, g, b, 0 };
}

This is my file sample :smiling_face_with_three_hearts:
ac734220-e1c3-324f-93df-b5c65ffb2f5e.obj (440.1 KB)
ac734220-e1c3-324f-93df-b5c65ffb2f5e.mtl (770 Bytes)

And this one is how it look like when open use https://3dviewer.net/ :

Any recommend is apprecitated, thank you, my thinking at the first time is it relate to Normals define at vn

We’ve got an obj file importer for the server, written in python:

Maybe it can serve as inspiration!

1 Like

Thank you @dimitrie , It can import perfecty with import from server, but I think team missing normals information inside .obj format , the location of my chair correct is in left side