Skip to content

Commit f6bee58

Browse files
committed
add the blender export script
1 parent cf67827 commit f6bee58

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed

readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ ID origin orientation height a b c shape col
333333
sp vec3 vec3 float float float float float u8vec3 material float/vec2
334334
```
335335

336+
I also included the [Blender Python Script](resources/rt_export_script_v004.py) I used to create these scenes in Blender and export them into the .rt file format. It's incomplete though and still requires some manual edits and additions.
337+
336338
# Assets
337339

338340
### HDRi Environment Maps

resources/rt_export_script_v004.py

+257
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import bpy
2+
import math
3+
import mathutils
4+
5+
# Prepare the output file
6+
output_file_path = bpy.path.abspath("/Users/bwerner/Documents/projects/rank04/miniRT/git_miniRT/scenes/scene.rt")
7+
8+
def get_material_info(material):
9+
# Default values if the material does not use nodes
10+
color = (204, 204, 204)
11+
metallic = 0.0
12+
roughness = 0.5
13+
ior = 1.45 # Default IOR for glass
14+
transmission = 0.0
15+
emission_strength = 0.0
16+
emission_color = (0, 0, 0)
17+
18+
if material.node_tree:
19+
for node in material.node_tree.nodes:
20+
if node.type == 'BSDF_PRINCIPLED':
21+
# Get base color
22+
base_color = node.inputs['Base Color'].default_value
23+
color = (
24+
int(base_color[0] * 255),
25+
int(base_color[1] * 255),
26+
int(base_color[2] * 255)
27+
)
28+
metallic = node.inputs['Metallic'].default_value
29+
roughness = node.inputs['Roughness'].default_value
30+
ior = node.inputs['IOR'].default_value
31+
# Handle transmission with safe access
32+
transmission = node.inputs[17].default_value
33+
emission_strength = node.inputs[27].default_value
34+
emission_color = node.inputs[26].default_value
35+
emission_color = (
36+
int(emission_color[0] * 255),
37+
int(emission_color[1] * 255),
38+
int(emission_color[2] * 255)
39+
)
40+
41+
return {
42+
'name': material.name.replace(' ', '_'),
43+
'color': color,
44+
'metallic': metallic,
45+
'roughness': roughness,
46+
'ior': ior,
47+
'transmission': transmission,
48+
'emission_strength': emission_strength,
49+
'emission_color': emission_color,
50+
}
51+
52+
def is_sphere(obj):
53+
return obj.type == 'MESH' and 'Sphere' in obj.name
54+
55+
def get_sphere_info(obj):
56+
location = (round(obj.location.x, 10),
57+
round(obj.location.y, 10),
58+
round(obj.location.z, 10))
59+
60+
dimensions = obj.dimensions
61+
largest_dimension = max(dimensions)
62+
63+
material_name = "None"
64+
65+
if obj.active_material:
66+
material_name = obj.active_material.name
67+
68+
return location, largest_dimension, material_name
69+
70+
def is_plane(obj):
71+
return obj.type == 'MESH' and 'Plane' in obj.name
72+
73+
def get_plane_info(obj):
74+
location = (round(obj.location.x, 10),
75+
round(obj.location.y, 10),
76+
round(obj.location.z, 10))
77+
78+
normal = obj.matrix_world.to_3x3() @ mathutils.Vector((0.0, 0.0, 1.0))
79+
normal = (round(normal.x, 10),
80+
round(normal.y, 10),
81+
round(normal.z, 10))
82+
83+
material_name = "None"
84+
85+
if obj.active_material:
86+
material_name = obj.active_material.name
87+
88+
return location, normal, material_name
89+
90+
def is_cylinder(obj):
91+
return obj.type == 'MESH' and 'Cylinder' in obj.name
92+
93+
def get_cylinder_info(obj):
94+
location = (round(obj.location.x, 10),
95+
round(obj.location.y, 10),
96+
round(obj.location.z, 10))
97+
98+
diameter = obj.dimensions.x
99+
height = obj.dimensions.z
100+
101+
orientation = obj.matrix_world.to_3x3() @ mathutils.Vector((0.0, 0.0, 1.0))
102+
orientation = (round(orientation.x, 10),
103+
round(orientation.y, 10),
104+
round(orientation.z, 10))
105+
106+
material_name = "None"
107+
108+
if obj.active_material:
109+
material_name = obj.active_material.name
110+
111+
return location, orientation, diameter, height, material_name
112+
113+
def is_pointlight(obj):
114+
return obj.type == 'LIGHT' and obj.data.type == 'POINT'
115+
116+
def get_pointlight_info(obj):
117+
location = (round(obj.location.x, 10),
118+
round(obj.location.y, 10),
119+
round(obj.location.z, 10))
120+
121+
power = obj.data.energy
122+
123+
color = obj.data.color
124+
color = (
125+
int(color[0] * 255),
126+
int(color[1] * 255),
127+
int(color[2] * 255)
128+
)
129+
130+
return location, power, color
131+
132+
def is_camera(obj):
133+
return obj.type == 'CAMERA' and obj.data.type == 'PERSP'
134+
135+
def get_camera_info(obj):
136+
location = (round(obj.location.x, 10),
137+
round(obj.location.y, 10),
138+
round(obj.location.z, 10))
139+
140+
fov = math.degrees(obj.data.angle)
141+
142+
direction = obj.matrix_world.to_3x3() @ mathutils.Vector((0.0, 0.0, -1.0))
143+
144+
direction = (round(direction.x, 10),
145+
round(direction.y, 10),
146+
round(direction.z, 10))
147+
148+
return location, fov, direction
149+
150+
def get_world_background_info():
151+
world = bpy.context.scene.world
152+
if not world or not world.node_tree:
153+
color = (0, 0, 0)
154+
strength = 0
155+
return color, strength
156+
157+
nodes = world.node_tree.nodes
158+
for node in nodes:
159+
if node.type == 'BACKGROUND':
160+
color = node.inputs['Color'].default_value
161+
color = (
162+
int(color[0] * 255),
163+
int(color[1] * 255),
164+
int(color[2] * 255)
165+
)
166+
strength = node.inputs['Strength'].default_value
167+
return color, strength
168+
169+
color = (0, 0, 0)
170+
strength = 0
171+
return color, strength
172+
173+
# Lists to hold object information
174+
cameras = []
175+
pointlights = []
176+
spheres = []
177+
planes = []
178+
cylinders = []
179+
180+
with open(output_file_path, 'w') as file:
181+
# Write material information at the top of the file
182+
file.write(f"# MATERIALS\n")
183+
file.write(f"# name color metallic roughness ior transmission emission_strength emission_color\n")
184+
for material in bpy.data.materials:
185+
if material.name != 'Dots Stroke':
186+
material_info = get_material_info(material)
187+
file.write(f"mat\t{material_info['name']:<30}"
188+
f"{material_info['color'][0]:>3},{material_info['color'][1]:>3},{material_info['color'][2]:>3} "
189+
f"{material_info['metallic']:<15.6f}"
190+
f"{material_info['roughness']:<15.6f}"
191+
f"{material_info['ior']:<15.6f}"
192+
f"{material_info['transmission']:<15.6f}"
193+
f"{material_info['emission_strength']:<20.6f}"
194+
f"{material_info['emission_color'][0]:>3},{material_info['emission_color'][1]:>3},{material_info['emission_color'][2]:>3}\n")
195+
196+
# Collect object information
197+
for obj in bpy.context.scene.objects:
198+
if is_camera(obj):
199+
location, fov, direction = get_camera_info(obj)
200+
cameras.append(f"C\t{location[0]:>12.6f},{location[1]:>12.6f},{location[2]:>12.6f} "
201+
f"{direction[0]:>12.6f},{direction[1]:>12.6f},{direction[2]:>12.6f} "
202+
f"{fov:<15.6f}\n")
203+
elif is_pointlight(obj):
204+
location, power, color = get_pointlight_info(obj)
205+
pointlights.append(f"l\t{location[0]:>12.6f},{location[1]:>12.6f},{location[2]:>12.6f} "
206+
f"{power:<15.6f}"
207+
f"{color[0]:>3},{color[1]:>3},{color[2]:>3}\n")
208+
elif is_sphere(obj):
209+
location, largest_dimension, material_name = get_sphere_info(obj)
210+
spheres.append(f"sp\t{location[0]:>12.6f},{location[1]:>12.6f},{location[2]:>12.6f} "
211+
f"{largest_dimension:<15.6f}"
212+
f"{material_name:<20}\n")
213+
elif is_plane(obj):
214+
location, normal, material_name = get_plane_info(obj)
215+
planes.append(f"pl\t{location[0]:>12.6f},{location[1]:>12.6f},{location[2]:>12.6f} "
216+
f"{normal[0]:>12.6f},{normal[1]:>12.6f},{normal[2]:>12.6f} "
217+
f"{material_name:<20}\n")
218+
elif is_cylinder(obj):
219+
location, orientation, diameter, height, material_name = get_cylinder_info(obj)
220+
cylinders.append(f"cy\t{location[0]:>12.6f},{location[1]:>12.6f},{location[2]:>12.6f} "
221+
f"{orientation[0]:>12.6f},{orientation[1]:>12.6f},{orientation[2]:>12.6f} "
222+
f"{diameter:<15.6f}"
223+
f"{height:<15.6f}"
224+
f"{material_name:<20}\n")
225+
226+
# Write sorted object information to file
227+
if spheres:
228+
file.write(f"\n# SPHERES")
229+
file.write(f"\n#\torigin diameter material\n")
230+
for sphere in spheres:
231+
file.write(sphere)
232+
if cylinders:
233+
file.write(f"\n# CYLINDERS")
234+
file.write(f"\n#\torigin orientation diameter height material\n")
235+
for cylinder in cylinders:
236+
file.write(cylinder)
237+
if planes:
238+
file.write(f"\n# PLANES")
239+
file.write(f"\n#\torigin normal material\n")
240+
for plane in planes:
241+
file.write(plane)
242+
if pointlights:
243+
file.write(f"\n# LIGHTS")
244+
file.write(f"\n#\torigin power color\n")
245+
for pointlight in pointlights:
246+
file.write(pointlight)
247+
if cameras:
248+
file.write(f"\n# CAMERA")
249+
file.write(f"\n#\torigin orientation fiew of view\n")
250+
for camera in cameras:
251+
file.write(camera)
252+
253+
# World background information
254+
file.write(f"\n# AMBIENT")
255+
file.write(f"\n#\tstrength color\n")
256+
color, strength = get_world_background_info()
257+
file.write(f"A\t{strength:<20.6f}{color[0]:>3},{color[1]:>3},{color[2]:>3}\n")

0 commit comments

Comments
 (0)