1 # SPDX-FileCopyrightText: 2021-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """Support POV Scene Description Language snippets or full includes: import,
7 load, create or edit"""
10 from bpy
.props
import StringProperty
, BoolProperty
, CollectionProperty
11 from bpy_extras
.io_utils
import ImportHelper
12 from bpy
.utils
import register_class
, unregister_class
14 from mathutils
import Vector
15 from math
import pi
, sqrt
18 def export_custom_code(file):
19 """write all POV user defined custom code to exported file"""
20 # Write CurrentAnimation Frame for use in Custom POV Code
21 file.write("#declare CURFRAMENUM = %d;\n" % bpy
.context
.scene
.frame_current
)
22 # Change path and uncomment to add an animated include file by hand:
23 file.write('//#include "/home/user/directory/animation_include_file.inc"\n')
24 for txt
in bpy
.data
.texts
:
25 if txt
.pov
.custom_code
== "both":
26 # Why are the newlines needed?
28 file.write(txt
.as_string())
32 # ----------------------------------- IMPORT
35 class SCENE_OT_POV_Import(bpy
.types
.Operator
, ImportHelper
):
36 """Load Povray files"""
38 bl_idname
= "import_scene.pov"
39 bl_label
= "POV-Ray files (.pov/.inc)"
40 bl_options
= {"PRESET", "UNDO"}
41 COMPAT_ENGINES
= {"POVRAY_RENDER"}
45 files
: CollectionProperty(
46 type=bpy
.types
.OperatorFileListElement
, options
={"HIDDEN", "SKIP_SAVE"}
48 directory
: StringProperty(maxlen
=1024, subtype
="FILE_PATH", options
={"HIDDEN", "SKIP_SAVE"})
50 filename_ext
= {".pov", ".inc"}
51 filter_glob
: StringProperty(default
="*.pov;*.inc", options
={"HIDDEN"})
53 import_at_cur
: BoolProperty(
54 name
="Import at Cursor Location", description
="Ignore Object Matrix", default
=False
57 def execute(self
, context
):
58 from mathutils
import Matrix
70 name
= "Mesh2_%s" % suffix
76 cylinder_search
= False
79 tex_search
= False # XXX
85 # file_pov = bpy.path.abspath(self.filepath) # was used for single files
87 def mat_search(cache
):
91 for item
, value
in enumerate(cache
):
92 # if value == 'texture': # add more later
93 if value
== "pigment":
94 # Todo: create function for all color models.
95 # instead of current pass statements
96 # distinguish srgb from rgb into blend option
97 if cache
[item
+ 2] in {"rgb", "srgb"}:
99 elif cache
[item
+ 2] in {"rgbf", "srgbf"}:
101 elif cache
[item
+ 2] in {"rgbt", "srgbt"}:
104 float(cache
[item
+ 3]),
105 float(cache
[item
+ 4]),
106 float(cache
[item
+ 5]),
107 float(cache
[item
+ 6]),
109 except BaseException
as e
:
111 print("An exception occurred: {}".format(e
))
112 r
= g
= b
= t
= float(cache
[item
+ 2])
115 elif cache
[item
+ 2] in {"rgbft", "srgbft"}:
121 if colors
== [] or color
not in colors
:
123 name
= ob
.name
+ "_mat"
124 mat_names
.append(name
)
125 mat
= bpy
.data
.materials
.new(name
)
126 mat
.diffuse_color
= (r
, g
, b
)
127 mat
.pov
.alpha
= 1 - t
128 if mat
.pov
.alpha
!= 1:
129 mat
.pov
.use_transparency
= True
130 ob
.data
.materials
.append(mat
)
133 for i
, value
in enumerate(colors
):
135 ob
.data
.materials
.append(bpy
.data
.materials
[mat_names
[i
]])
137 for file in self
.files
:
138 print("Importing file: " + file.name
)
139 file_pov
= self
.directory
+ file.name
140 # Ignore any non unicode character
141 with
open(file_pov
, 'r', encoding
='utf-8', errors
="ignore") as infile
:
143 string
= line
.replace("{", " ")
144 string
= string
.replace("}", " ")
145 string
= string
.replace("<", " ")
146 string
= string
.replace(">", " ")
147 string
= string
.replace(",", " ")
149 # lenwords = len(lw) # Not used... why written?
151 if lw
[0] == "object":
154 if lw
[0] not in {"object", "matrix"}:
156 if lw
[0] in {"matrix"}:
171 matrixes
[index
] = value
173 with
open(file_pov
, 'r', encoding
='utf-8', errors
="ignore") as infile
:
175 S
= line
.replace("{", " { ")
176 S
= S
.replace("}", " } ")
177 S
= S
.replace(",", " ")
178 S
= S
.replace("<", "")
179 S
= S
.replace(">", " ")
180 S
= S
.replace("=", " = ")
181 S
= S
.replace(";", " ; ")
183 # lenS = len(S) # Not used... why written?
185 # -------- Primitives Import -------- #
201 # Y is height in most pov files, not z
202 bpy
.ops
.pov
.addcone(base
=r0
, cap
=r1
, height
=(y1
- y0
))
204 ob
.location
= (x0
, y0
, z0
)
218 bpy
.ops
.pov
.addplane()
238 # imported_corner_1=(x0, y0, z0)
239 # imported_corner_2 =(x1, y1, z1)
240 center
= ((x0
+ x1
) / 2, (y0
+ y1
) / 2, (z0
+ z1
) / 2)
250 if word
== "cylinder":
251 cylinder_search
= True
263 imported_cyl_loc
= (x0
, y0
, z0
)
264 imported_cyl_loc_cap
= (x1
, y1
, z1
)
268 vec
= Vector(imported_cyl_loc_cap
) - Vector(imported_cyl_loc
)
270 rot
= Vector((0, 0, 1)).rotation_difference(
272 ) # Rotation from Z axis.
273 trans
= rot
@ Vector( # XXX Not used, why written?
275 ) # Such that origin is at center of the base of the cylinder.
276 # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
277 scale_z
= sqrt((x1
- x0
) ** 2 + (y1
- y0
) ** 2 + (z1
- z0
) ** 2) / 2
278 bpy
.ops
.pov
.addcylinder(
280 imported_cyl_loc
=imported_cyl_loc
,
281 imported_cyl_loc_cap
=imported_cyl_loc_cap
,
284 ob
.location
= (x0
, y0
, z0
)
285 # todo: test and search where to add the below currently commented
286 # since Blender defers the evaluation until the results are needed.
287 # bpy.context.view_layer.update()
288 # as explained here: https://docs.blender.org/api/current/info_gotcha.html?highlight=gotcha#no-updates-after-setting-values
289 ob
.rotation_euler
= rot
.to_euler()
290 ob
.scale
= (1, 1, scale_z
)
292 # scale data rather than obj?
293 # bpy.ops.object.mode_set(mode='EDIT')
294 # bpy.ops.mesh.reveal()
295 # bpy.ops.mesh.select_all(action='SELECT')
296 # bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL')
297 # bpy.ops.mesh.hide(unselected=False)
298 # bpy.ops.object.mode_set(mode='OBJECT')
305 cylinder_search
= False
321 except BaseException
as e
:
323 print("An exception occurred: {}".format(e
))
324 x
= y
= z
= float(cache
[2])
326 bpy
.ops
.pov
.addsphere(R
=r
, imported_loc
=(x
, y
, z
))
328 ob
.location
= (x
, y
, z
)
332 sphere_search
= False
333 # -------- End Primitives Import -------- #
334 if word
== "#declare":
345 if word
in {"texture", ";"}:
348 if word
== "vertex_vectors":
359 for j
in range(int(lenverts
)):
363 verts
.append((float(cache
[x
]), float(cache
[y
]), float(cache
[z
])))
365 # if word == 'face_indices':
366 # faces_search = True
367 if word
== "texture_list": # XXX
368 tex_search
= True # XXX
371 word
not in {"texture_list", "texture", "{", "}", "face_indices"}
372 and not word
.isdigit()
374 pov_mats
.append(word
) # XXX
375 if word
== "face_indices":
376 tex_search
= False # XXX
388 var
= int(len(cache
) / lf
)
394 faces
.append((int(cache
[v0
]), int(cache
[v1
]), int(cache
[v2
])))
400 materials
.append((int(cache
[m
])))
401 faces
.append((int(cache
[v0
]), int(cache
[v1
]), int(cache
[v2
])))
410 (int(cache
[m0
]), int(cache
[m1
]), int(cache
[m2
]))
412 faces
.append((int(cache
[v0
]), int(cache
[v1
]), int(cache
[v2
])))
413 # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
414 # ob = object_utils.object_data_add(context, mesh, operator=None)
416 me
= bpy
.data
.meshes
.new(name
) # XXX
417 ob
= bpy
.data
.objects
.new(name
, me
) # XXX
418 bpy
.context
.collection
.objects
.link(ob
) # XXX
419 me
.from_pydata(verts
, [], faces
) # XXX
421 for mat
in bpy
.data
.materials
: # XXX
422 blend_mats
.append(mat
.name
) # XXX
423 for m_name
in pov_mats
: # XXX
424 if m_name
not in blend_mats
: # XXX
425 bpy
.data
.materials
.new(m_name
) # XXX
427 ob
.data
.materials
.append(bpy
.data
.materials
[m_name
]) # XXX
429 for idx
, val
in enumerate(materials
): # XXX
431 ob
.data
.polygons
[idx
].material_index
= val
# XXX
432 except TypeError: # XXX
433 ob
.data
.polygons
[idx
].material_index
= int(val
[0]) # XXX
435 blend_mats
= [] # XXX
440 if name
in matrixes
and not self
.import_at_cur
:
441 global_matrix
= Matrix
.Rotation(pi
/ 2.0, 4, "X")
442 ob
= bpy
.context
.object
443 matrix
= ob
.matrix_world
457 matrix
= global_matrix
* ob
.matrix_world
458 ob
.matrix_world
= matrix
462 # if word == 'pigment':
464 # #all indices have been incremented once to fit a bad test file
465 # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
469 # #all indices have been incremented once to fit alternate test file
470 # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
472 # except UnboundLocalError:
473 # # In case no transmit is specified ? put it to 0
474 # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
478 # color = (0.8,0.8,0.8,0)
481 # if colors == [] or (colors != [] and color not in colors):
482 # colors.append(color)
483 # name = ob.name+"_mat"
484 # mat_names.append(name)
485 # mat = bpy.data.materials.new(name)
486 # mat.diffuse_color = (r,g,b)
487 # mat.pov.alpha = 1-t
488 # if mat.pov.alpha != 1:
489 # mat.pov.use_transparency=True
490 # ob.data.materials.append(mat)
493 # for m in range(len(colors)):
494 # if color == colors[m]:
495 # ob.data.materials.append(bpy.data.materials[mat_names[m]])
497 # To keep Avogadro Camera angle:
498 # for obj in bpy.context.view_layer.objects:
499 # if obj.type == "CAMERA":
500 # track = obj.constraints.new(type = "TRACK_TO")
502 # track.track_axis ="TRACK_NEGATIVE_Z"
503 # track.up_axis = "UP_Y"
504 # obj.location = (0,0,0)
508 classes
= (SCENE_OT_POV_Import
,)
517 for cls
in reversed(classes
):
518 unregister_class(cls
)