1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Author: Pontiac, Fourmadmen, Dreampainter
6 from bpy
.types
import Operator
7 from mathutils
import (
11 from math
import cos
, sin
, pi
12 from bpy
.props
import (
18 from bpy_extras
import object_utils
20 # Create a new mesh (object) from verts/edges/faces.
21 # verts/edges/faces ... List of vertices/edges/faces for the
22 # new mesh (as used in from_pydata)
23 # name ... Name of the new mesh (& object)
25 def create_mesh_object(context
, self
, verts
, edges
, faces
, name
):
28 mesh
= bpy
.data
.meshes
.new(name
)
30 # Make a mesh from a list of verts/edges/faces.
31 mesh
.from_pydata(verts
, edges
, faces
)
33 # Update mesh geometry after adding stuff.
36 from bpy_extras
import object_utils
37 return object_utils
.object_data_add(context
, mesh
, operator
=self
)
40 # A very simple "bridge" tool.
42 def createFaces(vertIdx1
, vertIdx2
, closed
=False, flipped
=False):
45 if not vertIdx1
or not vertIdx2
:
48 if len(vertIdx1
) < 2 and len(vertIdx2
) < 2:
52 if (len(vertIdx1
) != len(vertIdx2
)):
53 if (len(vertIdx1
) == 1 and len(vertIdx2
) > 1):
61 # Bridge the start with the end
68 face
.append(vertIdx1
[total
- 1])
72 face
= [vertIdx2
[0], vertIdx1
[0]]
74 face
.append(vertIdx1
[total
- 1])
75 face
.append(vertIdx2
[total
- 1])
78 # Bridge the rest of the faces
79 for num
in range(total
- 1):
82 face
= [vertIdx2
[num
], vertIdx1
[0], vertIdx2
[num
+ 1]]
84 face
= [vertIdx2
[num
], vertIdx1
[num
],
85 vertIdx1
[num
+ 1], vertIdx2
[num
+ 1]]
89 face
= [vertIdx1
[0], vertIdx2
[num
], vertIdx2
[num
+ 1]]
91 face
= [vertIdx1
[num
], vertIdx2
[num
],
92 vertIdx2
[num
+ 1], vertIdx1
[num
+ 1]]
98 # @todo Clean up vertex&face creation process a bit.
99 def add_gem(r1
, r2
, seg
, h1
, h2
):
103 seg = number of segments
106 Generates the vertices and faces of the gem
111 a
= 2.0 * pi
/ seg
# Angle between segments
112 offset
= a
/ 2.0 # Middle between segments
114 r3
= ((r1
+ r2
) / 2.0) / cos(offset
) # Middle of crown
115 r4
= (r1
/ 2.0) / cos(offset
) # Middle of pavilion
116 h3
= h2
/ 2.0 # Middle of crown height
117 h4
= -h1
/ 2.0 # Middle of pavilion height
120 vert_tip
= len(verts
)
121 verts
.append(Vector((0.0, 0.0, -h1
)))
123 # Middle vertex of the flat side (crown)
124 vert_flat
= len(verts
)
125 verts
.append(Vector((0.0, 0.0, h2
)))
130 s2
= sin(offset
+ i
* a
)
132 c2
= cos(offset
+ i
* a
)
134 verts
.append((r4
* s1
, r4
* c1
, h4
)) # Middle of pavilion
135 verts
.append((r1
* s2
, r1
* c2
, 0.0)) # Pavilion
136 verts
.append((r3
* s1
, r3
* c1
, h3
)) # Middle crown
137 edgeloop_flat
.append(len(verts
))
138 verts
.append((r2
* s2
, r2
* c2
, h2
)) # Crown
142 for index
in range(seg
):
144 j
= ((index
+ 1) % seg
) * 4
146 faces
.append([j
+ 2, vert_tip
, i
+ 2, i
+ 3]) # Tip -> Middle of pav
147 faces
.append([j
+ 2, i
+ 3, j
+ 3]) # Middle of pav -> pav
148 faces
.append([j
+ 3, i
+ 3, j
+ 4]) # Pav -> Middle crown
149 faces
.append([j
+ 4, i
+ 3, i
+ 4, i
+ 5]) # Crown quads
150 faces
.append([j
+ 4, i
+ 5, j
+ 5]) # Middle crown -> crown
152 faces_flat
= createFaces([vert_flat
], edgeloop_flat
, closed
=True, flipped
=True)
153 faces
.extend(faces_flat
)
158 def add_diamond(segments
, girdle_radius
, table_radius
,
159 crown_height
, pavilion_height
):
162 z_axis
= (0.0, 0.0, -1.0)
167 height_flat
= crown_height
169 height_tip
= -pavilion_height
171 # Middle vertex of the flat side (crown)
172 vert_flat
= len(verts
)
173 verts
.append(Vector((0.0, 0.0, height_flat
)))
176 vert_tip
= len(verts
)
177 verts
.append(Vector((0.0, 0.0, height_tip
)))
182 for index
in range(segments
):
183 quat
= Quaternion(z_axis
, (index
/ segments
) * PI_2
)
185 # angle = PI_2 * index / segments # UNUSED
188 verts_flat
.append(len(verts
))
189 vec
= quat
@ Vector((table_radius
, 0.0, height_flat
))
192 # Row for the middle/girdle
193 verts_girdle
.append(len(verts
))
194 vec
= quat
@ Vector((girdle_radius
, 0.0, height_middle
))
198 faces_flat
= createFaces([vert_flat
], verts_flat
, closed
=True,
201 faces_side
= createFaces(verts_girdle
, verts_flat
, closed
=True)
203 faces_tip
= createFaces([vert_tip
], verts_girdle
, closed
=True)
205 faces
.extend(faces_tip
)
206 faces
.extend(faces_side
)
207 faces
.extend(faces_flat
)
212 class AddDiamond(Operator
, object_utils
.AddObjectHelper
):
213 bl_idname
= "mesh.primitive_diamond_add"
214 bl_label
= "Add Diamond"
215 bl_description
= "Construct a diamond mesh"
216 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
218 Diamond
: BoolProperty(name
= "Diamond",
220 description
= "Diamond")
222 #### change properties
223 name
: StringProperty(name
= "Name",
224 description
= "Name")
226 change
: BoolProperty(name
= "Change",
228 description
= "change Diamond")
230 segments
: IntProperty(
232 description
="Number of segments for the diamond",
237 girdle_radius
: FloatProperty(
238 name
="Girdle Radius",
239 description
="Girdle radius of the diamond",
244 table_radius
: FloatProperty(
246 description
="Girdle radius of the diamond",
251 crown_height
: FloatProperty(
253 description
="Crown height of the diamond",
258 pavilion_height
: FloatProperty(
259 name
="Pavilion Height",
260 description
="Pavilion height of the diamond",
266 def draw(self
, context
):
269 box
.prop(self
, "segments")
270 box
.prop(self
, "girdle_radius")
271 box
.prop(self
, "table_radius")
272 box
.prop(self
, "crown_height")
273 box
.prop(self
, "pavilion_height")
275 if self
.change
== False:
276 # generic transform props
278 box
.prop(self
, 'align', expand
=True)
279 box
.prop(self
, 'location', expand
=True)
280 box
.prop(self
, 'rotation', expand
=True)
282 def execute(self
, context
):
283 # turn off 'Enter Edit Mode'
284 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
285 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
287 if bpy
.context
.mode
== "OBJECT":
288 if context
.selected_objects
!= [] and context
.active_object
and \
289 (context
.active_object
.data
is not None) and ('Diamond' in context
.active_object
.data
.keys()) and \
290 (self
.change
== True):
291 obj
= context
.active_object
293 oldmeshname
= obj
.data
.name
295 verts
, faces
= add_diamond(self
.segments
,
299 self
.pavilion_height
)
300 mesh
= bpy
.data
.meshes
.new("TMP")
301 mesh
.from_pydata(verts
, [], faces
)
305 for material
in oldmesh
.materials
:
306 obj
.data
.materials
.append(material
)
308 bpy
.data
.meshes
.remove(oldmesh
)
309 obj
.data
.name
= oldmeshname
311 verts
, faces
= add_diamond(self
.segments
,
315 self
.pavilion_height
)
317 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "Diamond")
319 obj
.data
["Diamond"] = True
320 obj
.data
["change"] = False
321 for prm
in DiamondParameters():
322 obj
.data
[prm
] = getattr(self
, prm
)
324 if bpy
.context
.mode
== "EDIT_MESH":
325 active_object
= context
.active_object
326 name_active_object
= active_object
.name
327 bpy
.ops
.object.mode_set(mode
='OBJECT')
328 verts
, faces
= add_diamond(self
.segments
,
332 self
.pavilion_height
)
334 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "TMP")
337 active_object
.select_set(True)
338 bpy
.context
.view_layer
.objects
.active
= active_object
339 bpy
.ops
.object.join()
340 context
.active_object
.name
= name_active_object
341 bpy
.ops
.object.mode_set(mode
='EDIT')
343 if use_enter_edit_mode
:
344 bpy
.ops
.object.mode_set(mode
= 'EDIT')
346 # restore pre operator state
347 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
351 def DiamondParameters():
352 DiamondParameters
= [
359 return DiamondParameters
362 class AddGem(Operator
, object_utils
.AddObjectHelper
):
363 bl_idname
= "mesh.primitive_gem_add"
365 bl_description
= "Construct an offset faceted gem mesh"
366 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
368 Gem
: BoolProperty(name
= "Gem",
372 #### change properties
373 name
: StringProperty(name
= "Name",
374 description
= "Name")
376 change
: BoolProperty(name
= "Change",
378 description
= "change Gem")
380 segments
: IntProperty(
382 description
="Longitudial segmentation",
387 pavilion_radius
: FloatProperty(
389 description
="Radius of the gem",
394 crown_radius
: FloatProperty(
396 description
="Radius of the table(top)",
401 crown_height
: FloatProperty(
403 description
="Height of the top half",
408 pavilion_height
: FloatProperty(
409 name
="Pavilion height",
410 description
="Height of bottom half",
416 def draw(self
, context
):
419 box
.prop(self
, "segments")
420 box
.prop(self
, "pavilion_radius")
421 box
.prop(self
, "crown_radius")
422 box
.prop(self
, "crown_height")
423 box
.prop(self
, "pavilion_height")
425 if self
.change
== False:
426 # generic transform props
428 box
.prop(self
, 'align', expand
=True)
429 box
.prop(self
, 'location', expand
=True)
430 box
.prop(self
, 'rotation', expand
=True)
432 def execute(self
, context
):
433 # turn off 'Enter Edit Mode'
434 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
435 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
437 if bpy
.context
.mode
== "OBJECT":
438 if context
.selected_objects
!= [] and context
.active_object
and \
439 (context
.active_object
.data
is not None) and ('Gem' in context
.active_object
.data
.keys()) and \
440 (self
.change
== True):
441 obj
= context
.active_object
443 oldmeshname
= obj
.data
.name
444 verts
, faces
= add_gem(
445 self
.pavilion_radius
,
448 self
.pavilion_height
,
450 mesh
= bpy
.data
.meshes
.new("TMP")
451 mesh
.from_pydata(verts
, [], faces
)
454 for material
in oldmesh
.materials
:
455 obj
.data
.materials
.append(material
)
456 bpy
.data
.meshes
.remove(oldmesh
)
457 obj
.data
.name
= oldmeshname
459 verts
, faces
= add_gem(
460 self
.pavilion_radius
,
463 self
.pavilion_height
,
466 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "Gem")
468 obj
.data
["Gem"] = True
469 obj
.data
["change"] = False
470 for prm
in GemParameters():
471 obj
.data
[prm
] = getattr(self
, prm
)
473 if bpy
.context
.mode
== "EDIT_MESH":
474 active_object
= context
.active_object
475 name_active_object
= active_object
.name
476 bpy
.ops
.object.mode_set(mode
='OBJECT')
477 verts
, faces
= add_gem(
478 self
.pavilion_radius
,
481 self
.pavilion_height
,
484 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "TMP")
487 active_object
.select_set(True)
488 bpy
.context
.view_layer
.objects
.active
= active_object
489 bpy
.ops
.object.join()
490 context
.active_object
.name
= name_active_object
491 bpy
.ops
.object.mode_set(mode
='EDIT')
493 if use_enter_edit_mode
:
494 bpy
.ops
.object.mode_set(mode
= 'EDIT')
496 # restore pre operator state
497 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode