Spaces:
Running
Running
| import io | |
| import numpy as np | |
| from PIL import Image as PIL_Image # Renaming to avoid conflict with Image from gltflib | |
| import struct | |
| import uuid | |
| from gltflib import ( | |
| GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType, | |
| BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness) | |
| # Common configuration information | |
| # Parts that are independent of the binary section's offset and size can be predefined. | |
| # Asset | |
| asset=Asset() | |
| # Image | |
| images=[ | |
| Image(mimeType='image/jpeg', bufferView=4), | |
| Image(mimeType='image/jpeg',bufferView=5), | |
| Image(mimeType='image/jpeg',bufferView=6), | |
| Image(mimeType='image/jpeg',bufferView=7), | |
| Image(mimeType='image/jpeg',bufferView=8), | |
| Image(mimeType='image/jpeg',bufferView=9), | |
| ] | |
| # Sampler | |
| samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter: Nearest filtering, minFilter: Mipmap + Nearest filtering | |
| # Texture | |
| textures = [ | |
| Texture(name='Front',sampler=0,source=0), | |
| Texture(name='Back',sampler=0,source=1), | |
| Texture(name='Left',sampler=0,source=2), | |
| Texture(name='Right',sampler=0,source=3), | |
| Texture(name='Top',sampler=0,source=4), | |
| Texture(name='Bottom',sampler=0,source=5), | |
| ] | |
| # Material | |
| materials = [ | |
| Material( | |
| pbrMetallicRoughness=PBRMetallicRoughness( | |
| baseColorTexture=TextureInfo(index=0), | |
| metallicFactor=0, | |
| roughnessFactor=0.5 | |
| ), | |
| name='Material0', | |
| alphaMode='OPAQUE', | |
| doubleSided=False | |
| ), | |
| Material( | |
| pbrMetallicRoughness=PBRMetallicRoughness( | |
| baseColorTexture=TextureInfo(index=1), | |
| metallicFactor=0, | |
| roughnessFactor=0.5 | |
| ), | |
| name='Material1', | |
| alphaMode='OPAQUE', | |
| doubleSided=False | |
| ), | |
| Material( | |
| pbrMetallicRoughness=PBRMetallicRoughness( | |
| baseColorTexture=TextureInfo(index=2), | |
| metallicFactor=0, | |
| roughnessFactor=0.5 | |
| ), | |
| name='Material2', | |
| alphaMode='OPAQUE', | |
| doubleSided=True | |
| ), | |
| Material( | |
| pbrMetallicRoughness=PBRMetallicRoughness( | |
| baseColorTexture=TextureInfo(index=3), | |
| metallicFactor=0, | |
| roughnessFactor=0.5 | |
| ), | |
| name='Material3', | |
| alphaMode='OPAQUE', | |
| doubleSided=True | |
| ), | |
| Material( | |
| pbrMetallicRoughness=PBRMetallicRoughness( | |
| baseColorTexture=TextureInfo(index=4), | |
| metallicFactor=0, | |
| roughnessFactor=0.5 | |
| ), | |
| name='Material4', | |
| alphaMode='OPAQUE', | |
| doubleSided=True | |
| ), | |
| Material( | |
| pbrMetallicRoughness=PBRMetallicRoughness( | |
| baseColorTexture=TextureInfo(index=5), | |
| metallicFactor=0, | |
| roughnessFactor=0.5 | |
| ), | |
| name='Material5', | |
| alphaMode='OPAQUE', | |
| doubleSided=True | |
| ), | |
| ] | |
| # Mesh | |
| meshes = [ | |
| Mesh(name='Front', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=0)]), | |
| Mesh(name='Back', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=1)]), | |
| Mesh(name='Left', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=2)]), | |
| Mesh(name='Right', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=3)]), | |
| Mesh(name='Top', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=4)]), | |
| Mesh(name='Bottom', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=5)]), | |
| ] | |
| def create_card_model(front_img_bytearray, back_img_bytearray, option_dict): | |
| is_thick = True if option_dict['厚み'] == '有' else False | |
| # Front image | |
| front_img = PIL_Image.open(front_img_bytearray).convert('RGB') | |
| front_bytearray = io.BytesIO() | |
| front_img.save(front_bytearray, format="JPEG", quality=95) # Force JPEG format for saving | |
| front_bytearray = front_bytearray.getvalue() | |
| front_bytelen = len(front_bytearray) | |
| # Back image | |
| back_img = PIL_Image.open(back_img_bytearray).convert('RGB') | |
| back_bytearray = io.BytesIO() | |
| back_img.save(back_bytearray, format="JPEG", quality=95) # Force JPEG format for saving | |
| back_bytearray = back_bytearray.getvalue() | |
| back_bytelen = len(back_bytearray) | |
| # Create side image (extend the color of the edges of the back side) | |
| back_img_array = np.array(back_img) | |
| left_side_img = PIL_Image.fromarray(np.tile(back_img_array[:, [0], :], [1, 32, 1])) | |
| right_side_img = PIL_Image.fromarray(np.tile(back_img_array[:, [back_img_array.shape[1] - 1], :], [1, 32, 1])) | |
| top_side_img = PIL_Image.fromarray(np.tile(back_img_array[[0], :, :], [32, 1, 1])) | |
| bottom_side_img = PIL_Image.fromarray(np.tile(back_img_array[[back_img_array.shape[0] - 1], :, :], [32, 1, 1])) | |
| left_side_bytearray = io.BytesIO() | |
| left_side_img.save(left_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving | |
| left_side_bytearray = left_side_bytearray.getvalue() | |
| left_side_bytelen = len(left_side_bytearray) | |
| right_side_bytearray = io.BytesIO() | |
| right_side_img.save(right_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving | |
| right_side_bytearray = right_side_bytearray.getvalue() | |
| right_side_bytelen = len(right_side_bytearray) | |
| top_side_bytearray = io.BytesIO() | |
| top_side_img.save(top_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving | |
| top_side_bytearray = top_side_bytearray.getvalue() | |
| top_side_bytelen = len(top_side_bytearray) | |
| bottom_side_bytearray = io.BytesIO() | |
| bottom_side_img.save(bottom_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving | |
| bottom_side_bytearray = bottom_side_bytearray.getvalue() | |
| bottom_side_bytelen = len(bottom_side_bytearray) | |
| # Vertex data(POSITION) | |
| vertices = [ | |
| (-1.0, -1.0, 0.0), | |
| ( 1.0, -1.0, 0.0), | |
| (-1.0, 1.0, 0.0), | |
| ( 1.0, 1.0, 0.0) | |
| ] | |
| vertex_bytearray = bytearray() | |
| for vertex in vertices: | |
| for value in vertex: | |
| vertex_bytearray.extend(struct.pack('f', value)) | |
| vertex_bytelen = len(vertex_bytearray) | |
| mins = [min([vertex[i] for vertex in vertices]) for i in range(3)] | |
| maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)] | |
| # Normal data(NORMAL) | |
| normals = [( 0.0, 0.0, 1.0)] * 4 | |
| normal_bytearray = bytearray() | |
| for normal in normals: | |
| for value in normal: | |
| normal_bytearray.extend(struct.pack('f', value)) | |
| normal_bytelen = len(normal_bytearray) | |
| # Texture coordinates(TEXCOORD_0) | |
| texcoord_0s = [ | |
| (0.0, 1.0), | |
| (1.0, 1.0), | |
| (0.0, 0.0), | |
| (1.0, 0.0) | |
| ] | |
| texcoord_0_bytearray = bytearray() | |
| for texcoord_0 in texcoord_0s: | |
| for value in texcoord_0: | |
| texcoord_0_bytearray.extend(struct.pack('f', value)) | |
| texcoord_0_bytelen = len(texcoord_0_bytearray) | |
| # Vertex indices | |
| vertex_indices = [0, 1, 2, 1, 3, 2] | |
| vertex_index_bytearray = bytearray() | |
| for value in vertex_indices: | |
| vertex_index_bytearray.extend(struct.pack('H', value)) | |
| vertex_index_bytelen = len(vertex_index_bytearray) | |
| # Concatenation of the binary data section | |
| bytearray_list = [ | |
| vertex_bytearray, | |
| normal_bytearray, | |
| texcoord_0_bytearray, | |
| vertex_index_bytearray, | |
| front_bytearray, | |
| back_bytearray, | |
| left_side_bytearray, | |
| right_side_bytearray, | |
| top_side_bytearray, | |
| bottom_side_bytearray, | |
| ] | |
| bytelen_list = [ | |
| vertex_bytelen, | |
| normal_bytelen, | |
| texcoord_0_bytelen, | |
| vertex_index_bytelen, | |
| front_bytelen, | |
| back_bytelen, | |
| left_side_bytelen, | |
| right_side_bytelen, | |
| top_side_bytelen, | |
| bottom_side_bytelen | |
| ] | |
| bytelen_cumsum_list = list(np.cumsum(bytelen_list)) | |
| bytelen_cumsum_list = list(map(lambda x: int(x), bytelen_cumsum_list)) | |
| all_bytearray = bytearray() | |
| for temp_bytearray in bytearray_list: | |
| all_bytearray.extend(temp_bytearray) | |
| offset_list = [0] + bytelen_cumsum_list # The first offset is 0 | |
| offset_list.pop() # Remove the end | |
| # GLBResource | |
| resources = [GLBResource(data=all_bytearray)] | |
| # Buffer | |
| buffers = [Buffer(byteLength=len(all_bytearray))] | |
| # BufferView | |
| bufferViews = [ | |
| BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value), | |
| BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value), | |
| BufferView(buffer=0, byteOffset=offset_list[2], byteLength=bytelen_list[2], target=BufferTarget.ARRAY_BUFFER.value), | |
| BufferView(buffer=0, byteOffset=offset_list[3], byteLength=bytelen_list[3], target=BufferTarget.ELEMENT_ARRAY_BUFFER.value), | |
| BufferView(buffer=0, byteOffset=offset_list[4], byteLength=bytelen_list[4], target=None), | |
| BufferView(buffer=0, byteOffset=offset_list[5], byteLength=bytelen_list[5], target=None), | |
| BufferView(buffer=0, byteOffset=offset_list[6], byteLength=bytelen_list[6], target=None), | |
| BufferView(buffer=0, byteOffset=offset_list[7], byteLength=bytelen_list[7], target=None), | |
| BufferView(buffer=0, byteOffset=offset_list[8], byteLength=bytelen_list[8], target=None), | |
| BufferView(buffer=0, byteOffset=offset_list[9], byteLength=bytelen_list[9], target=None), | |
| ] | |
| # Accessor | |
| accessors = [ | |
| Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins), | |
| Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None), | |
| Accessor(bufferView=2, componentType=ComponentType.FLOAT.value, count=len(texcoord_0s), type=AccessorType.VEC2.value, max=None, min=None), | |
| Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None), | |
| ] | |
| # Node | |
| card_thickness = 0.025 | |
| card_ratio_x = 0.8679999709129333 | |
| card_ratio_y = 1.2130000591278076 | |
| card_ratio_z = 1 | |
| if is_thick: | |
| nodes = [ | |
| Node(mesh=0, scale=[card_ratio_x, card_ratio_y, card_ratio_z], rotation=None, translation=[0, 0, card_thickness]), | |
| Node(mesh=1, scale=[card_ratio_x, card_ratio_y, card_ratio_z], rotation=[0, 1, 0, 0], translation=[0, 0, -card_thickness]), | |
| Node(mesh=2, scale=[card_thickness, card_ratio_y, card_ratio_z], rotation=[0, 0.7071, 0, 0.7071], translation=[ card_ratio_x, 0, 0]), # 左 | |
| Node(mesh=3, scale=[card_thickness, card_ratio_y, card_ratio_z], rotation=[0, -0.7071, 0, 0.7071], translation=[-card_ratio_x, 0, 0]), # 右 | |
| Node(mesh=4, scale=[card_ratio_x, card_thickness, card_ratio_z], rotation=[ 0.7071, 0, 0, 0.7071], translation=[0, card_ratio_y, 0]), # 上 | |
| Node(mesh=5, scale=[card_ratio_x, card_thickness, card_ratio_z], rotation=[-0.7071, 0, 0, 0.7071], translation=[0, -card_ratio_y, 0]), # 下 | |
| ] | |
| else: | |
| nodes = [ | |
| Node(mesh=0, scale=[card_ratio_x, card_ratio_y, card_ratio_z], rotation=None), | |
| Node(mesh=1, scale=[card_ratio_x, card_ratio_y, card_ratio_z], rotation=[0, 1, 0, 0]) | |
| ] | |
| # Scene | |
| scene = 0 | |
| if is_thick: | |
| scenes = [Scene(name='Scene', nodes=[0, 1, 2, 3, 4, 5])] | |
| else: | |
| scenes = [Scene(name='Scene', nodes=[0, 1])] | |
| model = GLTFModel( | |
| asset=asset, | |
| buffers=buffers, | |
| bufferViews=bufferViews, | |
| accessors=accessors, | |
| images=images, | |
| samplers=samplers, | |
| textures=textures, | |
| materials=materials, | |
| meshes=meshes, | |
| nodes=nodes, | |
| scene=scene, | |
| scenes=scenes | |
| ) | |
| gltf = GLTF(model=model, resources=resources) | |
| tmp_filename = uuid.uuid4().hex | |
| model_path = f'../tmp/{tmp_filename}.glb' | |
| gltf.export(model_path) | |
| return model_path | |