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 Binary Information | |
# Vertex data(POSITION) | |
vertices = [ | |
[ 1.0, 1.0, -1.0,], | |
[ 1.0, 1.0, -1.0,], | |
[ 1.0, 1.0, -1.0,], | |
[ 1.0, -1.0, -1.0,], | |
[ 1.0, -1.0, -1.0,], | |
[ 1.0, -1.0, -1.0,], | |
[ 1.0, 1.0, 1.0,], | |
[ 1.0, 1.0, 1.0,], | |
[ 1.0, 1.0, 1.0,], | |
[ 1.0, -1.0, 1.0,], | |
[ 1.0, -1.0, 1.0,], | |
[ 1.0, -1.0, 1.0,], | |
[-1.0, 1.0, -1.0,], | |
[-1.0, 1.0, -1.0,], | |
[-1.0, 1.0, -1.0,], | |
[-1.0, -1.0, -1.0,], | |
[-1.0, -1.0, -1.0,], | |
[-1.0, -1.0, -1.0,], | |
[-1.0, 1.0, 1.0,], | |
[-1.0, 1.0, 1.0,], | |
[-1.0, 1.0, 1.0,], | |
[-1.0, -1.0, 1.0,], | |
[-1.0, -1.0, 1.0,], | |
[-1.0, -1.0, 1.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,], | |
[ 0.0, 1.0, -0.0,], | |
[ 1.0, 0.0, -0.0,], | |
[ 0.0, -1.0, -0.0,], | |
[ 0.0, 0.0, -1.0,], | |
[ 1.0, 0.0, -0.0,], | |
[ 0.0, 0.0, 1.0,], | |
[ 0.0, 1.0, -0.0,], | |
[ 1.0, 0.0, -0.0,], | |
[ 0.0, -1.0, -0.0,], | |
[ 0.0, 0.0, 1.0,], | |
[ 1.0, 0.0, -0.0,], | |
[-1.0, 0.0, -0.0,], | |
[ 0.0, 0.0, -1.0,], | |
[ 0.0, 1.0, -0.0,], | |
[-1.0, 0.0, -0.0,], | |
[ 0.0, -1.0, -0.0,], | |
[ 0.0, 0.0, -1.0,], | |
[-1.0, 0.0, -0.0,], | |
[ 0.0, 0.0, 1.0,], | |
[ 0.0, 1.0, -0.0,], | |
[-1.0, 0.0, -0.0,], | |
[ 0.0, -1.0, -0.0,], | |
[ 0.0, 0.0, 1.0,], | |
] | |
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.9, 0.1],[0.9, 0.0],[1.0, 0.1], | |
[0.9, 1.0],[0.9, 0.9],[1.0, 0.9], | |
[0.9, 0.1],[0.9, 0.1],[0.9, 0.1], | |
[0.9, 0.9],[0.9, 0.9],[0.9, 0.9], | |
[0.0, 0.1],[0.1, 0.1],[0.1, 0.0], | |
[0.0, 0.9],[0.1, 1.0],[0.1, 0.9], | |
[0.1, 0.1],[0.1, 0.1],[0.1, 0.1], | |
[0.1, 0.9],[0.1, 0.9],[0.1, 0.9], | |
] | |
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 = [ | |
1, 14, 20, | |
1, 20, 7, | |
10, 6, 19, | |
10, 19, 23, | |
21, 18, 12, | |
21, 12, 15, | |
16, 3, 9, | |
16, 9, 22, | |
5, 2, 8, | |
5, 8, 11, | |
17, 13, 0, | |
17, 0, 4 | |
] | |
vertex_index_bytearray = bytearray() | |
for value in vertex_indices: | |
vertex_index_bytearray.extend(struct.pack('H', value)) | |
vertex_index_bytelen = len(vertex_index_bytearray) | |
def create_picture_box_model(img_bytearray): | |
# Loading Picture | |
img = PIL_Image.open(img_bytearray).convert('RGB') | |
# Adjusting to a Value That Is Divisible by 8 for Edge Length | |
img = img.resize((8 * (img.size[0] // 8), 8 * (img.size[1] // 8))) | |
temp_img = PIL_Image.new('RGB', (int(img.size[0] * 1.25), int(img.size[1] * 1.25)), 'white') | |
temp_img.paste(img, (int(temp_img.size[0] / 10), int(temp_img.size[1] / 10))) | |
img = temp_img.copy() | |
for offset_x in range(0, int(temp_img.size[0] / 10)): | |
img.paste(img.crop((int(temp_img.size[0] / 10)+1, 0, int(temp_img.size[0] / 10)+2, img.size[1])), (offset_x, 0)) | |
for offset_x in range(int(temp_img.size[0] / 10 * 9), img.size[0]): | |
img.paste(img.crop((int(temp_img.size[0] / 10 * 9) - 2, 0, int(temp_img.size[0] / 10 *9) - 1, img.size[1])), (offset_x, 0)) | |
for offset_y in range(0, int(temp_img.size[1] / 10)): | |
img.paste(img.crop((0, int(temp_img.size[1] / 10) + 1, img.size[0], int(temp_img.size[1] / 10) + 2)), (0, offset_y)) | |
for offset_y in range(int(temp_img.size[1] / 10 * 9), img.size[1]): | |
img.paste(img.crop((0, int(temp_img.size[1] / 10 * 9) - 2, img.size[0], int(temp_img.size[1] / 10 * 9) - 1)), (0, offset_y)) | |
img_bytearray = io.BytesIO() | |
img.save(img_bytearray, format="JPEG", quality=95) | |
img_bytearray = img_bytearray.getvalue() | |
img_bytelen = len(img_bytearray) | |
# Calculating the Scale of a 3D Model | |
scale_factor = np.power(img.size[0] * img.size[1], 0.5) | |
scale = (img.size[0] / scale_factor, img.size[1] / scale_factor, 0.1) | |
# Concatenation of the binary data section | |
bytearray_list = [ | |
vertex_bytearray, | |
normal_bytearray, | |
texcoord_0_bytearray, | |
vertex_index_bytearray, | |
img_bytearray, | |
] | |
bytelen_list = [ | |
vertex_bytelen, | |
normal_bytelen, | |
texcoord_0_bytelen, | |
vertex_index_bytelen, | |
img_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)] | |
# Asset | |
asset=Asset() | |
# 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), | |
] | |
# 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) | |
] | |
# Image | |
images=[ | |
Image(mimeType='image/jpeg', bufferView=4), | |
] | |
# Sampler | |
samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング | |
# Texture | |
textures = [ | |
Texture(name='Image',sampler=0,source=0), | |
] | |
# Material | |
materials = [ | |
Material( | |
pbrMetallicRoughness=PBRMetallicRoughness( | |
baseColorTexture=TextureInfo(index=0), | |
metallicFactor=0, | |
roughnessFactor=0.5 | |
), | |
name='Material0', | |
alphaMode='BLEND', | |
doubleSided=False | |
), | |
] | |
# Mesh | |
meshes = [ | |
Mesh(name='Image', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2),indices=3, material=0)]), | |
] | |
# Node | |
nodes = [ | |
Node(mesh=0,rotation=None, scale=scale), | |
] | |
# Scene | |
scene = 0 | |
scenes = [Scene(name='Scene', nodes=[0])] | |
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 | |