Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed non draco export #17

Merged
merged 3 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 38 additions & 17 deletions glTF-BinExporter/GlTFExporterCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,20 @@ protected override Result RunCommand(Rhino.RhinoDoc doc, RunMode mode)
{
GetObject go = Selection.GetValidExportObjects("Select objects to export.");

var opts = new ExportOptions() { UseDracoCompression = true, DracoCompressionLevel = 10, DracoQuantizationBits = 16, UseBinary = true };

// NOTE: The following options can be useful in dev/debug:
//bool useDracoCompression = true;
//Rhino.Input.RhinoGet.GetBool("Compression", true, "None", "Draco", ref useDracoCompression);
//bool useBinary = true;
//Rhino.Input.RhinoGet.GetBool("Mode", true, "Text", "Binary", ref useBinary);
//bool includeMaterials = true;
//Rhino.Input.RhinoGet.GetBool("Materials", true, "Exclude", "Include", ref opts.IncludeMaterial);
//int dracoCompressionLevel = 10;
Rhino.Input.RhinoGet.GetInteger("Draco Compression Level (max=10)", true, ref opts.DracoCompressionLevel, 1, 10);
//bool quantizaionBits = true;
Rhino.Input.RhinoGet.GetInteger("Quantization", true, ref opts.DracoQuantizationBits, 8, 32);
//Rhino.Input.RhinoGet.GetBool("Draco Quantization", true, "B", "Bits 16 Bits", ref quantizaionBits);
//opts.DracoQuantizationBits = quantizaionBits ? 24 : 12;

var dialog = new SaveFileDialog() { DefaultExt = ".glb", Title = "Select glTF Binary file to export to.", Filter = "glTF Binary (*.glb) | *.glb" };
var opts = new ExportOptions() { UseDracoCompression = false, DracoCompressionLevel = 10, DracoQuantizationBits = 16, UseBinary = true };

Rhino.Input.RhinoGet.GetBool("Binary or Text", true, "Text", "Binary", ref opts.UseBinary);

Rhino.Input.RhinoGet.GetBool("Compression", true, "None", "Draco", ref opts.UseDracoCompression);

if (opts.UseDracoCompression)
{
Rhino.Input.RhinoGet.GetInteger("Draco Compression Level (max=10)", true, ref opts.DracoCompressionLevel, 1, 10);
Rhino.Input.RhinoGet.GetInteger("Quantization", true, ref opts.DracoQuantizationBits, 8, 32);
}

var dialog = GetSaveFileDialog(opts.UseBinary);

var fileSelected = dialog.ShowSaveDialog();

if (!fileSelected) {
Expand Down Expand Up @@ -89,8 +86,32 @@ protected override Result RunCommand(Rhino.RhinoDoc doc, RunMode mode)
return Result.Success;
} catch (Exception e) {
RhinoApp.WriteLine("ERROR: Failed exporting selected geometry to file.");
System.Diagnostics.Debug.WriteLine(e.Message);
return Result.Failure;
}
}

private SaveFileDialog GetSaveFileDialog(bool binaryExport)
Doerge marked this conversation as resolved.
Show resolved Hide resolved
{
if(binaryExport)
{
return new SaveFileDialog()
{
DefaultExt = ".glb",
Title = "Select glTF Binary file to export to.",
Filter = "glTF Binary (*.glb) | *.glb"
};
}
else //Text glTF
{
return new SaveFileDialog()
{
DefaultExt = ".gltf",
Title = "Select glTF file to export to.",
Filter = "glTF Text (*.gltf) | *.gltf"
};
}
}

}
}
17 changes: 17 additions & 0 deletions glTF-BinExporter/glTF/GlBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class Buffer
public bool IsGLBinaryMode;

// dump to byte[] with ".flatten" + .ToArray();
[JsonIgnore]
jrz371 marked this conversation as resolved.
Show resolved Hide resolved
public List<IEnumerable<byte>> RawBytes;

public bool ShouldSerializeRawBytes()
Expand Down Expand Up @@ -75,6 +76,22 @@ public void Add(Point3d point)
PrimitiveCount += 1;
}

public void Add(Vector3d point)
{
// Switch GL coords for Y<=>Z
float[] coords = new float[] { (float)point.X, (float)point.Z, -(float)point.Y };
Add(coords);
PrimitiveCount += 1;
}

public void Add(Point2f point)
jrz371 marked this conversation as resolved.
Show resolved Hide resolved
{
// Switch GL coords for Y<=>Z
float[] coords = new float[] { (float)point.X, -(float)point.Y };
Add(coords);
PrimitiveCount += 1;
}

