Spaces:
Running
Running
2Nike2
commited on
Commit
·
5d3564f
1
Parent(s):
93324c5
add manhole model creation
Browse files- app.py +19 -0
- requirements.txt +2 -1
- src/display_message_en.json +2 -1
- src/display_message_ja.json +2 -1
- src/manhole_model.py +281 -0
app.py
CHANGED
@@ -11,6 +11,7 @@ from src.front_card_image_historic_site import create_historic_site_card_image
|
|
11 |
from src.picture_box_model import create_picture_box_model
|
12 |
from src.extracted_objects_model import create_extracted_objects_model
|
13 |
from src.card_model import create_card_model
|
|
|
14 |
|
15 |
language = os.environ.get('LANGUAGE', 'en')
|
16 |
print(f'LANGUAGE: {language}')
|
@@ -64,6 +65,15 @@ def create_3dmodel(model_no, title, color, mark, historic_site_type, difficulty,
|
|
64 |
|
65 |
return model_path
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
with gr.Blocks() as demo:
|
68 |
|
69 |
gr.Markdown(display_message_dict['header'])
|
@@ -114,5 +124,14 @@ with gr.Blocks() as demo:
|
|
114 |
)
|
115 |
|
116 |
gr.Markdown(display_message_dict['footer_historic_site_card'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
|
118 |
demo.launch()
|
|
|
11 |
from src.picture_box_model import create_picture_box_model
|
12 |
from src.extracted_objects_model import create_extracted_objects_model
|
13 |
from src.card_model import create_card_model
|
14 |
+
from src.manhole_model import create_manhole_model
|
15 |
|
16 |
language = os.environ.get('LANGUAGE', 'en')
|
17 |
print(f'LANGUAGE: {language}')
|
|
|
65 |
|
66 |
return model_path
|
67 |
|
68 |
+
def create_manhole(image):
|
69 |
+
img_bytearray = BytesIO()
|
70 |
+
image['background'].save(img_bytearray, "JPEG", quality=95)
|
71 |
+
img_bytearray.seek(0) # Seek to the beginning of the image, otherwise it results in empty data.
|
72 |
+
|
73 |
+
model_path = create_manhole_model(img_bytearray)
|
74 |
+
|
75 |
+
return model_path
|
76 |
+
|
77 |
with gr.Blocks() as demo:
|
78 |
|
79 |
gr.Markdown(display_message_dict['header'])
|
|
|
124 |
)
|
125 |
|
126 |
gr.Markdown(display_message_dict['footer_historic_site_card'])
|
127 |
+
|
128 |
+
with gr.Tab(display_message_dict["tab_label_manhole"]):
|
129 |
+
image = gr.ImageEditor(image_mode='RGB', sources="upload", type="pil", label=display_message_dict['label_image'])
|
130 |
+
button = gr.Button(display_message_dict['label_button'])
|
131 |
+
button.click(
|
132 |
+
create_manhole,
|
133 |
+
inputs=[image],
|
134 |
+
outputs=[gr.Model3D(camera_position=(90, 90, 5))]
|
135 |
+
)
|
136 |
|
137 |
demo.launch()
|
requirements.txt
CHANGED
@@ -3,4 +3,5 @@ Pillow==9.5.0
|
|
3 |
numpy==1.23.5
|
4 |
gltflib==1.0.13
|
5 |
triangle==20230923
|
6 |
-
opencv-python==4.8.0.76
|
|
|
|
3 |
numpy==1.23.5
|
4 |
gltflib==1.0.13
|
5 |
triangle==20230923
|
6 |
+
opencv-python==4.8.0.76
|
7 |
+
scipy==1.12.0
|
src/display_message_en.json
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
"header": "# 3D Card Creation \nPlease enter various settings, upload an image and specify its range. Then, press the \"Create 3D Card\" button. \nOnce the 3D card is completed, it will be displayed at the bottom of the screen and you will be able to download it.",
|
3 |
"tab_label_card_general": "Card",
|
4 |
"tab_label_historic_site_card": "Historic Site Card",
|
|
|
5 |
"label_title": "Title",
|
6 |
"placeholder_title": "The title displayed on the top of the card.",
|
7 |
"label_card_type": "Card Type",
|
@@ -15,7 +16,7 @@
|
|
15 |
"label_is_thick": "Thickness",
|
16 |
"info_is_thick": "Please check this option if you want to add thickness to the card.",
|
17 |
"label_image": "Image",
|
18 |
-
"label_button": "Start 3D
|
19 |
"label_color": "Color(Historic Site Classification)",
|
20 |
"color_dict": {
|
21 |
"茶": "Brown: Shell mounds, settlement ruins, ancient tombs, graveyards",
|
|
|
2 |
"header": "# 3D Card Creation \nPlease enter various settings, upload an image and specify its range. Then, press the \"Create 3D Card\" button. \nOnce the 3D card is completed, it will be displayed at the bottom of the screen and you will be able to download it.",
|
3 |
"tab_label_card_general": "Card",
|
4 |
"tab_label_historic_site_card": "Historic Site Card",
|
5 |
+
"tab_label_manhole": "Manhole",
|
6 |
"label_title": "Title",
|
7 |
"placeholder_title": "The title displayed on the top of the card.",
|
8 |
"label_card_type": "Card Type",
|
|
|
16 |
"label_is_thick": "Thickness",
|
17 |
"info_is_thick": "Please check this option if you want to add thickness to the card.",
|
18 |
"label_image": "Image",
|
19 |
+
"label_button": "Start 3D Model Creation",
|
20 |
"label_color": "Color(Historic Site Classification)",
|
21 |
"color_dict": {
|
22 |
"茶": "Brown: Shell mounds, settlement ruins, ancient tombs, graveyards",
|
src/display_message_ja.json
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
"header": "# 3Dカード作成 \n各種設定値の入力、画像のアップロードと範囲の指定を行った後、「3Dカード作成」ボタンを押してください。 \n3Dカードが完成したら画面下部に表示され、ダウンロードすることが出来ます。",
|
3 |
"tab_label_card_general": "カード(一般)",
|
4 |
"tab_label_historic_site_card": "史跡カード",
|
|
|
5 |
"label_title": "タイトル",
|
6 |
"placeholder_title": "カード上部に表示されるタイトルです。",
|
7 |
"label_card_type": "カード種類",
|
@@ -15,7 +16,7 @@
|
|
15 |
"label_is_thick": "厚み",
|
16 |
"info_is_thick": "カードに厚みを付けるときはチェックして下さい。",
|
17 |
"label_image": "画像",
|
18 |
-
"label_button": "3D
|
19 |
"label_color": "色(史跡の分類)",
|
20 |
"color_dict": {
|
21 |
"茶": "「茶」: 貝塚、集落跡、古墳、墓地等",
|
|
|
2 |
"header": "# 3Dカード作成 \n各種設定値の入力、画像のアップロードと範囲の指定を行った後、「3Dカード作成」ボタンを押してください。 \n3Dカードが完成したら画面下部に表示され、ダウンロードすることが出来ます。",
|
3 |
"tab_label_card_general": "カード(一般)",
|
4 |
"tab_label_historic_site_card": "史跡カード",
|
5 |
+
"tab_label_manhole": "マンホール",
|
6 |
"label_title": "タイトル",
|
7 |
"placeholder_title": "カード上部に表示されるタイトルです。",
|
8 |
"label_card_type": "カード種類",
|
|
|
16 |
"label_is_thick": "厚み",
|
17 |
"info_is_thick": "カードに厚みを付けるときはチェックして下さい。",
|
18 |
"label_image": "画像",
|
19 |
+
"label_button": "3Dモデル作成",
|
20 |
"label_color": "色(史跡の分類)",
|
21 |
"color_dict": {
|
22 |
"茶": "「茶」: 貝塚、集落跡、古墳、墓地等",
|
src/manhole_model.py
ADDED
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import io
|
2 |
+
import numpy as np
|
3 |
+
from PIL import Image as PIL_Image # gltflibのImageと被るので別名にする。
|
4 |
+
from PIL import ImageDraw as PIL_ImageDraw # 上記と同じ命名規則にする。
|
5 |
+
from scipy.spatial import Delaunay
|
6 |
+
import cv2
|
7 |
+
import os
|
8 |
+
import struct
|
9 |
+
import matplotlib.pyplot as plt
|
10 |
+
import triangle
|
11 |
+
import uuid
|
12 |
+
|
13 |
+
from gltflib import (
|
14 |
+
GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
|
15 |
+
BufferTarget, ComponentType, GLBResource, FileResource, PBRMetallicRoughness)
|
16 |
+
|
17 |
+
# Common configuration information
|
18 |
+
# Parts that are independent of the binary section's offset and size can be predefined.
|
19 |
+
|
20 |
+
# Asset
|
21 |
+
asset=Asset()
|
22 |
+
|
23 |
+
# Image
|
24 |
+
images=[
|
25 |
+
Image(mimeType='image/jpeg', bufferView=4),
|
26 |
+
]
|
27 |
+
|
28 |
+
# Sampler
|
29 |
+
samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング
|
30 |
+
|
31 |
+
# Texture
|
32 |
+
textures = [
|
33 |
+
Texture(name='Main',sampler=0,source=0),
|
34 |
+
]
|
35 |
+
|
36 |
+
# Material
|
37 |
+
materials = [
|
38 |
+
Material(
|
39 |
+
pbrMetallicRoughness=PBRMetallicRoughness(
|
40 |
+
baseColorTexture=TextureInfo(index=0),
|
41 |
+
metallicFactor=0,
|
42 |
+
roughnessFactor=1
|
43 |
+
),
|
44 |
+
name='Material0',
|
45 |
+
alphaMode='OPAQUE',
|
46 |
+
doubleSided=True
|
47 |
+
),
|
48 |
+
]
|
49 |
+
|
50 |
+
# Mesh
|
51 |
+
meshes = [
|
52 |
+
Mesh(name='Main', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2),
|
53 |
+
indices=3, material=0, mode=4)]),
|
54 |
+
]
|
55 |
+
|
56 |
+
# Scene
|
57 |
+
scene = 0
|
58 |
+
scenes = [Scene(name='Scene', nodes=[0])]
|
59 |
+
|
60 |
+
def create_manhole_model(original_img_bytearray):
|
61 |
+
|
62 |
+
# Image
|
63 |
+
img = PIL_Image.open(original_img_bytearray).convert('RGB')
|
64 |
+
|
65 |
+
# Add edge color space.
|
66 |
+
# (edge color is decided at 'Decide edge color' section)
|
67 |
+
temp_img = PIL_Image.new('RGB', (img.size[0] + 3, img.size[1] + 3), 'black')
|
68 |
+
temp_img.paste(img, (3, 3))
|
69 |
+
img = temp_img.copy()
|
70 |
+
|
71 |
+
# Get coordinates of manhole
|
72 |
+
ret, mask = cv2.threshold(cv2.cvtColor(np.array(img), cv2.COLOR_RGB2GRAY), 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY_INV)
|
73 |
+
edges = cv2.Canny(mask , 50, 150, apertureSize=3)
|
74 |
+
circle = cv2.HoughCircles(edges,
|
75 |
+
cv2.HOUGH_GRADIENT, 1, int(max(img.size)*2),
|
76 |
+
param1=50, param2=30, minRadius=0, maxRadius=0)[0][0]
|
77 |
+
|
78 |
+
coordinate_list = []
|
79 |
+
num_polygons = 360
|
80 |
+
for i in range(num_polygons):
|
81 |
+
coordinate_list.append((
|
82 |
+
int(circle[0] + np.cos((i / num_polygons) * (2 * np.pi)) * circle[2]),
|
83 |
+
int(circle[1] + np.sin((i / num_polygons) * (2 * np.pi)) * circle[2])
|
84 |
+
))
|
85 |
+
coordinate_list = [coordinate_list]
|
86 |
+
|
87 |
+
# Decide edge color
|
88 |
+
coordinate_colors = list(map(lambda x: img.getpixel(x), coordinate_list[0]))
|
89 |
+
edge_color = tuple(np.mean(np.array(coordinate_colors), axis=0).astype(np.uint8))
|
90 |
+
|
91 |
+
draw = PIL_ImageDraw.Draw(img)
|
92 |
+
draw.point(tuple((i, j) for i in range(3) for j in range(3)), fill=edge_color)
|
93 |
+
|
94 |
+
# image binary data
|
95 |
+
img_ext = "JPEG"
|
96 |
+
img_bytearray = io.BytesIO()
|
97 |
+
img.save(img_bytearray, format=img_ext, quality=95)
|
98 |
+
img_bytearray = img_bytearray.getvalue()
|
99 |
+
img_bytelen = len(img_bytearray)
|
100 |
+
|
101 |
+
# calculate scale of 3d model
|
102 |
+
scale_factor = np.power(img.size[0] * img.size[1], 0.5)
|
103 |
+
scale = (img.size[0] / scale_factor, img.size[1] / scale_factor, 0.4)
|
104 |
+
|
105 |
+
# Thickness of manhole
|
106 |
+
thickness = 0.2 # manhole
|
107 |
+
|
108 |
+
# Vertex data(POSITION)
|
109 |
+
front_vertex_list = []
|
110 |
+
back_vertex_list = []
|
111 |
+
for coordinates in coordinate_list:
|
112 |
+
front_vertices = []
|
113 |
+
back_vertices = []
|
114 |
+
# Be aware that the Y-axis direction is inverted between images and GLB files
|
115 |
+
for coordinate in coordinates:
|
116 |
+
front_vertices.append((coordinate[0] * 2 / img.size[0] - 1.0, -(coordinate[1] * 2 / img.size[1] - 1.0), thickness))
|
117 |
+
back_vertices.append((coordinate[0] * 2 / img.size[0] - 1.0, -(coordinate[1] * 2 / img.size[1] - 1.0), -thickness))
|
118 |
+
|
119 |
+
front_vertex_list.append(front_vertices)
|
120 |
+
back_vertex_list.append(back_vertices)
|
121 |
+
|
122 |
+
vertices = front_vertex_list[0] + back_vertex_list[0]
|
123 |
+
vertex_bytearray = bytearray()
|
124 |
+
|
125 |
+
# Set vertices with 'Front+Back' and 'Edge'
|
126 |
+
for i in range(2): # first for 'Front+Back', second for 'Edge'
|
127 |
+
for vertex in vertices:
|
128 |
+
for value in vertex:
|
129 |
+
vertex_bytearray.extend(struct.pack('f', value))
|
130 |
+
|
131 |
+
vertex_bytelen = len(vertex_bytearray)
|
132 |
+
mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
|
133 |
+
maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
|
134 |
+
|
135 |
+
# Normal data(NORMAL)
|
136 |
+
normals = [( 0.0, 0.0, 1.0)] * len(front_vertex_list[0]) + [( 0.0, 0.0, -1.0)] * len(back_vertex_list[0])
|
137 |
+
|
138 |
+
normal_bytearray = bytearray()
|
139 |
+
|
140 |
+
# Consider not only 'Front+Back' but also 'Edge'
|
141 |
+
for i in range(2): # first for 'Front+Back', second for 'Edge'
|
142 |
+
for normal in normals:
|
143 |
+
for value in normal:
|
144 |
+
normal_bytearray.extend(struct.pack('f', value))
|
145 |
+
|
146 |
+
normal_bytelen = len(normal_bytearray)
|
147 |
+
|
148 |
+
# Texture coordinates(TEXCOORD_0)
|
149 |
+
texcoord_0s = [
|
150 |
+
((vertex[0] + 1.0) / 2.0, 1.0 - ((vertex[1] + 1.0) / 2.0) ) for vertex in vertices
|
151 |
+
]
|
152 |
+
texcoord_0_bytearray = bytearray()
|
153 |
+
|
154 |
+
# 'Front+Back'
|
155 |
+
for texcoord_0 in texcoord_0s:
|
156 |
+
for value in texcoord_0:
|
157 |
+
texcoord_0_bytearray.extend(struct.pack('f', value))
|
158 |
+
# 'Edge'
|
159 |
+
for texcoord_0 in texcoord_0s:
|
160 |
+
for value in texcoord_0:
|
161 |
+
texcoord_0_bytearray.extend(struct.pack('f', 0.0))
|
162 |
+
|
163 |
+
texcoord_0_bytelen = len(texcoord_0_bytearray)
|
164 |
+
|
165 |
+
# Vertex indices
|
166 |
+
polygon = {
|
167 |
+
'vertices': np.array(front_vertex_list[0])[:, :2],
|
168 |
+
'segments': np.array([( i, (i + 1) % (len(front_vertex_list[0])) ) for i in range(len(front_vertex_list[0]))])
|
169 |
+
}
|
170 |
+
triangulate_result = triangle.triangulate(polygon, 'p')
|
171 |
+
|
172 |
+
vertex_indices = []
|
173 |
+
|
174 |
+
# Front
|
175 |
+
vertex_indices.extend(list(np.array(triangulate_result['triangles']).flatten()))
|
176 |
+
|
177 |
+
# Back
|
178 |
+
temp_list = list((np.array(triangulate_result['triangles'])+len(front_vertex_list[0])).flatten())
|
179 |
+
# Swap vertex indices to get Back vertex indices from Front vertex indices
|
180 |
+
def swap_elements(lst):
|
181 |
+
if len(lst) < 2:
|
182 |
+
return lst
|
183 |
+
for i in range(len(lst) - 1):
|
184 |
+
if i % 3 == 1:
|
185 |
+
lst[i], lst[i + 1] = lst[i + 1], lst[i]
|
186 |
+
|
187 |
+
return lst
|
188 |
+
|
189 |
+
temp_list = swap_elements(temp_list)
|
190 |
+
vertex_indices.extend(temp_list) # Back
|
191 |
+
|
192 |
+
# Edge
|
193 |
+
vertex_indices.extend(list(np.array([[len(vertices) + i,
|
194 |
+
len(vertices) + (i + 1) % len(front_vertex_list[0]),
|
195 |
+
len(vertices) + len(front_vertex_list[0]) + i]
|
196 |
+
for i in range(len(front_vertex_list[0]))]).flatten()))
|
197 |
+
vertex_indices.extend(list(np.array([[len(vertices) + len(front_vertex_list[0]) + (i + 1) % len(front_vertex_list[0]),
|
198 |
+
len(vertices) + len(front_vertex_list[0]) + i,
|
199 |
+
len(vertices) + (i + 1) % len(front_vertex_list[0])]
|
200 |
+
for i in range(len(front_vertex_list[0]))]).flatten()))
|
201 |
+
|
202 |
+
vertex_index_bytearray = bytearray()
|
203 |
+
for value in vertex_indices:
|
204 |
+
vertex_index_bytearray.extend(struct.pack('H', value))
|
205 |
+
vertex_index_bytelen = len(vertex_index_bytearray)
|
206 |
+
|
207 |
+
# Concatenate binar data
|
208 |
+
bytearray_list = [
|
209 |
+
vertex_bytearray,
|
210 |
+
normal_bytearray,
|
211 |
+
texcoord_0_bytearray,
|
212 |
+
vertex_index_bytearray,
|
213 |
+
img_bytearray,
|
214 |
+
]
|
215 |
+
bytelen_list = [
|
216 |
+
vertex_bytelen,
|
217 |
+
normal_bytelen,
|
218 |
+
texcoord_0_bytelen,
|
219 |
+
vertex_index_bytelen,
|
220 |
+
img_bytelen,
|
221 |
+
]
|
222 |
+
bytelen_cumsum_list = list(np.cumsum(bytelen_list))
|
223 |
+
bytelen_cumsum_list = list(map(lambda x: int(x), bytelen_cumsum_list))
|
224 |
+
|
225 |
+
all_bytearray = bytearray()
|
226 |
+
for temp_bytearray in bytearray_list:
|
227 |
+
all_bytearray.extend(temp_bytearray)
|
228 |
+
offset_list = [0] + bytelen_cumsum_list # First offset is 0
|
229 |
+
offset_list.pop() # Delete the last element
|
230 |
+
|
231 |
+
# Resource
|
232 |
+
resources = [GLBResource(data=all_bytearray)]
|
233 |
+
|
234 |
+
# Buffer
|
235 |
+
buffers = [Buffer(byteLength=len(all_bytearray))]
|
236 |
+
|
237 |
+
# BufferView
|
238 |
+
bufferViews = [
|
239 |
+
BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
|
240 |
+
BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
|
241 |
+
BufferView(buffer=0, byteOffset=offset_list[2], byteLength=bytelen_list[2], target=BufferTarget.ARRAY_BUFFER.value),
|
242 |
+
BufferView(buffer=0, byteOffset=offset_list[3], byteLength=bytelen_list[3], target=BufferTarget.ELEMENT_ARRAY_BUFFER.value),
|
243 |
+
BufferView(buffer=0, byteOffset=offset_list[4], byteLength=bytelen_list[4], target=None),
|
244 |
+
]
|
245 |
+
|
246 |
+
# Accessor
|
247 |
+
accessors = [
|
248 |
+
Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices*2), type=AccessorType.VEC3.value, max=maxs, min=mins),
|
249 |
+
Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals*2), type=AccessorType.VEC3.value, max=None, min=None),
|
250 |
+
Accessor(bufferView=2, componentType=ComponentType.FLOAT.value, count=len(texcoord_0s*2), type=AccessorType.VEC2.value, max=None, min=None),
|
251 |
+
Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None)
|
252 |
+
]
|
253 |
+
|
254 |
+
# Node
|
255 |
+
nodes = [
|
256 |
+
Node(mesh=0,rotation=None, scale=scale),
|
257 |
+
]
|
258 |
+
|
259 |
+
model = GLTFModel(
|
260 |
+
asset=asset,
|
261 |
+
buffers=buffers,
|
262 |
+
bufferViews=bufferViews,
|
263 |
+
accessors=accessors,
|
264 |
+
images=images,
|
265 |
+
samplers=samplers,
|
266 |
+
textures=textures,
|
267 |
+
materials=materials,
|
268 |
+
meshes=meshes,
|
269 |
+
nodes=nodes,
|
270 |
+
scene=scene,
|
271 |
+
scenes=scenes,
|
272 |
+
)
|
273 |
+
|
274 |
+
gltf = GLTF(model=model, resources=resources)
|
275 |
+
|
276 |
+
tmp_filename = uuid.uuid4().hex
|
277 |
+
model_path = f'../tmp/{tmp_filename}.glb'
|
278 |
+
|
279 |
+
gltf.export(model_path)
|
280 |
+
|
281 |
+
return model_path
|