public static Rhino.Commands.Result AddTexture(Rhino.RhinoDoc doc) { // Select object to add texture const ObjectType filter = Rhino.DocObjects.ObjectType.Surface | Rhino.DocObjects.ObjectType.PolysrfFilter | Rhino.DocObjects.ObjectType.Mesh; Rhino.DocObjects.ObjRef objref; Rhino.Commands.Result rc = Rhino.Input.RhinoGet.GetOneObject("Select object to add texture", false, filter, out objref); if (rc != Rhino.Commands.Result.Success) { return(rc); } Rhino.DocObjects.RhinoObject rhino_object = objref.Object(); if (rhino_object == null) { return(Rhino.Commands.Result.Failure); } // Select texture Rhino.UI.OpenFileDialog fd = new Rhino.UI.OpenFileDialog(); fd.Filter = "Image Files (*.bmp;*.png;*.jpg)|*.bmp;*.png;*.jpg"; if (fd.ShowDialog() != System.Windows.Forms.DialogResult.OK) { return(Rhino.Commands.Result.Cancel); } // Verify texture string bitmap_filename = fd.FileName; if (string.IsNullOrEmpty(bitmap_filename) || !System.IO.File.Exists(bitmap_filename)) { return(Rhino.Commands.Result.Nothing); } // Make sure the object has it's material source set to "material_from_object" rhino_object.Attributes.MaterialSource = Rhino.DocObjects.ObjectMaterialSource.MaterialFromObject; // Make sure the object has a material assigned int material_index = rhino_object.Attributes.MaterialIndex; if (material_index < 0) { // Create a new material based on Rhino's default material material_index = doc.Materials.Add(); // Assign the new material (index) to the object. rhino_object.Attributes.MaterialIndex = material_index; } if (material_index >= 0) { Rhino.DocObjects.Material mat = doc.Materials[material_index]; mat.SetBumpTexture(bitmap_filename); mat.CommitChanges(); //Don't forget to update the object, if necessary rhino_object.CommitChanges(); doc.Views.Redraw(); return(Rhino.Commands.Result.Success); } return(Rhino.Commands.Result.Failure); }
public static void Load(string filename, RhinoDoc doc) { var model = Interface.LoadModel(filename); if (model != null) { // dictionary with deserialized buffer data var bufferData = new Dictionary <int, byte[]>(); // accessorData contains a dictionary to the buffer data meant to be accessed by an accessor via a bufferView var accessorData = new Dictionary <int, dynamic>(); // material data contains a dictionary with the material index and the Rhino material ID var materialData = new Dictionary <int, int>(); // mesh data var meshData = new Dictionary <int, List <Rhino.Geometry.Mesh> >(); var nodeXformData = new Dictionary <int, Transform>(); var dir = Path.GetDirectoryName(filename); #region Read Buffers for (int i = 0; i < model.Buffers.Length; i++) { var data = Interface.LoadBinaryBuffer(model, i, filename); bufferData.Add(i, data); } #endregion #region Process Images //go through images in model //save them to disk if necessary const string EMBEDDEDPNG = "data:image/png;base64,"; const string EMBEDDEDJPEG = "data:image/jpeg;base64,"; var imageData = new Dictionary <int, string>(); //image index, image path if (model.Images != null) { for (int i = 0; i < model.Images.Length; i++) { var image = model.Images[i]; var name = image.Name ?? "embeddedImage_" + i; var extension = string.Empty; var imageStream = Stream.Null; if (image.BufferView.HasValue) { imageStream = Interface.OpenImageFile(model, i, filename); if (image.MimeType.HasValue) { if (image.MimeType == glTFLoader.Schema.Image.MimeTypeEnum.image_jpeg) { extension = ".jpg"; } else if (image.MimeType == glTFLoader.Schema.Image.MimeTypeEnum.image_png) { extension = ".png"; } } var imgPath = Path.Combine(dir, "EmbeddedImages"); if (!Directory.Exists(imgPath)) { Directory.CreateDirectory(imgPath); } imgPath = Path.Combine(imgPath, name + extension); using (var fileStream = File.Create(imgPath)) { imageStream.Seek(0, SeekOrigin.Begin); imageStream.CopyTo(fileStream); imageData.Add(i, imgPath); } } if (image.Uri != null && image.Uri.StartsWith("data:image/")) { if (image.Uri.StartsWith(EMBEDDEDPNG)) { extension = ".png"; } if (image.Uri.StartsWith(EMBEDDEDJPEG)) { extension = ".jpg"; } imageStream = Interface.OpenImageFile(model, i, filename); var imgPath = Path.Combine(dir, "EmbeddedImages"); if (!Directory.Exists(imgPath)) { Directory.CreateDirectory(imgPath); } imgPath = Path.Combine(imgPath, name + extension); using (var fileStream = File.Create(imgPath)) { imageStream.Seek(0, SeekOrigin.Begin); imageStream.CopyTo(fileStream); imageData.Add(i, imgPath); } } if (image.Uri != null && File.Exists(Path.Combine(dir, image.Uri))) { imageData.Add(i, Path.Combine(dir, image.Uri)); } } } #endregion #region Process Materials // TODO: Update for Rhino 7 PBR Materials if (model.Materials != null) { for (int i = 0; i < model.Materials.Length; i++) { var mat = model.Materials[i]; var rhinoMat = new Rhino.DocObjects.Material(); var texId = -1; int?sourceId = null; if (mat.NormalTexture != null) { } if (mat.OcclusionTexture != null) { } if (mat.EmissiveTexture != null) { } if (mat.PbrMetallicRoughness.BaseColorTexture != null) { texId = mat.PbrMetallicRoughness.BaseColorTexture.Index; sourceId = model.Textures[texId].Source.Value; rhinoMat.SetBitmapTexture(imageData[sourceId.Value]); } if (mat.PbrMetallicRoughness.MetallicRoughnessTexture != null) { texId = mat.PbrMetallicRoughness.MetallicRoughnessTexture.Index; sourceId = model.Textures[texId].Source.Value; rhinoMat.SetBumpTexture(imageData[sourceId.Value]); } rhinoMat.Name = mat.Name; var id = doc.Materials.Add(rhinoMat); var rMat = Rhino.Render.RenderMaterial.CreateBasicMaterial(rhinoMat, doc); doc.RenderMaterials.Add(rMat); materialData.Add(i, id); } } #endregion #region Access Buffers for (int i = 0; i < model.Accessors.Length; i++) { var accessor = model.Accessors[i]; //process, afterwards, check if sparse if (accessor.BufferView != null) { var bufferView = model.BufferViews[accessor.BufferView.Value]; var buffer = bufferData[bufferView.Buffer]; //byte[] //calculate byte length var elementBytes = GetTypeMultiplier(accessor.Type) * GetComponentTypeMultiplier(accessor.ComponentType); var stride = bufferView.ByteStride != null ? bufferView.ByteStride.Value : 0; var strideDiff = stride > 0 ? stride - elementBytes : 0; var count = (elementBytes + strideDiff) * accessor.Count; var arr = new byte[count]; System.Buffer.BlockCopy(buffer, bufferView.ByteOffset + accessor.ByteOffset, arr, 0, count); var res = AccessBuffer(accessor.Type, accessor.Count, accessor.ComponentType, stride, arr); accessorData.Add(i, res); } // if accessor is sparse, need to modify the data. accessorData index is i if (accessor.Sparse != null) { //TODO //If a BufferView is specified in the accessor, sparse acts a a way to replace values //if a BufferView does not exist in the accessor, just process the sparse data //access construct data var bufferViewI = model.BufferViews[accessor.Sparse.Indices.BufferView]; var bufferI = bufferData[bufferViewI.Buffer]; //byte[] var sparseComponentType = (Accessor.ComponentTypeEnum)accessor.Sparse.Indices.ComponentType; //calculate count var elementBytesI = GetTypeMultiplier(Accessor.TypeEnum.SCALAR) * GetComponentTypeMultiplier(sparseComponentType); var strideI = bufferViewI.ByteStride != null ? bufferViewI.ByteStride.Value : 0; var strideDiffI = strideI > 0 ? strideI - elementBytesI : 0; var countI = (elementBytesI + strideDiffI) * accessor.Sparse.Count; var arrI = new byte[countI]; System.Buffer.BlockCopy(bufferI, accessor.Sparse.Indices.ByteOffset + bufferViewI.ByteOffset, arrI, 0, countI); var resIndices = AccessBuffer(Accessor.TypeEnum.SCALAR, accessor.Sparse.Count, sparseComponentType, strideI, arrI); ///////// var bufferViewV = model.BufferViews[accessor.Sparse.Values.BufferView]; var bufferV = bufferData[bufferViewV.Buffer]; //byte[] //calculate count var elementBytesV = GetTypeMultiplier(accessor.Type) * GetComponentTypeMultiplier(accessor.ComponentType); var strideV = bufferViewV.ByteStride != null ? bufferViewV.ByteStride.Value : 0; var strideDiffV = strideV > 0 ? strideV - elementBytesV : 0; var countV = (elementBytesV + strideDiffV) * accessor.Sparse.Count; var arrV = new byte[countV]; System.Buffer.BlockCopy(bufferV, accessor.Sparse.Values.ByteOffset + bufferViewV.ByteOffset, arrV, 0, countV); var resValues = AccessBuffer(accessor.Type, accessor.Sparse.Count, accessor.ComponentType, strideV, arrV); //mod accessorData var valueCnt = 0; for (int j = 0; j < accessor.Sparse.Count; j++) { var index = resIndices[j]; var mult = GetTypeMultiplier(accessor.Type); var indexAccessorData = index * mult; for (int k = 0; k < mult; k++) { accessorData[i][indexAccessorData + k] = resValues[valueCnt]; valueCnt++; } } } } #endregion #region Process Meshes //foreach (var m in model.Meshes) for (int j = 0; j < model.Meshes.Length; j++) { var m = model.Meshes[j]; var groupId = doc.Groups.Add(m.Name); var meshes = new List <Rhino.Geometry.Mesh>(); foreach (var mp in m.Primitives) { //Do I need to treat different MeshPrimitive.ModeEnum differently? Yes because if we get POINTS, LINES, LINE_LOOP, LINE_STRIP then it won't be a mesh var meshPart = new Rhino.Geometry.Mesh(); foreach (var att in mp.Attributes) { var attributeData = accessorData[att.Value]; switch (att.Key) { case "POSITION": var pts = new List <Point3d>(); for (int i = 0; i <= attributeData.Count - 3; i = i + 3) { pts.Add(new Point3d(attributeData[i], attributeData[i + 1], attributeData[i + 2])); } meshPart.Vertices.AddVertices(pts); break; case "TEXCOORD_0": var uvs = new List <Point2f>(); for (int i = 0; i <= attributeData.Count - 2; i = i + 2) { uvs.Add(new Point2f(attributeData[i], attributeData[i + 1])); } meshPart.TextureCoordinates.AddRange(uvs.ToArray()); break; case "NORMAL": var normals = new List <Vector3f>(); for (int i = 0; i <= attributeData.Count - 3; i = i + 3) { normals.Add(new Vector3f(attributeData[i], attributeData[i + 1], attributeData[i + 2])); } meshPart.Normals.AddRange(normals.ToArray()); break; case "COLOR_0": var colors = new List <Color>(); for (int i = 0; i <= attributeData.Count - 3; i = i + 3) { colors.Add(ColorFromSingle(attributeData[i], attributeData[i + 1], attributeData[i + 2])); } meshPart.VertexColors.AppendColors(colors.ToArray()); break; default: RhinoApp.WriteLine("Rhino glTF Importer: Attribute {0} not supported in Rhino. Skipping.", att.Key); /* NOT SUPPORTED IN RHINO ... yet * * - TANGENT * - TEXCOORD_1 //might be supported with multiple mapping channels? * - JOINTS_0 * - WEIGHTS_0 * */ break; } } if (mp.Indices != null) { // Indices can be defined as UNSIGNED_BYTE 5121 or UNSIGNED_SHORT 5123, maybe even as UNSIGNED_INT 5125 var faceIds = accessorData[mp.Indices.Value]; var faces = new List <MeshFace>(); for (int i = 0; i <= faceIds.Count - 3; i = i + 3) { faces.Add(new MeshFace((int)faceIds[i], (int)faceIds[i + 1], (int)faceIds[i + 2])); } meshPart.Faces.AddFaces(faces); } //meshPart.Weld(Math.PI); // TODO: CLEANUP var oa = new ObjectAttributes { MaterialSource = ObjectMaterialSource.MaterialFromObject, MaterialIndex = (mp.Material != null) ? materialData[mp.Material.Value] : 0, Name = m.Name }; //if (mp.Material != null) //oa.MaterialIndex = materialData[mp.Material.Value]; meshPart.Compact(); #if DEBUG if (!meshPart.IsValid) { //meshPart.Weld(Math.PI); //meshPart.Vertices.Align(0.0001); //meshPart.Vertices.CombineIdentical(true, true); //meshPart.Vertices.CullUnused(); //meshPart.FaceNormals.ComputeFaceNormals(); //meshPart.Normals.ComputeNormals(); if (!meshPart.IsValid) { for (int i = 0; i < meshPart.Vertices.Count; i++) { doc.Objects.AddTextDot(i.ToString(), meshPart.Vertices[i]); var ptD = new Point3d(meshPart.Vertices[i]); ptD.Transform(Transform.Translation(meshPart.Normals[i])); doc.Objects.AddLine(meshPart.Vertices[i], ptD); } foreach (var mf in meshPart.Faces) { RhinoApp.WriteLine("Rhino glTF: Mesh Face - {0}", mf); } doc.Objects.AddPoints(meshPart.Vertices.ToPoint3dArray()); } else { doc.Objects.AddMesh(meshPart, oa); } } #endif // var guid = doc.Objects.AddMesh(meshPart, oa); // doc.Groups.AddToGroup(groupId, guid); meshes.Add(meshPart); } meshData.Add(j, meshes); } #endregion #region Process Nodes Transforms for (int i = 0; i < model.Nodes.Length; i++) { nodeXformData.Add(i, ProcessNode(model.Nodes[i])); } //for (int i = 0; i < model.Nodes.Length; i++) //TraverseNode(model, model.Nodes[i], Transform.Unset, meshData); TraverseNode(model, model.Nodes[model.Scenes[model.Scene.Value].Nodes[0]], Transform.Identity, meshData); #endregion #region Add to doc for (int i = 0; i < model.Nodes.Length; i++) { var n = model.Nodes[i]; if (n.Mesh.HasValue) { int j = 0; foreach (var meshes in meshData.Values) { var group = doc.Groups.Add(n.Name); foreach (var m in meshes) { // TODO: Assign material var oa = new ObjectAttributes { Name = model.Meshes[j].Name }; var guid = doc.Objects.AddMesh(m, oa); doc.Groups.AddToGroup(group, guid); } j++; } } } #endregion } }