File: StlMeshParser.cs

package info (click to toggle)
mujoco 2.2.2-3.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 39,796 kB
  • sloc: ansic: 28,947; cpp: 28,897; cs: 14,241; python: 10,465; xml: 5,104; sh: 93; makefile: 34
file content (127 lines) | stat: -rw-r--r-- 4,898 bytes parent folder | download | duplicates (2)
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2019 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;

namespace Mujoco {

public static class BinaryReaderExtensions {
  public static Vector3 ReadVector3(this BinaryReader reader) {
    var x = reader.ReadSingle();
    var y = reader.ReadSingle();
    var z = reader.ReadSingle();
    return new Vector3(x, y, z);
  }
}

public static class BinaryWriterExtensions {
  public static void Write(this BinaryWriter writer, Vector3 val) {
    writer.Write(val.x);
    writer.Write(val.y);
    writer.Write(val.z);
  }
}

public class StlMeshParser {

  private const int _headerLength = 80;
  private const int _attributesSizeLength = 2;
  private const int _verticesPerTriangle = 3;
  private const int _unityLimitNumVerticesPerMesh = 65535;
  private const string _asciiFileTypeId = "solid";

  private static Vector3 ToXZY(Vector3 v) => new Vector3(v.x, v.z, v.y);

  // The binary STL format is described here: https://en.wikipedia.org/wiki/STL_(file_format)
  public static Mesh ParseBinary(byte[] stlFileContents, Vector3 scale) {
    var fileTypeId = System.Text.Encoding.UTF8.GetString(
      stlFileContents.Take(_asciiFileTypeId.Length).ToArray());
    if (fileTypeId == _asciiFileTypeId) {
      throw new IOException("Ascii STL file format is not supported.");
    }

    using (var stream = new MemoryStream(stlFileContents)) {
      using (var reader = new BinaryReader(stream)) {
        reader.ReadBytes(_headerLength);
        var numTriangles = reader.ReadUInt32();
        var numVertices = numTriangles * _verticesPerTriangle;
        if (numVertices > _unityLimitNumVerticesPerMesh) {
          throw new IndexOutOfRangeException(
              "The mesh exceeds the number of vertices per mesh allowed by Unity. " +
              $"({numVertices} > {_unityLimitNumVerticesPerMesh})");
        }
        var triangleIndices = new List<int>(capacity: (int)numVertices);
        var vertices = new List<Vector3>(capacity: (int)numVertices);
        var normals = new List<Vector3>(capacity: (int)numVertices);
        for (var i = 0; i < numVertices; i += _verticesPerTriangle) {
          var triangleNormal = ToXZY(reader.ReadVector3());
          normals.AddRange(new[] { triangleNormal, triangleNormal, triangleNormal });
          vertices.AddRange(new[] {
            ToXZY(reader.ReadVector3()),
            ToXZY(reader.ReadVector3()),
            ToXZY(reader.ReadVector3()) });
          triangleIndices.AddRange(new[] {i, i + 2, i + 1});
          reader.ReadInt16();  // Read the unused attribute indices field.
        }

        var mesh = new Mesh();
        mesh.vertices = vertices.ToArray();
        mesh.normals = normals.ToArray();
        mesh.triangles = triangleIndices.ToArray();
        mesh.vertices = mesh.vertices.Select(
            vertexPosition => Vector3.Scale(vertexPosition, scale)).ToArray();
        mesh.RecalculateNormals();
        mesh.RecalculateTangents();
        mesh.RecalculateBounds();
        return mesh;
      }
    }
  }

  public static byte[] SerializeBinary(Mesh mesh) {
    using (var stream = new MemoryStream()) {
      using (var writer = new BinaryWriter(stream)) {
        // Write the header. We only want to write the id and then pad the rest with zeros, up to 80
        // bytes.
        writer.Write(_asciiFileTypeId);
        writer.Write(new byte[_headerLength - _asciiFileTypeId.Length - 1]);

        var numTriangles = mesh.triangles.Length / 3;
        writer.Write((int)numTriangles);

        for (var i = 0; i < mesh.triangles.Length; i += _verticesPerTriangle) {
          // STL format uses face normals, while Unity Meshes use vertex normals. We need to convert
          // one into another by calculating a mean of vertex normals.
          var i1 = mesh.triangles[i];
          var i2 = mesh.triangles[i + 1];
          var i3 = mesh.triangles[i + 2];
          var faceNormal = (mesh.normals[i1] + mesh.normals[i2] + mesh.normals[i3]).normalized;
          writer.Write(ToXZY(faceNormal));

          writer.Write(ToXZY(mesh.vertices[i1]));
          writer.Write(ToXZY(mesh.vertices[i3]));
          writer.Write(ToXZY(mesh.vertices[i2]));

          writer.Write((short)0);
        }
        return stream.GetBuffer();
      }
    }
  }
}
}