/
MixedRealityModelingTools.py
438 lines (339 loc) · 14.9 KB
/
MixedRealityModelingTools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
bl_info = {
"name": "MixedRealityModelingTools",
"author": "HoloMoto",
"version": (0, 0,9,85),
"blender": (3, 4, 0),
"location": "View3D > UI > My Panel",
"description": "MixedRealityModelingTools",
"warning": "",
"doc_url": "",
"category": "3D View",
}
#version 0.0.98
import bpy
import socket
import threading
import msgpack
import struct
import base64
import math
from mathutils import Quaternion
import numpy as np
#MainSettings
class MixedRealityModelingToolsOperator(bpy.types.Operator):
bl_idname = "object.hello_world"
bl_label = "Start Server"
server_text_input: bpy.props.StringProperty(name="Server")
port_text_input: bpy.props.StringProperty(name="port")
def execute(self, context):
server_address = self.server_text_input
port = int(self.port_text_input)
server_thread = ServerThread(server_address, port)
server_thread.start()
self.report({'INFO'}, f"Server started on {server_address}:{port}")
return {'FINISHED'}
class MixedRealityModelingToolsSettingsPanel(bpy.types.Panel):
bl_label = "MRModelingTools"
bl_idname = "OBJECT_PT_settings_ModelingTools"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'MRMT'
def draw(self, context):
layout = self.layout
layout.prop(context.scene, "server_text_input")
layout.prop(context.scene, "port_text_input")
layout.operator("object.start_server")
class StartServerOperator(bpy.types.Operator):
bl_idname = "object.start_server"
bl_label = "Start Server"
def execute(self, context):
# サーバーアドレスとポート番号を取得
server_address = context.scene.server_text_input
port = int(context.scene.port_text_input)
# サーバースレッドを作成して開始
server_thread = ServerThread(server_address, port)
server_thread.start()
# メッセージを表示
self.report({'INFO'}, f"Server started on {server_address}:{port}")
return {'FINISHED'}
class ClientHandler(threading.Thread):
def __init__(self, client_socket):
threading.Thread.__init__(self)
self.client_socket = client_socket
def run(self):
try:
while True:
request = self.client_socket.recv(1024)
if not request:
break
process_received_data(request)
print("[*] Received:", request)
except Exception as e:
print(f"Error while handling client: {e}")
finally:
self.client_socket.close()
class ServerThread(threading.Thread):
def __init__(self, bind_ip, bind_port):
threading.Thread.__init__(self)
self.bind_ip = bind_ip
self.bind_port = bind_port
self.clients = []
def run(self):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((self.bind_ip, self.bind_port))
server.listen(5)
print("[*] Listening on %s:%d" % (self.bind_ip, self.bind_port))
while True:
client, addr = server.accept()
print("[*] Accepted connection from: %s:%d" % (addr[0], addr[1]))
client_handler = ClientHandler(client)
client_handler.start()
self.clients.append(client)
def close_all_clients(self):
for client in self.clients:
client.close()
# Run the server in a new thread
server_thread = ServerThread("0.0.0.0", 9998)
server_thread.start()
def send_message_to_unity(message):
for client in server_thread.clients:
try:
client.send(message.encode())
except Exception as e:
print(f"Error while sending message to client: {e}")
def send_mesh_data_to_unity(mesh_data):
vertices, triangles, normals, uvs = mesh_data
# Convert numpy arrays to list
vertices_list = np.array(vertices, dtype='<f4').flatten().tolist()
triangles_list = np.array(triangles, dtype='<i4').flatten().tolist()
normals_list = np.array(normals, dtype='<f4').flatten().tolist()
uvs_list = np.array(uvs, dtype='<f4').flatten().tolist()
# Debug: Print UV values
print("UV Values:")
for i in range(0, len(uvs_list), 2):
uv_x = uvs_list[i]
uv_y = uvs_list[i + 1]
print(f"UV[{i//2}]: ({uv_x}, {uv_y})")
MESH_HEADER = "MESH" # header of Mesh Data
# Build data as Dictionary
data_dict = {
'header': MESH_HEADER,
'objectname' : bpy.context.view_layer.objects.active.name,
'vertices': vertices_list,
'triangles': triangles_list,
'normals': normals_list,
'uvs': uvs_list
}
# serialize MessagePack
serialized_mesh_data = msgpack.packb(data_dict)
#print(f"Serialized data (bytes): {serialized_mesh_data.hex()}")
#Verification
#verification_mesh_data(serialized_mesh_data)
# Check Deserialization
try:
deserialized_data = msgpack.unpackb(serialized_mesh_data)
print("Deserialization success!")
#print(deserialized_data)
except Exception as e:
print(f"Deserialization error: {e}")
return
for client in server_thread.clients:
try:
client.sendall(serialized_mesh_data)
print(serialized_mesh_data)
except Exception as e:
print(f"Error while sending mesh data to client: {e}")
class SimpleOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.send_message"
bl_label = "Send Mesh"
def execute(self, context):
mesh_data = get_mesh_data()
send_mesh_data_to_unity(mesh_data)
return {'FINISHED'}
class MaterialSenderOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.send_mat"
bl_label = "Send Mat"
send_base_color_texture: bpy.props.BoolProperty(name="Send BaseColor Texture", default=True)
def execute(self, context):
mat_data = get_material_data()
send_material_data_to_unity(mat_data , self.send_base_color_texture)
return{'FINISHED'}
class CustomPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "MixedRealityModelingTools for"
bl_idname = "OBJECT_PT_hello"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
def draw(self, context):
layout = self.layout
layout.operator("object.send_message")
layout.prop(context.scene, "MRMT_AutoUpdateToggle")
layout.operator("object.send_mat")
layout.prop(context.scene, "send_base_color_texture")
bpy.utils.register_class(MixedRealityModelingToolsOperator)
bpy.types.Scene.server_text_input = bpy.props.StringProperty(name="Server",default = "0.0.0.0")
bpy.types.Scene.port_text_input = bpy.props.StringProperty(name = "Port",default = "9998")
bpy.utils.register_class(MixedRealityModelingToolsSettingsPanel)
bpy.utils.register_class(StartServerOperator)
bpy.utils.register_class(SimpleOperator)
bpy.utils.register_class(CustomPanel)
bpy.types.Scene.MRMT_AutoUpdateToggle = bpy.props.BoolProperty(name="Auto Update(/3sec)",default = False)
bpy.utils.register_class(MaterialSenderOperator)
bpy.types.Scene.send_base_color_texture = bpy.props.BoolProperty(name="Send BaseColor Texture", default=False)
def get_mesh_data():
obj = bpy.context.view_layer.objects.active
print(f"Active object name: {obj.name}")
# Add Triangulate modifier
triangulate_mod = obj.modifiers.new(name="Triangulate", type='TRIANGULATE')
triangulate_mod.keep_custom_normals = True
triangulate_mod.quad_method = 'BEAUTY'
triangulate_mod.ngon_method = 'BEAUTY'
# Apply modifiers and get the new mesh data
bpy.context.view_layer.update()
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_eval = obj.evaluated_get(depsgraph)
temp_mesh = bpy.data.meshes.new_from_object(obj_eval)
#bpy.ops.mesh.customdata_custom_splitnormals_clear()
# Get vertices and triangles
vertices = [[v.co.x, v.co.y, v.co.z] for v in temp_mesh.vertices]
triangles = []
for p in temp_mesh.polygons:
triangles.extend(p.vertices)
uvs = []
for p in temp_mesh.polygons:
for loop_index in p.loop_indices:
uv = temp_mesh.uv_layers.active.data[loop_index].uv
uvs.extend([uv.x, uv.y])
# Remove Triangulate modifier
obj.modifiers.remove(triangulate_mod)
# Get normals
normals = [[v.normal.x, v.normal.y, v.normal.z] for v in temp_mesh.vertices]
# Don't forget to remove the temporary mesh data
bpy.data.meshes.remove(temp_mesh)
print(f"Mesh data generated: vertices={len(vertices)}, triangles={len(triangles)}, normals={len(normals)}, uvs={len(uvs)}")
return (vertices, triangles, normals ,uvs)
def verification_mesh_data(serialized_mesh_data):
data_dict = msgpack.unpackb(serialized_mesh_data)
# 2. データから頂点、三角形、法線のリストを取得
vertices = [(data_dict['vertices'][i], data_dict['vertices'][i+1], data_dict['vertices'][i+2]) for i in range(0, len(data_dict['vertices']), 3)]
triangles = [(data_dict['triangles'][i], data_dict['triangles'][i+1], data_dict['triangles'][i+2]) for i in range(0, len(data_dict['triangles']), 3)]
normals = [(data_dict['normals'][i], data_dict['normals'][i+1], data_dict['normals'][i+2]) for i in range(0, len(data_dict['normals']), 3)]
# 3. 新しいメッシュを作成
mesh = bpy.data.meshes.new(name="VerifiedMesh")
mesh.from_pydata(vertices, [], triangles)
# 法線を設定 (オプション: 必要に応じてコメントアウトしても良い)
mesh.normals_split_custom_set_from_vertices(normals)
mesh.use_auto_smooth = True
# 4. メッシュをシーンに追加
obj = bpy.data.objects.new("VerifiedMeshObject", mesh)
bpy.context.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
return obj
def get_material_data():
selected_objects = bpy.context.selected_objects
material_names = []
for obj in selected_objects:
if obj.type == 'MESH': # メッシュオブジェクトのみ処理する
for slot in obj.material_slots:
if slot.material:
material_names.append(slot.material.name)
return material_names
def send_material_data_to_unity(mat_data,send_base_color_texture):
for selected_material_name in mat_data:
selected_material = bpy.data.materials.get(selected_material_name)
MAT_HEADER = "MATE" #Header of Material Data
if selected_material:
base_color_node = selected_material.node_tree.nodes.get("Principled BSDF")
if base_color_node:
base_color = base_color_node.inputs["Base Color"].default_value
print(f"Material: {selected_material_name}")
print(f"Base Color (RGBA): ({base_color[0]}, {base_color[1]}, {base_color[2]}, {base_color[3]})")
base_metallic = base_color_node.inputs["Metallic"].default_value
print(f"Metallic:{base_metallic}")
material_smoothness = base_color_node.inputs['Roughness'].default_value
print(f"Smooth:{material_smoothness}")
if send_base_color_texture:
get_texture(selected_material)
rgba = list(base_color)
rgba_list = np.array(rgba, dtype='<f4').flatten().tolist()
mat_data_dict = {
'header': MAT_HEADER,
'materialname': [selected_material_name],
'rgba': rgba_list,
'metal': base_metallic,
'smooth': material_smoothness
}
serialized_mat_data = msgpack.packb(mat_data_dict)
print(serialized_mat_data)
print("material_Send")
for client in server_thread.clients:
try:
client.sendall(serialized_mat_data)
except Exception as e:
print(f"Error while sending mesh data to client: {e}")
else:
print("Invalid material name")
print(f"Error while sending mesh data to client: {e}")
def get_texture(material):
if material.use_nodes: # check used node
nodes = material.node_tree.nodes# get node tree
for node in nodes:#ノードの数実行
if node.type == 'BSDF_PRINCIPLED': # プリンシパルBSDFシェーダーノードを検索
base_color_socket = node.inputs["Base Color"]
if base_color_socket.is_linked: # BaseColorにリンクがあるかチェック
texture_node = base_color_socket.links[0].from_node
if texture_node.type == 'TEX_IMAGE': # テクスチャノードを検索
texture = texture_node.image
texture_path = bpy.path.abspath(texture_node.image.filepath)
send_texture_data(texture_path,texture.name)
def send_texture_data(texture_path,texture_name):
IMAGE_HEADER = "IMGE"
with open(texture_path, 'rb') as f:
image_data = f.read()
image_data_base64 = base64.b64encode(image_data).decode('utf-8')
data_dict = {
'header': IMAGE_HEADER,
'imagename': texture_name,
'imagedata': image_data_base64
}
serialized_image_data = msgpack.packb(data_dict)
for client in server_thread.clients:
try:
print(serialized_image_data)
client.sendall(serialized_image_data)
except Exception as e:
print(f"Error while sending image data to client: {e}")
def update_camera_position(new_position,new_rotation):
camera = bpy.data.objects.get("Camera") # カメラオブジェクトの名前を指定
if camera:
print(new_rotation)
camera.location = new_position
camera_rotation_rad = [math.radians(angle) for angle in new_rotation]
camera.rotation_euler = camera_rotation_rad
bpy.context.view_layer.update()
print("Camera position updated:", new_position)
else:
print("Camera not found in the scene.")
def process_received_data(request):
try:
data = msgpack.unpackb(request, raw=False)
header = data[0]
if header == 'UCAM':
position = data[1]
rotation = data[2]
update_camera_position(position,rotation)
elif header == 'REQM':# RequestMextdeta
obj = bpy.context.view_layer.objects.active
if obj and obj.type == 'MESH':
mesh_data = get_mesh_data() # 新しいメッシュデータを取得
send_mesh_data_to_unity(mesh_data)
else:
print("Active object is not a mesh.")
else:
print("Unknown header:", header)
except Exception as e:
print(f"Error while processing received data: {e}")