1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Author: Pontiac, Fourmadmen, Dreampainter
8 from bpy
.types
import Operator
9 from mathutils
import (
13 from math
import cos
, sin
, pi
14 from bpy
.props
import (
20 from bpy_extras
import object_utils
22 # Create a new mesh (object) from verts/edges/faces.
23 # verts/edges/faces ... List of vertices/edges/faces for the
24 # new mesh (as used in from_pydata)
25 # name ... Name of the new mesh (& object)
27 def create_mesh_object(context
, self
, verts
, edges
, faces
, name
):
30 mesh
= bpy
.data
.meshes
.new(name
)
32 # Make a mesh from a list of verts/edges/faces.
33 mesh
.from_pydata(verts
, edges
, faces
)
35 # Update mesh geometry after adding stuff.
38 from bpy_extras
import object_utils
39 return object_utils
.object_data_add(context
, mesh
, operator
=self
)
42 # A very simple "bridge" tool.
44 def createFaces(vertIdx1
, vertIdx2
, closed
=False, flipped
=False):
47 if not vertIdx1
or not vertIdx2
:
50 if len(vertIdx1
) < 2 and len(vertIdx2
) < 2:
54 if (len(vertIdx1
) != len(vertIdx2
)):
55 if (len(vertIdx1
) == 1 and len(vertIdx2
) > 1):
63 # Bridge the start with the end
70 face
.append(vertIdx1
[total
- 1])
74 face
= [vertIdx2
[0], vertIdx1
[0]]
76 face
.append(vertIdx1
[total
- 1])
77 face
.append(vertIdx2
[total
- 1])
80 # Bridge the rest of the faces
81 for num
in range(total
- 1):
84 face
= [vertIdx2
[num
], vertIdx1
[0], vertIdx2
[num
+ 1]]
86 face
= [vertIdx2
[num
], vertIdx1
[num
],
87 vertIdx1
[num
+ 1], vertIdx2
[num
+ 1]]
91 face
= [vertIdx1
[0], vertIdx2
[num
], vertIdx2
[num
+ 1]]
93 face
= [vertIdx1
[num
], vertIdx2
[num
],
94 vertIdx2
[num
+ 1], vertIdx1
[num
+ 1]]
100 # @todo Clean up vertex&face creation process a bit.
101 def add_gem(r1
, r2
, seg
, h1
, h2
):
105 seg = number of segments
108 Generates the vertices and faces of the gem
113 a
= 2.0 * pi
/ seg
# Angle between segments
114 offset
= a
/ 2.0 # Middle between segments
116 r3
= ((r1
+ r2
) / 2.0) / cos(offset
) # Middle of crown
117 r4
= (r1
/ 2.0) / cos(offset
) # Middle of pavilion
118 h3
= h2
/ 2.0 # Middle of crown height
119 h4
= -h1
/ 2.0 # Middle of pavilion height
122 vert_tip
= len(verts
)
123 verts
.append(Vector((0.0, 0.0, -h1
)))
125 # Middle vertex of the flat side (crown)
126 vert_flat
= len(verts
)
127 verts
.append(Vector((0.0, 0.0, h2
)))
132 s2
= sin(offset
+ i
* a
)
134 c2
= cos(offset
+ i
* a
)
136 verts
.append((r4
* s1
, r4
* c1
, h4
)) # Middle of pavilion
137 verts
.append((r1
* s2
, r1
* c2
, 0.0)) # Pavilion
138 verts
.append((r3
* s1
, r3
* c1
, h3
)) # Middle crown
139 edgeloop_flat
.append(len(verts
))
140 verts
.append((r2
* s2
, r2
* c2
, h2
)) # Crown
144 for index
in range(seg
):
146 j
= ((index
+ 1) % seg
) * 4
148 faces
.append([j
+ 2, vert_tip
, i
+ 2, i
+ 3]) # Tip -> Middle of pav
149 faces
.append([j
+ 2, i
+ 3, j
+ 3]) # Middle of pav -> pav
150 faces
.append([j
+ 3, i
+ 3, j
+ 4]) # Pav -> Middle crown
151 faces
.append([j
+ 4, i
+ 3, i
+ 4, i
+ 5]) # Crown quads
152 faces
.append([j
+ 4, i
+ 5, j
+ 5]) # Middle crown -> crown
154 faces_flat
= createFaces([vert_flat
], edgeloop_flat
, closed
=True, flipped
=True)
155 faces
.extend(faces_flat
)
160 def add_diamond(segments
, girdle_radius
, table_radius
,
161 crown_height
, pavilion_height
):
164 z_axis
= (0.0, 0.0, -1.0)
169 height_flat
= crown_height
171 height_tip
= -pavilion_height
173 # Middle vertex of the flat side (crown)
174 vert_flat
= len(verts
)
175 verts
.append(Vector((0.0, 0.0, height_flat
)))
178 vert_tip
= len(verts
)
179 verts
.append(Vector((0.0, 0.0, height_tip
)))
184 for index
in range(segments
):
185 quat
= Quaternion(z_axis
, (index
/ segments
) * PI_2
)
187 # angle = PI_2 * index / segments # UNUSED
190 verts_flat
.append(len(verts
))
191 vec
= quat
@ Vector((table_radius
, 0.0, height_flat
))
194 # Row for the middle/girdle
195 verts_girdle
.append(len(verts
))
196 vec
= quat
@ Vector((girdle_radius
, 0.0, height_middle
))
200 faces_flat
= createFaces([vert_flat
], verts_flat
, closed
=True,
203 faces_side
= createFaces(verts_girdle
, verts_flat
, closed
=True)
205 faces_tip
= createFaces([vert_tip
], verts_girdle
, closed
=True)
207 faces
.extend(faces_tip
)
208 faces
.extend(faces_side
)
209 faces
.extend(faces_flat
)
214 class AddDiamond(Operator
, object_utils
.AddObjectHelper
):
215 bl_idname
= "mesh.primitive_diamond_add"
216 bl_label
= "Add Diamond"
217 bl_description
= "Construct a diamond mesh"
218 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
220 Diamond
: BoolProperty(name
= "Diamond",
222 description
= "Diamond")
224 #### change properties
225 name
: StringProperty(name
= "Name",
226 description
= "Name")
228 change
: BoolProperty(name
= "Change",
230 description
= "change Diamond")
232 segments
: IntProperty(
234 description
="Number of segments for the diamond",
239 girdle_radius
: FloatProperty(
240 name
="Girdle Radius",
241 description
="Girdle radius of the diamond",
246 table_radius
: FloatProperty(
248 description
="Girdle radius of the diamond",
253 crown_height
: FloatProperty(
255 description
="Crown height of the diamond",
260 pavilion_height
: FloatProperty(
261 name
="Pavilion Height",
262 description
="Pavilion height of the diamond",
268 def draw(self
, context
):
271 box
.prop(self
, "segments")
272 box
.prop(self
, "girdle_radius")
273 box
.prop(self
, "table_radius")
274 box
.prop(self
, "crown_height")
275 box
.prop(self
, "pavilion_height")
277 if self
.change
== False:
278 # generic transform props
280 box
.prop(self
, 'align', expand
=True)
281 box
.prop(self
, 'location', expand
=True)
282 box
.prop(self
, 'rotation', expand
=True)
284 def execute(self
, context
):
285 # turn off 'Enter Edit Mode'
286 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
287 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
289 if bpy
.context
.mode
== "OBJECT":
290 if context
.selected_objects
!= [] and context
.active_object
and \
291 (context
.active_object
.data
is not None) and ('Diamond' in context
.active_object
.data
.keys()) and \
292 (self
.change
== True):
293 obj
= context
.active_object
295 oldmeshname
= obj
.data
.name
297 verts
, faces
= add_diamond(self
.segments
,
301 self
.pavilion_height
)
302 mesh
= bpy
.data
.meshes
.new("TMP")
303 mesh
.from_pydata(verts
, [], faces
)
307 for material
in oldmesh
.materials
:
308 obj
.data
.materials
.append(material
)
310 bpy
.data
.meshes
.remove(oldmesh
)
311 obj
.data
.name
= oldmeshname
313 verts
, faces
= add_diamond(self
.segments
,
317 self
.pavilion_height
)
319 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "Diamond")
321 obj
.data
["Diamond"] = True
322 obj
.data
["change"] = False
323 for prm
in DiamondParameters():
324 obj
.data
[prm
] = getattr(self
, prm
)
326 if bpy
.context
.mode
== "EDIT_MESH":
327 active_object
= context
.active_object
328 name_active_object
= active_object
.name
329 bpy
.ops
.object.mode_set(mode
='OBJECT')
330 verts
, faces
= add_diamond(self
.segments
,
334 self
.pavilion_height
)
336 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "TMP")
339 active_object
.select_set(True)
340 bpy
.context
.view_layer
.objects
.active
= active_object
341 bpy
.ops
.object.join()
342 context
.active_object
.name
= name_active_object
343 bpy
.ops
.object.mode_set(mode
='EDIT')
345 if use_enter_edit_mode
:
346 bpy
.ops
.object.mode_set(mode
= 'EDIT')
348 # restore pre operator state
349 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
353 def DiamondParameters():
354 DiamondParameters
= [
361 return DiamondParameters
364 class AddGem(Operator
, object_utils
.AddObjectHelper
):
365 bl_idname
= "mesh.primitive_gem_add"
367 bl_description
= "Construct an offset faceted gem mesh"
368 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
370 Gem
: BoolProperty(name
= "Gem",
374 #### change properties
375 name
: StringProperty(name
= "Name",
376 description
= "Name")
378 change
: BoolProperty(name
= "Change",
380 description
= "change Gem")
382 segments
: IntProperty(
384 description
="Longitudial segmentation",
389 pavilion_radius
: FloatProperty(
391 description
="Radius of the gem",
396 crown_radius
: FloatProperty(
398 description
="Radius of the table(top)",
403 crown_height
: FloatProperty(
405 description
="Height of the top half",
410 pavilion_height
: FloatProperty(
411 name
="Pavilion height",
412 description
="Height of bottom half",
418 def draw(self
, context
):
421 box
.prop(self
, "segments")
422 box
.prop(self
, "pavilion_radius")
423 box
.prop(self
, "crown_radius")
424 box
.prop(self
, "crown_height")
425 box
.prop(self
, "pavilion_height")
427 if self
.change
== False:
428 # generic transform props
430 box
.prop(self
, 'align', expand
=True)
431 box
.prop(self
, 'location', expand
=True)
432 box
.prop(self
, 'rotation', expand
=True)
434 def execute(self
, context
):
435 # turn off 'Enter Edit Mode'
436 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
437 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
439 if bpy
.context
.mode
== "OBJECT":
440 if context
.selected_objects
!= [] and context
.active_object
and \
441 (context
.active_object
.data
is not None) and ('Gem' in context
.active_object
.data
.keys()) and \
442 (self
.change
== True):
443 obj
= context
.active_object
445 oldmeshname
= obj
.data
.name
446 verts
, faces
= add_gem(
447 self
.pavilion_radius
,
450 self
.pavilion_height
,
452 mesh
= bpy
.data
.meshes
.new("TMP")
453 mesh
.from_pydata(verts
, [], faces
)
456 for material
in oldmesh
.materials
:
457 obj
.data
.materials
.append(material
)
458 bpy
.data
.meshes
.remove(oldmesh
)
459 obj
.data
.name
= oldmeshname
461 verts
, faces
= add_gem(
462 self
.pavilion_radius
,
465 self
.pavilion_height
,
468 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "Gem")
470 obj
.data
["Gem"] = True
471 obj
.data
["change"] = False
472 for prm
in GemParameters():
473 obj
.data
[prm
] = getattr(self
, prm
)
475 if bpy
.context
.mode
== "EDIT_MESH":
476 active_object
= context
.active_object
477 name_active_object
= active_object
.name
478 bpy
.ops
.object.mode_set(mode
='OBJECT')
479 verts
, faces
= add_gem(
480 self
.pavilion_radius
,
483 self
.pavilion_height
,
486 obj
= create_mesh_object(context
, self
, verts
, [], faces
, "TMP")
489 active_object
.select_set(True)
490 bpy
.context
.view_layer
.objects
.active
= active_object
491 bpy
.ops
.object.join()
492 context
.active_object
.name
= name_active_object
493 bpy
.ops
.object.mode_set(mode
='EDIT')
495 if use_enter_edit_mode
:
496 bpy
.ops
.object.mode_set(mode
= 'EDIT')
498 # restore pre operator state
499 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode