3dmodelmaker / src /card_model.py
ikeda
translate Japanese comments to English
6810bed
raw
history blame contribute delete
No virus
12.3 kB
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