1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """Support POV Scene Description Language snippets or full includes: import,
5 load, create or edit"""
8 from bpy
.props
import StringProperty
, BoolProperty
, CollectionProperty
9 from bpy_extras
.io_utils
import ImportHelper
10 from bpy
.utils
import register_class
, unregister_class
12 from mathutils
import Vector
13 from math
import pi
, sqrt
16 def export_custom_code(file):
17 """write all POV user defined custom code to exported file"""
18 # Write CurrentAnimation Frame for use in Custom POV Code
19 file.write("#declare CURFRAMENUM = %d;\n" % bpy
.context
.scene
.frame_current
)
20 # Change path and uncomment to add an animated include file by hand:
21 file.write('//#include "/home/user/directory/animation_include_file.inc"\n')
22 for txt
in bpy
.data
.texts
:
23 if txt
.pov
.custom_code
== "both":
24 # Why are the newlines needed?
26 file.write(txt
.as_string())
30 # ----------------------------------- IMPORT
33 class SCENE_OT_POV_Import(bpy
.types
.Operator
, ImportHelper
):
34 """Load Povray files"""
36 bl_idname
= "import_scene.pov"
37 bl_label
= "POV-Ray files (.pov/.inc)"
38 bl_options
= {"PRESET", "UNDO"}
39 COMPAT_ENGINES
= {"POVRAY_RENDER"}
43 files
: CollectionProperty(
44 type=bpy
.types
.OperatorFileListElement
, options
={"HIDDEN", "SKIP_SAVE"}
46 directory
: StringProperty(maxlen
=1024, subtype
="FILE_PATH", options
={"HIDDEN", "SKIP_SAVE"})
48 filename_ext
= {".pov", ".inc"}
49 filter_glob
: StringProperty(default
="*.pov;*.inc", options
={"HIDDEN"})
51 import_at_cur
: BoolProperty(
52 name
="Import at Cursor Location", description
="Ignore Object Matrix", default
=False
55 def execute(self
, context
):
56 from mathutils
import Matrix
68 name
= "Mesh2_%s" % suffix
74 cylinder_search
= False
77 tex_search
= False # XXX
83 # file_pov = bpy.path.abspath(self.filepath) # was used for single files
85 def mat_search(cache
):
89 for item
, value
in enumerate(cache
):
90 # if value == 'texture': # add more later
91 if value
== "pigment":
92 # Todo: create function for all color models.
93 # instead of current pass statements
94 # distinguish srgb from rgb into blend option
95 if cache
[item
+ 2] in {"rgb", "srgb"}:
97 elif cache
[item
+ 2] in {"rgbf", "srgbf"}:
99 elif cache
[item
+ 2] in {"rgbt", "srgbt"}:
102 float(cache
[item
+ 3]),
103 float(cache
[item
+ 4]),
104 float(cache
[item
+ 5]),
105 float(cache
[item
+ 6]),
107 except BaseException
as e
:
109 print("An exception occurred: {}".format(e
))
110 r
= g
= b
= t
= float(cache
[item
+ 2])
113 elif cache
[item
+ 2] in {"rgbft", "srgbft"}:
119 if colors
== [] or color
not in colors
:
121 name
= ob
.name
+ "_mat"
122 mat_names
.append(name
)
123 mat
= bpy
.data
.materials
.new(name
)
124 mat
.diffuse_color
= (r
, g
, b
)
125 mat
.pov
.alpha
= 1 - t
126 if mat
.pov
.alpha
!= 1:
127 mat
.pov
.use_transparency
= True
128 ob
.data
.materials
.append(mat
)
131 for i
, value
in enumerate(colors
):
133 ob
.data
.materials
.append(bpy
.data
.materials
[mat_names
[i
]])
135 for file in self
.files
:
136 print("Importing file: " + file.name
)
137 file_pov
= self
.directory
+ file.name
138 # Ignore any non unicode character
139 with
open(file_pov
, 'r', encoding
='utf-8', errors
="ignore") as infile
:
141 string
= line
.replace("{", " ")
142 string
= string
.replace("}", " ")
143 string
= string
.replace("<", " ")
144 string
= string
.replace(">", " ")
145 string
= string
.replace(",", " ")
147 # lenwords = len(lw) # Not used... why written?
149 if lw
[0] == "object":
152 if lw
[0] not in {"object", "matrix"}:
154 if lw
[0] in {"matrix"}:
169 matrixes
[index
] = value
171 with
open(file_pov
, 'r', encoding
='utf-8', errors
="ignore") as infile
:
173 S
= line
.replace("{", " { ")
174 S
= S
.replace("}", " } ")
175 S
= S
.replace(",", " ")
176 S
= S
.replace("<", "")
177 S
= S
.replace(">", " ")
178 S
= S
.replace("=", " = ")
179 S
= S
.replace(";", " ; ")
181 # lenS = len(S) # Not used... why written?
183 # -------- Primitives Import -------- #
199 # Y is height in most pov files, not z
200 bpy
.ops
.pov
.addcone(base
=r0
, cap
=r1
, height
=(y1
- y0
))
202 ob
.location
= (x0
, y0
, z0
)
216 bpy
.ops
.pov
.addplane()
236 # imported_corner_1=(x0, y0, z0)
237 # imported_corner_2 =(x1, y1, z1)
238 center
= ((x0
+ x1
) / 2, (y0
+ y1
) / 2, (z0
+ z1
) / 2)
248 if word
== "cylinder":
249 cylinder_search
= True
261 imported_cyl_loc
= (x0
, y0
, z0
)
262 imported_cyl_loc_cap
= (x1
, y1
, z1
)
266 vec
= Vector(imported_cyl_loc_cap
) - Vector(imported_cyl_loc
)
268 rot
= Vector((0, 0, 1)).rotation_difference(
270 ) # Rotation from Z axis.
271 trans
= rot
@ Vector( # XXX Not used, why written?
273 ) # Such that origin is at center of the base of the cylinder.
274 # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
275 scale_z
= sqrt((x1
- x0
) ** 2 + (y1
- y0
) ** 2 + (z1
- z0
) ** 2) / 2
276 bpy
.ops
.pov
.addcylinder(
278 imported_cyl_loc
=imported_cyl_loc
,
279 imported_cyl_loc_cap
=imported_cyl_loc_cap
,
282 ob
.location
= (x0
, y0
, z0
)
283 # todo: test and search where to add the below currently commented
284 # since Blender defers the evaluation until the results are needed.
285 # bpy.context.view_layer.update()
286 # as explained here: https://docs.blender.org/api/current/info_gotcha.html?highlight=gotcha#no-updates-after-setting-values
287 ob
.rotation_euler
= rot
.to_euler()
288 ob
.scale
= (1, 1, scale_z
)
290 # scale data rather than obj?
291 # bpy.ops.object.mode_set(mode='EDIT')
292 # bpy.ops.mesh.reveal()
293 # bpy.ops.mesh.select_all(action='SELECT')
294 # bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL')
295 # bpy.ops.mesh.hide(unselected=False)
296 # bpy.ops.object.mode_set(mode='OBJECT')
303 cylinder_search
= False
319 except BaseException
as e
:
321 print("An exception occurred: {}".format(e
))
322 x
= y
= z
= float(cache
[2])
324 bpy
.ops
.pov
.addsphere(R
=r
, imported_loc
=(x
, y
, z
))
326 ob
.location
= (x
, y
, z
)
330 sphere_search
= False
331 # -------- End Primitives Import -------- #
332 if word
== "#declare":
343 if word
in {"texture", ";"}:
346 if word
== "vertex_vectors":
357 for j
in range(int(lenverts
)):
361 verts
.append((float(cache
[x
]), float(cache
[y
]), float(cache
[z
])))
363 # if word == 'face_indices':
364 # faces_search = True
365 if word
== "texture_list": # XXX
366 tex_search
= True # XXX
369 word
not in {"texture_list", "texture", "{", "}", "face_indices"}
370 and not word
.isdigit()
372 pov_mats
.append(word
) # XXX
373 if word
== "face_indices":
374 tex_search
= False # XXX
386 var
= int(len(cache
) / lf
)
392 faces
.append((int(cache
[v0
]), int(cache
[v1
]), int(cache
[v2
])))
398 materials
.append((int(cache
[m
])))
399 faces
.append((int(cache
[v0
]), int(cache
[v1
]), int(cache
[v2
])))
408 (int(cache
[m0
]), int(cache
[m1
]), int(cache
[m2
]))
410 faces
.append((int(cache
[v0
]), int(cache
[v1
]), int(cache
[v2
])))
411 # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
412 # ob = object_utils.object_data_add(context, mesh, operator=None)
414 me
= bpy
.data
.meshes
.new(name
) # XXX
415 ob
= bpy
.data
.objects
.new(name
, me
) # XXX
416 bpy
.context
.collection
.objects
.link(ob
) # XXX
417 me
.from_pydata(verts
, [], faces
) # XXX
419 for mat
in bpy
.data
.materials
: # XXX
420 blend_mats
.append(mat
.name
) # XXX
421 for m_name
in pov_mats
: # XXX
422 if m_name
not in blend_mats
: # XXX
423 bpy
.data
.materials
.new(m_name
) # XXX
425 ob
.data
.materials
.append(bpy
.data
.materials
[m_name
]) # XXX
427 for idx
, val
in enumerate(materials
): # XXX
429 ob
.data
.polygons
[idx
].material_index
= val
# XXX
430 except TypeError: # XXX
431 ob
.data
.polygons
[idx
].material_index
= int(val
[0]) # XXX
433 blend_mats
= [] # XXX
438 if name
in matrixes
and not self
.import_at_cur
:
439 global_matrix
= Matrix
.Rotation(pi
/ 2.0, 4, "X")
440 ob
= bpy
.context
.object
441 matrix
= ob
.matrix_world
455 matrix
= global_matrix
* ob
.matrix_world
456 ob
.matrix_world
= matrix
460 # if word == 'pigment':
462 # #all indices have been incremented once to fit a bad test file
463 # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
467 # #all indices have been incremented once to fit alternate test file
468 # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
470 # except UnboundLocalError:
471 # # In case no transmit is specified ? put it to 0
472 # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
476 # color = (0.8,0.8,0.8,0)
479 # if colors == [] or (colors != [] and color not in colors):
480 # colors.append(color)
481 # name = ob.name+"_mat"
482 # mat_names.append(name)
483 # mat = bpy.data.materials.new(name)
484 # mat.diffuse_color = (r,g,b)
485 # mat.pov.alpha = 1-t
486 # if mat.pov.alpha != 1:
487 # mat.pov.use_transparency=True
488 # ob.data.materials.append(mat)
491 # for m in range(len(colors)):
492 # if color == colors[m]:
493 # ob.data.materials.append(bpy.data.materials[mat_names[m]])
495 # To keep Avogadro Camera angle:
496 # for obj in bpy.context.view_layer.objects:
497 # if obj.type == "CAMERA":
498 # track = obj.constraints.new(type = "TRACK_TO")
500 # track.track_axis ="TRACK_NEGATIVE_Z"
501 # track.up_axis = "UP_Y"
502 # obj.location = (0,0,0)
506 classes
= (SCENE_OT_POV_Import
,)
515 for cls
in reversed(classes
):
516 unregister_class(cls
)