public void Add(MeshFace face)
{
if (face.IsTriangle)
Expand Down
250 changes: 169 additions & 81 deletions glTF-BinExporter/glTF/GlTFRootModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace glTF_BinExporter.glTF
/// </summary>
public class RootModel
{
public int scene;
public int scene = 0;

public List<Scene> scenes;

public List<Node> nodes;
Expand Down Expand Up @@ -145,6 +146,9 @@ public void SerializeToGLB(in MemoryStream memoryStream)
bv.byteOffset = bv.bufferRef.binaryOffset;
}

//Have to get rid of the other buffers so only the new buffer
//all the data was dumped into remains
buffers.Clear();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Man... how did that even work before? 👍 Thanks!

While reviewing this I realize I may have misunderstood the spec initially: I now think it is valid to create multiple binary buffers, instead of mashing everything into one and using views to discern the data blobs. Not sure what the implications are of that yet. The validator fails with:

           {
                "code": "ACCESSOR_TOTAL_OFFSET_ALIGNMENT",
                "message": "Accessor's total byteOffset 397965 isn't a multiple of componentType length 4.",
                "severity": 0,
                "pointer": "/accessors/0"
            },

Maybe that's a side effect of this weird choice. Will create a separate ticket for that though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think this changed. While we probably can cram multiple things into a single buffer it's unnecessarily complicated and makes the code and final output file confusing. The validator error makes sense to me since we're probably mixing floats/bytes/integers all in one buffer and just fiddling with offsets. It's not super imperative though so we'll get to #22 when either of us has a chance.

buffers.Add(new Buffer(false) { byteLength = binaryChunkUnpaddedLength, uri = null });

// JSON Chunk
Expand Down Expand Up @@ -518,90 +522,174 @@ public void AddMaterial(Rhino.DocObjects.Material rhinoMaterial, Guid renderMatI

public void AddRhinoObject(Rhino.Geometry.Mesh[] rhinoMeshes, Rhino.DocObjects.Material material, Guid renderMatId)
{
// TODO: Disabled for now. Don't have bandwidth to fix none-DracoCompression for now. Beware of lot's of "old" code below.
//int currentMaterialIdx = materials.FindIndex(m => m.Id == material.Id);
//if (currentMaterialIdx == -1)
//{
// AddMaterial(material);
// currentMaterialIdx = materials.Count - 1;
//}
int currentMaterialIdx = materials.FindIndex(m => m.Id == material.Id);
if (currentMaterialIdx == -1)
{
AddMaterial(material, renderMatId);
currentMaterialIdx = materials.Count - 1;
}

//var primitives = new List<Primitive>();
//// For each rhino mesh, create gl-buffers, gl-meshes, etc.
//foreach (var rhinoMesh in rhinoMeshes)
//{
// // Create buffers for data (position, normals, indices etc.)
// var vtxBuffer = new Buffer(IsGLBinaryMode);
var primitives = new List<Primitive>();

// var min = new Point3d() { X = Double.PositiveInfinity, Y = Double.PositiveInfinity, Z = Double.PositiveInfinity };
// var max = new Point3d() { X = Double.NegativeInfinity, Y = Double.NegativeInfinity, Z = Double.NegativeInfinity };
// foreach (var p in rhinoMesh.Vertices)
// {
// vtxBuffer.Add(p);

// min.X = Math.Min(min.X, p.X);
// // Switch Y<=>Z for GL coords
// min.Y = Math.Min(min.Y, p.Z);
// min.Z = Math.Min(min.Z, -p.Y);

// max.X = Math.Max(max.X, p.X);
// // Switch Y<=>Z for GL coords
// max.Y = Math.Max(max.Y, p.Z);
// max.Z = Math.Max(max.Z, -p.Y);
// }
// buffers.Add(vtxBuffer);
// int vtxBufferIdx = buffers.Count - 1;

// var idsBuffer = new Buffer(IsGLBinaryMode);
// foreach (var f in rhinoMesh.Faces)
// {
// idsBuffer.Add(f);
// }
// buffers.Add(idsBuffer);
// int idsBufferIdx = buffers.Count - 1;

// // Create bufferviews
// var vtxBufferView = new BufferView() { bufferRef = vtxBuffer, buffer = vtxBufferIdx, byteOffset = 0, byteLength = vtxBuffer.byteLength, target = GlConstants.ARRAY_BUFFER };
// bufferViews.Add(vtxBufferView);
// int vtxBufferViewIdx = bufferViews.Count - 1;

// var idsBufferView = new BufferView() { bufferRef = idsBuffer, buffer = idsBufferIdx, byteOffset = 0, byteLength = idsBuffer.byteLength, target = GlConstants.ELEMENT_ARRAY_BUFFER };
// bufferViews.Add(idsBufferView);
// int idsBufferViewIdx = bufferViews.Count - 1;

// // Create accessors
// var vtxAccessor = new AccessorVec3
// {
// bufferView = vtxBufferViewIdx,
// count = vtxBuffer.PrimitiveCount,
// min = new float[] { (float)min.X, (float)min.Y, (float)min.Z },
// max = new float[] { (float)max.X, (float)max.Y, (float)max.Z }
// };
// accessors.Add(vtxAccessor);
// int vtxAccessorIdx = accessors.Count - 1;

// var idsAccessor = new AccessorScalar
// {
// bufferView = idsBufferViewIdx,
// count = idsBuffer.PrimitiveCount
// };
// accessors.Add(idsAccessor);
// int idsAccessorIdx = accessors.Count - 1;

// // Create primitives
// var attribute = new Attribute() { POSITION = vtxAccessorIdx };
// var primitive = new Primitive() { attributes = attribute, indices = idsAccessorIdx, material = currentMaterialIdx };

// // Create mesh
// primitives.Add(primitive);
//}
//var mesh = new Mesh() { primitives = primitives };
//meshes.Add(mesh);
foreach (var rhinoMesh in rhinoMeshes)
{
// Create buffers for data (position, normals, indices etc.)
var vtxBuffer = new Buffer(ExportOptions.UseBinary);
var vtxMin = new Point3d() { X = Double.PositiveInfinity, Y = Double.PositiveInfinity, Z = Double.PositiveInfinity };
var vtxMax = new Point3d() { X = Double.NegativeInfinity, Y = Double.NegativeInfinity, Z = Double.NegativeInfinity };
foreach (var p in rhinoMesh.Vertices)
{
vtxBuffer.Add(p);

//var node = new Node() { mesh = meshes.Count - 1 };
//nodes.Add(node);
vtxMin.X = Math.Min(vtxMin.X, p.X);
// Switch Y<=>Z for GL coords
vtxMin.Y = Math.Min(vtxMin.Y, p.Z);
vtxMin.Z = Math.Min(vtxMin.Z, -p.Y);

//scenes[scene].nodes.Add(nodes.Count - 1);
vtxMax.X = Math.Max(vtxMax.X, p.X);
// Switch Y<=>Z for GL coords
vtxMax.Y = Math.Max(vtxMax.Y, p.Z);
vtxMax.Z = Math.Max(vtxMax.Z, -p.Y);
}
buffers.Add(vtxBuffer);
int vtxBufferIdx = buffers.Count - 1;

var idsBuffer = new Buffer(ExportOptions.UseBinary);
foreach (var f in rhinoMesh.Faces)
{
idsBuffer.Add(f);
}
buffers.Add(idsBuffer);
int idsBufferIdx = buffers.Count - 1;

Buffer normalsBuffer = new Buffer(ExportOptions.UseBinary);
var normalsMin = new Point3d() { X = Double.PositiveInfinity, Y = Double.PositiveInfinity, Z = Double.PositiveInfinity };
var normalsMax = new Point3d() { X = Double.NegativeInfinity, Y = Double.NegativeInfinity, Z = Double.NegativeInfinity };
//normalsBuffer.Add(rhinoMesh.Normals.ToFloatArray());
foreach (var n in rhinoMesh.Normals)
{
normalsBuffer.Add(n);

normalsMin.X = Math.Min(normalsMin.X, n.X);
// Switch Y<=>Z for GL coords
normalsMin.Y = Math.Min(normalsMin.Y, n.Z);
normalsMin.Z = Math.Min(normalsMin.Z, -n.Y);

normalsMax.X = Math.Max(normalsMax.X, n.X);
// Switch Y<=>Z for GL coords
normalsMax.Y = Math.Max(normalsMax.Y, n.Z);
normalsMax.Z = Math.Max(normalsMax.Z, -n.Y);
}
int normalsIdx = buffers.AddAndReturnIndex(normalsBuffer);

Buffer texCoordsBuffer = new Buffer(ExportOptions.UseBinary);
var texCoordsMin = new Point2d() { X = Double.PositiveInfinity, Y = Double.PositiveInfinity };
var texCoordsMax = new Point2d() { X = Double.NegativeInfinity, Y = Double.NegativeInfinity };
foreach (var tx in rhinoMesh.TextureCoordinates)
{
texCoordsBuffer.Add(tx);

texCoordsMin.X = Math.Min(texCoordsMin.X, tx.X);
// Switch Y<=>Z for GL coords
texCoordsMin.Y = Math.Min(texCoordsMin.Y, -tx.Y);

texCoordsMax.X = Math.Max(texCoordsMax.X, tx.X);
// Switch Y<=>Z for GL coords
texCoordsMax.Y = Math.Max(texCoordsMax.Y, -tx.Y);
}

int texCoordsIdx = buffers.AddAndReturnIndex(texCoordsBuffer);

// Create bufferviews
var vtxBufferView = new BufferView() { bufferRef = vtxBuffer, buffer = vtxBufferIdx, byteOffset = 0, byteLength = vtxBuffer.byteLength, target = GLConstants.ARRAY_BUFFER };
bufferViews.Add(vtxBufferView);
int vtxBufferViewIdx = bufferViews.Count - 1;

var idsBufferView = new BufferView() { bufferRef = idsBuffer, buffer = idsBufferIdx, byteOffset = 0, byteLength = idsBuffer.byteLength, target = GLConstants.ELEMENT_ARRAY_BUFFER };
bufferViews.Add(idsBufferView);
int idsBufferViewIdx = bufferViews.Count - 1;

BufferView normalsBufferView = new BufferView()
{
bufferRef = normalsBuffer,
buffer = normalsIdx,
byteOffset = 0,
byteLength = normalsBuffer.byteLength,
target = GLConstants.ARRAY_BUFFER
};
int normalsBufferViewIdx = bufferViews.AddAndReturnIndex(normalsBufferView);

BufferView texCoordsBufferView = new BufferView()
{
bufferRef = texCoordsBuffer,
buffer = texCoordsIdx,
byteOffset = 0,
byteLength = texCoordsBuffer.byteLength,
target = GLConstants.ARRAY_BUFFER
};
int texCoordsBufferViewIdx = bufferViews.AddAndReturnIndex(texCoordsBufferView);

// Create accessors
var vtxAccessor = new AccessorVec3()
{
bufferView = vtxBufferViewIdx,
count = vtxBuffer.PrimitiveCount,
min = new float[] { (float)vtxMin.X, (float)vtxMin.Y, (float)vtxMin.Z },
max = new float[] { (float)vtxMax.X, (float)vtxMax.Y, (float)vtxMax.Z }
};

accessors.Add(vtxAccessor);
int vtxAccessorIdx = accessors.Count - 1;

var idsAccessor = new AccessorScalar()
{
min = new int[] { 0 },
max = new int[] { rhinoMesh.Vertices.Count - 1 },
bufferView = idsBufferViewIdx,
count = idsBuffer.PrimitiveCount
};
accessors.Add(idsAccessor);
int idsAccessorIdx = accessors.Count - 1;

AccessorVec3 normalsAccessor = new AccessorVec3()
{
bufferView = normalsBufferViewIdx,
count = rhinoMesh.Normals.Count,
min = new float[] { (float)normalsMin.X, (float)normalsMin.Y, (float)normalsMin.Z },
max = new float[] { (float)normalsMax.X, (float)normalsMax.Y, (float)normalsMax.Z }
};
int normalsAccessorIdx = accessors.AddAndReturnIndex(normalsAccessor);

AccessorVec2 texCoordsAccessor = new AccessorVec2()
{
bufferView = texCoordsBufferViewIdx,
count = rhinoMesh.TextureCoordinates.Count,
min = new float[] { 0.0f, 0.0f },
max = new float[] { 1.0f, 1.0f },
};
int texCoordsAccessorIdx = accessors.AddAndReturnIndex(texCoordsAccessor);

// Create primitives
var attribute = new Attribute()
{
POSITION = vtxAccessorIdx,
NORMAL = normalsAccessorIdx,
TEXCOORD_0 = texCoordsAccessorIdx
};

var primitive = new Primitive() { attributes = attribute, indices = idsAccessorIdx, material = currentMaterialIdx };

// Create mesh
primitives.Add(primitive);
}

var mesh = new Mesh() { primitives = primitives };
meshes.Add(mesh);

var node = new Node() { mesh = meshes.Count - 1 };
nodes.Add(node);

scenes[scene].nodes.Add(nodes.Count - 1);
}
}
}
7 changes: 7 additions & 0 deletions glTF-BinExporter/glTF/GlTFUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,12 @@ public static void ExportBinary(MemoryStream outStream, IEnumerable<RhinoObject>

outStream.Flush();
}

public static int AddAndReturnIndex<T>(this List<T> list, T item)
Doerge marked this conversation as resolved.
Show resolved Hide resolved
{
list.Add(item);
return list.Count - 1;
}

}
}