From 7be182b6d59a444403f23ee2513928e87286f2fd Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Thu, 20 Oct 2022 23:55:09 -0400 Subject: [PATCH] Single primitive rendering with textures --- opendm/gltf.py | 230 ++++++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 117 deletions(-) diff --git a/opendm/gltf.py b/opendm/gltf.py index 007a8d3b..05fefdde 100644 --- a/opendm/gltf.py +++ b/opendm/gltf.py @@ -62,11 +62,6 @@ def load_obj(obj_path, _info=print): cv, ct = map(int, c.split("/")[0:2]) faces[current_material].append((av - 1, bv - 1, cv - 1, at - 1, bt - 1, ct - 1)) - if len(vertices) > len(uvs): - # Pad with empty UV coordinates - add_uvs = len(vertices) - len(uvs) - uvs += [[0,0]] * add_uvs - obj['vertices'] = np.array(vertices, dtype=np.float32) obj['uvs'] = np.array(uvs, dtype=np.float32) obj['normals'] = np.array(normals, dtype=np.float32) @@ -96,142 +91,143 @@ def load_mtl(mtl_file, obj_base_path, _info=print): _info("Loading %s" % map_kd_filename) with MemoryFile() as memfile: - with rasterio.open(map_kd, 'r') as r: - mats[current_mtl] = r.read() - # TODO: copy code from export-rgb (webodm) + with rasterio.open(map_kd, 'r') as src: + data = src.read() + with memfile.open(driver='JPEG', jpeg_quality=90, count=3, width=src.width, height=src.height, dtype=rasterio.dtypes.uint8) as dst: + for b in range(1, min(3, src.count) + 1): + # TODO: convert if uint16 or float + dst.write(data[b - 1], b) + memfile.seek(0) + mats[current_mtl] = memfile.read() return mats +def paddedBuffer(buf, boundary): + r = len(buf) % boundary + if r == 0: + return buf + pad = boundary - r + return buf + b'\x00' * pad def obj2glb(input_obj, output_glb, _info=print): + _info("Converting %s --> %s" % (input_obj, output_glb)) obj = load_obj(input_obj, _info=_info) - + vertices = obj['vertices'] uvs = obj['uvs'] + # Flip Y + uvs = (([0, 1] - (uvs * [0, 1])) + uvs * [1, 0]).astype(np.float32) normals = obj['normals'] - vertices_blob = vertices.tobytes() - uvs_blob = uvs.tobytes() + binary = b'' + accessors = [] + bufferViews = [] + primitives = [] + materials = [] + textures = [] + images = [] - faces = obj['faces']['material0000'] - material = obj['materials']['material0000'] - print(material.shape) - print(material.) - exit(1) - # TODO: all faces + bufOffset = 0 + def addBufferView(buf, target=None): + nonlocal bufferViews, bufOffset + bufferViews += [pygltflib.BufferView( + buffer=0, + byteOffset=bufOffset, + byteLength=len(buf), + target=target, + )] + bufOffset += len(buf) + return len(bufferViews) - 1 - faces = np.array(faces, dtype=np.uint32) + for material in obj['faces'].keys(): + faces = obj['faces'][material] + texture_blob = paddedBuffer(obj['materials'][material], 4) - indices = faces[:,0:3].flatten() - uv_indices = faces[:,3:6].flatten() + faces = np.array(faces, dtype=np.uint32) - if faces.shape[1] == 9: - normal_indices = faces[:,6:9].flatten() - else: - normal_indices = None + prim_vertices = vertices[faces[:,0:3].flatten()] + prim_uvs = uvs[faces[:,3:6].flatten()] - #faces_blob = faces.tobytes() + if faces.shape[1] == 9: + prim_normals = normals[faces[:,6:9].flatten()] + normals_blob = prim_normals.tobytes() + else: + prim_normals = None + normals_blob = None - indices_blob = indices.tobytes() - uv_indices_blob = uv_indices.tobytes() + vertices_blob = prim_vertices.tobytes() + uvs_blob = prim_uvs.tobytes() - binary = vertices_blob + uvs_blob + indices_blob + uv_indices_blob + binary += vertices_blob + uvs_blob + if normals_blob is not None: + binary += normals_blob + binary += texture_blob + + verticesBufferView = addBufferView(vertices_blob, pygltflib.ARRAY_BUFFER) + uvsBufferView = addBufferView(uvs_blob, pygltflib.ARRAY_BUFFER) + if normals_blob is not None: + normalsBufferView = addBufferView(normals_blob, pygltflib.ARRAY_BUFFER) + textureBufferView = addBufferView(texture_blob) + + accessors += [ + pygltflib.Accessor( + bufferView=verticesBufferView, + componentType=pygltflib.FLOAT, + count=len(prim_vertices), + type=pygltflib.VEC3, + max=prim_vertices.max(axis=0).tolist(), + min=prim_vertices.min(axis=0).tolist(), + ), + pygltflib.Accessor( + bufferView=uvsBufferView, + componentType=pygltflib.FLOAT, + count=len(prim_uvs), + type=pygltflib.VEC2, + max=prim_uvs.max(axis=0).tolist(), + min=prim_uvs.min(axis=0).tolist(), + ), + ] + + if prim_normals is not None: + accessors += [ + pygltflib.Accessor( + bufferView=normalsBufferView, + componentType=pygltflib.FLOAT, + count=len(prim_normals), + type=pygltflib.VEC3, + max=prim_normals.max(axis=0).tolist(), + min=prim_normals.min(axis=0).tolist(), + ) + ] + + images += [pygltflib.Image(bufferView=textureBufferView, mimeType="image/jpeg")] + textures += [pygltflib.Texture(source=len(images) - 1, sampler=0)] + materials += [pygltflib.Material(pbrMetallicRoughness=pygltflib.PbrMetallicRoughness(baseColorTexture=pygltflib.TextureInfo(index=0), metallicFactor=0, roughnessFactor=1), + alphaMode=pygltflib.MASK)] + primitives += [pygltflib.Primitive( + attributes=pygltflib.Attributes(POSITION=verticesBufferView, TEXCOORD_0=uvsBufferView), material=len(materials) - 1 + )] + if len(primitives) == 2: + break gltf = pygltflib.GLTF2( scene=0, scenes=[pygltflib.Scene(nodes=[0])], nodes=[pygltflib.Node(mesh=0)], - meshes=[ - pygltflib.Mesh( - primitives=[ - pygltflib.Primitive( - attributes=pygltflib.Attributes(POSITION=0, TEXCOORD_0=1), indices=2, material=0 - ) - ] - ) - ], - materials=[ - - ], - textures=[ - pygltflib.Texture(source=0, sampler=0) - ], - images=[ - pygltflib.Image(bufferView=0, mimeType="image/png") # TODO: use JPG - ], - accessors=[ - pygltflib.Accessor( - bufferView=0, - componentType=pygltflib.FLOAT, - count=len(vertices), - type=pygltflib.VEC3, - max=vertices.max(axis=0).tolist(), - min=vertices.min(axis=0).tolist(), - ), - pygltflib.Accessor( - bufferView=1, - componentType=pygltflib.FLOAT, - count=len(uvs), - type=pygltflib.VEC2, - max=uvs.max(axis=0).tolist(), - min=uvs.min(axis=0).tolist(), - ), - pygltflib.Accessor( - bufferView=2, - componentType=pygltflib.UNSIGNED_INT, - count=len(indices), - type=pygltflib.SCALAR, - max=[int(indices.max())], - min=[int(indices.min())], - ), - pygltflib.Accessor( - bufferView=3, - componentType=pygltflib.UNSIGNED_INT, - count=len(uv_indices), - type=pygltflib.SCALAR, - max=[int(uv_indices.max())], - min=[int(uv_indices.min())], - ), - ], - bufferViews=[ - pygltflib.BufferView( - buffer=0, - byteLength=len(vertices_blob), - target=pygltflib.ARRAY_BUFFER, - ), - pygltflib.BufferView( - buffer=0, - byteOffset=len(vertices_blob), - byteLength=len(uvs_blob), - target=pygltflib.ARRAY_BUFFER, - ), - pygltflib.BufferView( - buffer=0, - byteOffset=len(vertices_blob) + len(uvs_blob), - byteLength=len(indices_blob), - target=pygltflib.ELEMENT_ARRAY_BUFFER, - ), - pygltflib.BufferView( - buffer=0, - byteOffset=len(vertices_blob) + len(uvs_blob) + len(indices_blob), - byteLength=len(uv_indices_blob), - target=pygltflib.ELEMENT_ARRAY_BUFFER, - ), - pygltflib.BufferView( - buffer=0, - byteOffset=len(vertices_blob) + len(uvs_blob) + len(indices_blob) + len(uv_indices_blob), - byteLength=len(texture_blob), - target=pygltflib.ARRAY_BUFFER - ) - ], - buffers=[ - pygltflib.Buffer( - byteLength=len(binary) - ) - ], + meshes=[pygltflib.Mesh( + primitives=primitives + )], + materials=materials, + textures=textures, + samplers=[pygltflib.Sampler(magFilter=pygltflib.LINEAR, minFilter=pygltflib.LINEAR)], + images=images, + accessors=accessors, + bufferViews=bufferViews, + buffers=[pygltflib.Buffer(byteLength=len(binary))], ) gltf.set_binary_blob(binary) + _info("Writing...") gltf.save(output_glb) - print("OK") + _info("Wrote %s" % output_glb)