def export_sign_2(gltf: GLTF, name: str, sign: Sign) -> int: texture = sign.get_name_texture() # x = -0.2 CM = 0.01 PAPER_WIDTH, PAPER_HEIGHT = 8.5 * CM, 15.5 * CM # PAPER_THICK = 0.01 # BASE_SIGN = 5 * CM # WIDTH_SIGN = 1.1 * CM # # y = -1.5 * PAPER_HEIGHT # XXX not sure why this is negative # y = BASE_SIGN # x = -PAPER_WIDTH / 2 fn = get_texture_file(texture)[0] material_index = make_material(gltf, doubleSided=False, baseColorFactor=[1, 1, 1, 1.0], fn=fn) mi = get_square() mesh_index = add_polygon( gltf, name + "-mesh", vertices=mi.vertices, texture=mi.textures, colors=mi.color, normals=mi.normals, material=material_index, ) sign_node = Node(mesh=mesh_index) sign_node_index = add_node(gltf, sign_node) fn = get_texture_file("wood")[0] material_index_white = make_material(gltf, doubleSided=False, baseColorFactor=[0.5, 0.5, 0.5, 1], fn=fn) back_mesh_index = add_polygon( gltf, name + "-mesh", vertices=mi.vertices, texture=mi.textures, colors=mi.color, normals=mi.normals, material=material_index_white, ) back_rot = SE3_from_rotation_translation(roty(np.pi), np.array([0, 0, 0])) back_node = Node(matrix=gm(back_rot), mesh=back_mesh_index) back_node_index = add_node(gltf, back_node) scale = np.diag([PAPER_WIDTH, PAPER_HEIGHT, PAPER_WIDTH, 1]) rot = SE3_from_rotation_translation( rotz(np.pi / 2) @ rotx(np.pi / 2), # @ rotz(np.pi), np.array([0, 0, 0.8 * PAPER_HEIGHT]), ) M = rot @ scale node = Node(matrix=gm(M), children=[sign_node_index, back_node_index]) return add_node(gltf, node)
def export_DB18(gltf: GLTF, name, ob: DB18) -> int: _ = get_resource_path("duckiebot3/main.gltf") g2 = GLTF.load(_) # color = [0, 1, 0, 1] color = get_duckiebot_color_from_colorname(ob.color) set_duckiebot_color(g2, "gkmodel0_chassis_geom0_mat_001-material.001", color) set_duckiebot_color(g2, "gkmodel0_chassis_geom0_mat_001-material", color) index0 = embed(gltf, g2) radius = 0.0318 M = SE3_from_R3(np.array([0, 0, radius])) node = Node(children=[index0], matrix=gm(M)) index = add_node(gltf, node) return index
def export_sign(gltf: GLTF, name, ob: Sign): _ = get_resource_path("sign_generic/main.gltf") tname = ob.get_name_texture() logger.info(f"t = {type(ob)} tname = {tname}") fn_texture = get_texture_file(tname)[0] uri = os.path.join("textures/signs", os.path.basename(fn_texture)) data = read_bytes_from_file(fn_texture) def transform(g: GLTF) -> GLTF: rf = FileResource(uri, data=data) g.resources.append(rf) g.model.images[0] = Image(name=tname, uri=uri) return g index = embed_external(gltf, _, key=tname, transform=transform) # M = SE3_roty(np.pi/5) M = SE3_rotz(-np.pi / 2) n2 = Node(matrix=gm(M), children=[index]) return add_node(gltf, n2)
def _convert_nodes( rsm_version: int, nodes: List[AbstractNode], tex_id_by_node: List[List[int]], gltf_model: GLTFModel) -> Tuple[List[FileResource], List[int]]: root_nodes = [] model_bbox = calculate_model_bounding_box(rsm_version, nodes) for node in nodes: if node.parent is None: _compute_transform_matrices(rsm_version, node, len(nodes) == 0, model_bbox) nodes_children: Dict[str, List[int]] = {} vertex_bytearray = bytearray() tex_vertex_bytearray = bytearray() byteoffset = 0 tex_byteoffset = 0 for node_id, node in enumerate(nodes): rsm_node = node.impl node_name = decode_string(rsm_node.name) if node.parent is not None: parent_name = decode_string(node.parent.impl.name) if parent_name in nodes_children: nodes_children[parent_name] += [node_id] else: nodes_children[parent_name] = [node_id] if rsm_version >= 0x203: node_tex_ids = tex_id_by_node[node_id] else: node_tex_ids = rsm_node.texture_ids vertices_by_texture = _sort_vertices_by_texture(rsm_node, node_tex_ids) gltf_primitives = [] for tex_id, vertices in vertices_by_texture.items(): # Model vertices bytelen = _serialize_vertices(vertices[0], vertex_bytearray) # Texture vertices tex_bytelen = _serialize_vertices(vertices[1], tex_vertex_bytearray) (mins, maxs) = _calculate_vertices_bounds(vertices[0]) (tex_mins, tex_maxs) = _calculate_vertices_bounds(vertices[1]) gltf_model.bufferViews.append( BufferView( buffer=0, # Vertices byteOffset=byteoffset, byteLength=bytelen, target=BufferTarget.ARRAY_BUFFER.value)) gltf_model.bufferViews.append( BufferView( buffer=1, # Texture vertices byteOffset=tex_byteoffset, byteLength=tex_bytelen, target=BufferTarget.ARRAY_BUFFER.value)) buffer_view_id = len(gltf_model.accessors) gltf_model.accessors.append( Accessor(bufferView=buffer_view_id, byteOffset=0, componentType=ComponentType.FLOAT.value, count=len(vertices[0]), type=AccessorType.VEC3.value, min=mins, max=maxs)) gltf_model.accessors.append( Accessor(bufferView=buffer_view_id + 1, byteOffset=0, componentType=ComponentType.FLOAT.value, count=len(vertices[1]), type=AccessorType.VEC2.value, min=tex_mins, max=tex_maxs)) gltf_primitives.append( Primitive(attributes=Attributes(POSITION=buffer_view_id + 0, TEXCOORD_0=buffer_view_id + 1), material=tex_id)) byteoffset += bytelen tex_byteoffset += tex_bytelen gltf_model.meshes.append(Mesh(primitives=gltf_primitives)) # Decompose matrix to TRS translation, rotation, scale = decompose_matrix( node.gltf_transform_matrix) gltf_model.nodes.append( Node(name=node_name, mesh=node_id, translation=translation.to_list() if translation else None, rotation=rotation.to_list() if rotation else None, scale=scale.to_list() if scale else None)) if node.parent is None: root_nodes.append(node_id) # Register vertex buffers vtx_file_name = 'vertices.bin' tex_vtx_file_name = 'tex_vertices.bin' gltf_resources = [ FileResource(vtx_file_name, data=vertex_bytearray), FileResource(tex_vtx_file_name, data=tex_vertex_bytearray) ] gltf_model.buffers = [ Buffer(byteLength=byteoffset, uri=vtx_file_name), Buffer(byteLength=tex_byteoffset, uri=tex_vtx_file_name) ] # Update nodes' children # Note(LinkZ): Consume children with `pop` to avoid issues with models # containing multiple nodes with the same name for gltf_node in gltf_model.nodes: gltf_node.children = nodes_children.pop(gltf_node.name, None) return gltf_resources, root_nodes
def embed(gltf: GLTF, g2: GLTF) -> int: lenn = lambda x: 0 if x is None else len(x) model = gltf.model off_accessors = lenn(model.accessors) off_animations = lenn(model.animations) off_buffers = lenn(model.buffers) off_bufferViews = lenn(model.bufferViews) off_cameras = lenn(model.cameras) off_images = lenn(model.images) off_materials = lenn(model.materials) off_meshes = lenn(model.meshes) off_nodes = lenn(model.nodes) off_samplers = lenn(model.samplers) off_scenes = lenn(model.scenes) off_skins = lenn(model.skins) off_textures = len(model.textures) model2 = g2.model def add_if_not_none(a, b): if a is None: return None else: return a + b # b: Buffer if model2.buffers: for b in model2.buffers: model.buffers.append(b) if model2.bufferViews: for bv in model2.bufferViews: buffer = add_if_not_none(bv.buffer, off_buffers) bv2 = replace(bv, buffer=buffer) model.bufferViews.append(bv2) if model2.samplers: for s in model2.samplers: model.samplers.append(s) if model2.cameras: for c in model2.cameras: model.cameras.append(c) if model2.images: for i in model2.images: i2 = replace(i, bufferView=add_if_not_none(i.bufferView, off_bufferViews)) model.images.append(i2) def convert_texture_info(t: Optional[TextureInfo]) -> Optional[TextureInfo]: if t is None: return None index = add_if_not_none(t.index, off_textures) return replace(t, index=index) def convert_metallic_r(t: Optional[PBRMetallicRoughness]) -> Optional[PBRMetallicRoughness]: if t is None: return None baseColorTexture = convert_texture_info(t.baseColorTexture) metallicRoughnessTexture = convert_texture_info(t.metallicRoughnessTexture) return replace( t, baseColorTexture=baseColorTexture, metallicRoughnessTexture=metallicRoughnessTexture ) if model2.materials: m: Material for m in model2.materials: pbrMetallicRoughness = convert_metallic_r(m.pbrMetallicRoughness) normalTexture = convert_texture_info(m.normalTexture) occlusionTexture = convert_texture_info(m.occlusionTexture) emissiveTexture = convert_texture_info(m.emissiveTexture) m2 = replace( m, normalTexture=normalTexture, occlusionTexture=occlusionTexture, emissiveTexture=emissiveTexture, pbrMetallicRoughness=pbrMetallicRoughness, ) model.materials.append(m2) def convert_node(n1: Node) -> Node: camera2 = add_if_not_none(n1.camera, off_cameras) mesh2 = add_if_not_none(n1.mesh, off_meshes) if n1.children is None: children2 = None else: children2 = [_ + off_nodes for _ in n1.children] return replace(n1, camera=camera2, mesh=mesh2, children=children2) if model2.nodes: n_: Node for n_ in model2.nodes: n2 = convert_node(n_) model.nodes.append(n2) def replace_attributes(a: Attributes) -> Attributes: # class Attributes: # """ # Helper type for describing the attributes of a primitive. Each property corresponds to mesh # attribute semantic and # each value is the index of the accessor containing the attribute's data. # """ return Attributes( POSITION=add_if_not_none(a.POSITION, off_accessors), NORMAL=add_if_not_none(a.NORMAL, off_accessors), TANGENT=add_if_not_none(a.TANGENT, off_accessors), TEXCOORD_0=add_if_not_none(a.TEXCOORD_0, off_accessors), TEXCOORD_1=add_if_not_none(a.TEXCOORD_1, off_accessors), COLOR_0=add_if_not_none(a.COLOR_0, off_accessors), JOINTS_0=add_if_not_none(a.JOINTS_0, off_accessors), WEIGHTS_0=add_if_not_none(a.WEIGHTS_0, off_accessors), ) def replace_primitives(p: Primitive) -> Primitive: # TODO: targets attributes = replace_attributes(p.attributes) indices = add_if_not_none(p.indices, off_accessors) material = add_if_not_none(p.material, off_materials) return replace(p, attributes=attributes, indices=indices, material=material) if model2.meshes: mesh: Mesh for mesh in model2.meshes: primitives = [replace_primitives(_) for _ in mesh.primitives] mesh = replace(mesh, primitives=primitives) model.meshes.append(mesh) if model2.textures: tex: Texture for tex in model2.textures: sampler = add_if_not_none(tex.sampler, off_samplers) source = add_if_not_none(tex.source, off_images) tex2 = replace(tex, sampler=sampler, source=source) model.textures.append(tex2) if model2.accessors: a: Accessor for a in model2.accessors: bufferView = add_if_not_none(a.bufferView, off_bufferViews) a2 = replace(a, bufferView=bufferView) model.accessors.append(a2) assert len(model2.scenes) == 1, model2 # scene: Scene # for scene in model2.scenes: # nodes = [add_if_not_none(_, off_nodes) for _ in scene.nodes] # scene2 = replace(scene, nodes=nodes) # model.scenes.append(scene2) scene0 = model2.scenes[0] scene_nodes = [add_if_not_none(_, off_nodes) for _ in scene0.nodes] node = Node(children=scene_nodes) node_index = add_node(gltf, node) # logger.debug(f"the main scene node for imported is {node_index}") # model.scenes[0].nodes.extend(nodes) gltf.resources.extend(g2.resources) check_loops(gltf) return node_index
def export_gltf(dm: DuckietownMap, outdir: str, background: bool = True): gltf = GLTF() # setattr(gltf, 'md52PV', {}) scene_index = add_scene(gltf, Scene(nodes=[])) map_nodes = [] it: IterateByTestResult tiles = list(iterate_by_class(dm, Tile)) if not tiles: raise ZValueError("no tiles?") for it in tiles: tile = cast(Tile, it.object) name = it.fqn[-1] fn = tile.fn fn_normal = tile.fn_normal fn_emissive = tile.fn_emissive fn_metallic_roughness = tile.fn_metallic_roughness fn_occlusion = tile.fn_occlusion material_index = make_material( gltf, doubleSided=False, baseColorFactor=[1, 1, 1, 1.0], fn=fn, fn_normals=fn_normal, fn_emissive=fn_emissive, fn_metallic_roughness=fn_metallic_roughness, fn_occlusion=fn_occlusion, ) mi = get_square() mesh_index = add_polygon( gltf, name + "-mesh", vertices=mi.vertices, texture=mi.textures, colors=mi.color, normals=mi.normals, material=material_index, ) node1_index = add_node(gltf, Node(mesh=mesh_index)) i, j = ij_from_tilename(name) c = (i + j) % 2 color = [1, 0, 0, 1.0] if c else [0, 1, 0, 1.0] add_back = False if add_back: material_back = make_material(gltf, doubleSided=False, baseColorFactor=color) back_mesh_index = add_polygon( gltf, name + "-mesh", vertices=mi.vertices, texture=mi.textures, colors=mi.color, normals=mi.normals, material=material_back, ) flip = np.diag([1.0, 1.0, -1.0, 1.0]) flip[2, 3] = -0.01 back_index = add_node(gltf, Node(mesh=back_mesh_index, matrix=gm(flip))) else: back_index = None tile_transform = it.transform_sequence tile_matrix2d = tile_transform.asmatrix2d().m s = dm.tile_size scale = np.diag([s, s, s, 1]) tile_matrix = SE3_from_SE2(tile_matrix2d) tile_matrix = tile_matrix @ scale @ SE3_rotz(-np.pi / 2) tile_matrix_float = list(tile_matrix.T.flatten()) if back_index is not None: children = [node1_index, back_index] else: children = [node1_index] tile_node = Node(name=name, matrix=tile_matrix_float, children=children) tile_node_index = add_node(gltf, tile_node) map_nodes.append(tile_node_index) if background: bg_index = add_background(gltf) add_node_to_scene(gltf, scene_index, bg_index) exports = { "Sign": export_sign, # XXX: the tree model is crewed up "Tree": export_tree, # "Tree": None, "Duckie": export_duckie, "DB18": export_DB18, # "Bus": export_bus, # "Truck": export_truck, # "House": export_house, "TileMap": None, "TrafficLight": export_trafficlight, "LaneSegment": None, "PlacedObject": None, "DuckietownMap": None, "Tile": None, } for it in iterate_by_class(dm, PlacedObject): ob = it.object K = type(ob).__name__ if isinstance(ob, Sign): K = "Sign" if K not in exports: logger.warn(f"cannot convert {type(ob).__name__}") continue f = exports[K] if f is None: continue thing_index = f(gltf, it.fqn[-1], ob) tile_transform = it.transform_sequence tile_matrix2d = tile_transform.asmatrix2d().m tile_matrix = SE3_from_SE2(tile_matrix2d) @ SE3_rotz(np.pi / 2) sign_node_index = add_node(gltf, Node(children=[thing_index])) tile_matrix_float = list(tile_matrix.T.flatten()) tile_node = Node(name=it.fqn[-1], matrix=tile_matrix_float, children=[sign_node_index]) tile_node_index = add_node(gltf, tile_node) map_nodes.append(tile_node_index) mapnode = Node(name="tiles", children=map_nodes) map_index = add_node(gltf, mapnode) add_node_to_scene(gltf, scene_index, map_index) # add_node_to_scene(model, scene_index, node1_index) yfov = np.deg2rad(60) camera = Camera( name="perpcamera", type="perspective", perspective=PerspectiveCameraInfo(aspectRatio=4 / 3, yfov=yfov, znear=0.01, zfar=1000), ) gltf.model.cameras.append(camera) t = np.array([2, 2, 0.15]) matrix = look_at(pos=t, target=np.array([0, 2, 0])) cam_index = add_node(gltf, Node(name="cameranode", camera=0, matrix=list(matrix.T.flatten()))) add_node_to_scene(gltf, scene_index, cam_index) cleanup_model(gltf) fn = os.path.join(outdir, "main.gltf") make_sure_dir_exists(fn) logger.info(f"writing to {fn}") gltf.export(fn) if True: data = read_ustring_from_utf8_file(fn) j = json.loads(data) data2 = json.dumps(j, indent=2) write_ustring_to_utf8_file(data2, fn) fnb = os.path.join(outdir, "main.glb") logger.info(f"writing to {fnb}") gltf.export(fnb) verify_trimesh = False if verify_trimesh: res = trimesh.load(fn) # camera_pose, _ = res.graph['cameranode'] # logger.info(res=res) import pyrender scene = pyrender.Scene.from_trimesh_scene(res)
def export_gltf(icon, filename, metadata=None): basename = PurePath(filename).stem vertex_info_format = ("3f" * icon.animation_shapes) + "3f 2f 3f" float_size = struct.calcsize("f") animation_speed = 0.1 animation_present = icon.animation_shapes > 1 model_data = bytearray() mins = {} maxs = {} for i, vertex in enumerate(icon.vertices): for j, position in enumerate(vertex.positions): if j == 0: values_basis = [ position.x / 4096, -position.y / 4096, -position.z / 4096 ] values = values_basis else: # Subtract basis position to compensate for shape keys being relative to basis values = [ position.x / 4096 - values_basis[0], -position.y / 4096 - values_basis[1], -position.z / 4096 - values_basis[2] ] if j not in mins: mins[j] = values.copy() else: if values[0] < mins[j][0]: mins[j][0] = values[0] if values[1] < mins[j][1]: mins[j][1] = values[1] if values[2] < mins[j][2]: mins[j][2] = values[2] if j not in maxs: maxs[j] = values.copy() else: if values[0] > maxs[j][0]: maxs[j][0] = values[0] if values[1] > maxs[j][1]: maxs[j][1] = values[1] if values[2] > maxs[j][2]: maxs[j][2] = values[2] model_data.extend(struct.pack("3f", *values)) model_data.extend( struct.pack("3f 2f 3f", vertex.normal.x / 4096, -vertex.normal.y / 4096, -vertex.normal.z / 4096, 1.0 - (vertex.tex_coord.u / 4096), 1.0 - (vertex.tex_coord.v / 4096), vertex.color.r / 255, vertex.color.g / 255, vertex.color.b / 255)) # Generate animation data if multiple animation shapes are present if animation_present: animation_offset = len(model_data) for i in range(icon.frame_count + 1): model_data.extend(struct.pack("f", i * animation_speed)) for i, frame in enumerate(icon.frames + [icon.frames[0]]): segment = [struct.pack("f", 0.0)] * (icon.animation_shapes - 1) if frame.shape_id != 0: segment[frame.shape_id - 1] = struct.pack("f", 1.0) for item in segment: model_data.extend(item) animation_length = len(model_data) - animation_offset # Generate texture if isinstance(icon.texture, Ps2ico.CompressedTexture): image_data = convert_compressed_texture_data(icon.texture.size, icon.texture.data) elif isinstance(icon.texture, Ps2ico.UncompressedTexture): image_data = convert_uncompressed_texture_data(icon.texture.data) with BytesIO() as png: PILImage.frombytes("RGB", (128, 128), image_data).save(png, "png") texture_data = png.getvalue() # Basic glTF info model = GLTFModel() model.asset = Asset(version="2.0", generator=f"ico2gltf v{VERSION}") model.scenes = [Scene(nodes=[0])] model.scene = 0 model.nodes = [Node(mesh=0)] # If present, embed metadata if metadata: # Normalize title: turn japanese full-width characters into normal ones and insert the line break title = unicodedata.normalize("NFKC", metadata.title).rstrip("\x00") title = title[:metadata.offset_2nd_line // 2] + "\n" + title[metadata.offset_2nd_line // 2:] model.extras = { "title": title, "background_opacity": metadata.bg_opacity / 0x80, "background_bottom_left_color": [ metadata.bg_color_lowerleft.r / 0x80, metadata.bg_color_lowerleft.g / 0x80, metadata.bg_color_lowerleft.b / 0x80, metadata.bg_color_lowerleft.a / 0x80 ], "background_bottom_right_color": [ metadata.bg_color_lowerright.r / 0x80, metadata.bg_color_lowerright.g / 0x80, metadata.bg_color_lowerright.b / 0x80, metadata.bg_color_lowerright.a / 0x80 ], "background_top_left_color": [ metadata.bg_color_upperleft.r / 0x80, metadata.bg_color_upperleft.g / 0x80, metadata.bg_color_upperleft.b / 0x80, metadata.bg_color_upperleft.a / 0x80 ], "background_top_right_color": [ metadata.bg_color_upperright.r / 0x80, metadata.bg_color_upperright.g / 0x80, metadata.bg_color_upperright.b / 0x80, metadata.bg_color_lowerright.a / 0x80 ], "ambient_color": [ metadata.light_ambient_color.r, metadata.light_ambient_color.g, metadata.light_ambient_color.b ], "light1_direction": [ metadata.light1_direction.x, metadata.light1_direction.y, metadata.light1_direction.z ], "light1_color": [ metadata.light1_color.r, metadata.light1_color.g, metadata.light1_color.b, metadata.light1_color.a ], "light2_direction": [ metadata.light2_direction.x, metadata.light2_direction.y, metadata.light2_direction.z ], "light2_color": [ metadata.light2_color.r, metadata.light2_color.g, metadata.light2_color.b, metadata.light2_color.a ], "light3_direction": [ metadata.light3_direction.x, metadata.light3_direction.y, metadata.light3_direction.z ], "light3_color": [ metadata.light3_color.r, metadata.light3_color.g, metadata.light3_color.b, metadata.light3_color.a ], } # Meshes primitive = Primitive(attributes=Attributes( POSITION=0, NORMAL=icon.animation_shapes, TEXCOORD_0=icon.animation_shapes + 1, COLOR_0=icon.animation_shapes + 2), material=0) if animation_present: primitive.targets = [{ "POSITION": i + 1 } for i in range(icon.animation_shapes - 1)] model.meshes = [Mesh(name="Icon", primitives=[primitive])] # Buffers model.buffers = [ Buffer(uri=f"{basename}.bin", byteLength=len(model_data)), Buffer(uri=f"{basename}.png", byteLength=len(texture_data)) ] # Materials model.images = [Image(bufferView=1, mimeType="image/png")] model.textures = [Texture(source=0)] model.materials = [ Material(name="Material", pbrMetallicRoughness=PBRMetallicRoughness( baseColorTexture=TextureInfo(index=0), roughnessFactor=1, metallicFactor=0)) ] # Animations if animation_present: model.animations = [ Animation(name="Default", samplers=[ AnimationSampler( input=icon.animation_shapes + 3, output=icon.animation_shapes + 4, interpolation=Interpolation.LINEAR.value) ], channels=[ Channel(sampler=0, target=Target(node=0, path="weights")) ]), ] # Buffer Views model.bufferViews = [ BufferView(name="Data", buffer=0, byteStride=struct.calcsize(vertex_info_format), byteLength=len(model_data)), BufferView(name="Texture", buffer=1, byteLength=len(texture_data)), ] if animation_present: model.bufferViews.append( BufferView(name="Animation", buffer=0, byteOffset=animation_offset, byteLength=animation_length), ) # Accessors model.accessors = [ Accessor(name=f"Position {i}", bufferView=0, byteOffset=i * 3 * float_size, min=mins[i], max=maxs[i], count=len(icon.vertices), componentType=ComponentType.FLOAT.value, type=AccessorType.VEC3.value) for i in range(icon.animation_shapes) ] model.accessors.extend([ Accessor(name="Normal", bufferView=0, byteOffset=((icon.animation_shapes - 1) * 3 * float_size) + 3 * float_size, count=len(icon.vertices), componentType=ComponentType.FLOAT.value, type=AccessorType.VEC3.value), Accessor(name="UV", bufferView=0, byteOffset=((icon.animation_shapes - 1) * 3 * float_size) + 6 * float_size, count=len(icon.vertices), componentType=ComponentType.FLOAT.value, type=AccessorType.VEC2.value), Accessor(name="Color", bufferView=0, byteOffset=((icon.animation_shapes - 1) * 3 * float_size) + 8 * float_size, count=len(icon.vertices), componentType=ComponentType.FLOAT.value, type=AccessorType.VEC3.value), ]) if animation_present: model.accessors.extend([ Accessor(name="Animation Time", bufferView=2, byteOffset=0, min=[0.0], max=[(icon.frame_count) * animation_speed], count=(icon.frame_count + 1), componentType=ComponentType.FLOAT.value, type=AccessorType.SCALAR.value), Accessor(name="Animation Data", bufferView=2, byteOffset=(icon.frame_count + 1) * float_size, min=[0.0], max=[1.0], count=(icon.frame_count + 1) * (icon.animation_shapes - 1), componentType=ComponentType.FLOAT.value, type=AccessorType.SCALAR.value) ]) resources = [ FileResource(f"{basename}.bin", data=model_data), FileResource(f"{basename}.png", data=texture_data) ] gltf = GLTF(model=model, resources=resources) gltf.export(filename)
def add_background(gltf: GLTF) -> int: model = gltf.model resources = gltf.resources root = "pannello %02d.pdf.jpg" found = [] for i in range(1, 27): basename = root % i try: fn = get_resource_path(basename) except KeyError: logger.warn(f"not found {basename!r}") else: found.append(fn) found = found[:9] n = len(found) if n == 0: raise ValueError(root) from .export import make_material from .export import get_square from .export import add_polygon from .export import add_node from .export import gm dist = 30 fov_y = np.deg2rad(45) nodes_panels = [] for i, fn in enumerate(found): # if i > 5: # break material_index = make_material(gltf, doubleSided=True, baseColorFactor=[0.5, 0.5, 0.5, 1.0], fn_emissive=fn # fn=fn, fn_normals=None ) print(fn, material_index) mi = get_square() mesh_index = add_polygon( gltf, f"bg-{i}", vertices=mi.vertices, texture=mi.textures, colors=mi.color, normals=mi.normals, material=material_index, ) v = (np.pi * 2) / n theta = v * i a = 2 * np.tan((2 * np.pi / n) / 2) * dist matrix = (SE3_rotz(theta) @ SE3_trans(np.array( (dist, 0, 0))) @ SE3_roty(np.pi / 2) @ SE3_rotz(np.pi / 2) @ np.diag(np.array( (a, a, 1, 1))) @ SE3_trans(np.array([0, 0.35, 0]))) node1_index = add_node( gltf, Node(name=f"panel-{i}", mesh=mesh_index, matrix=gm(matrix))) nodes_panels.append(node1_index) node = Node(children=nodes_panels) return add_node(gltf, node)