Fix #105067: Node Wrangler: cannot preview multi-user material group
[blender-addons.git] / add_mesh_extra_objects / add_mesh_gemstones.py
blobc35dc0d2ad74e6c7aa488b8ff4ad628336153140
1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Author: Pontiac, Fourmadmen, Dreampainter
7 import bpy
8 from bpy.types import Operator
9 from mathutils import (
10 Vector,
11 Quaternion,
13 from math import cos, sin, pi
14 from bpy.props import (
15 FloatProperty,
16 IntProperty,
17 BoolProperty,
18 StringProperty,
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):
29 # Create new mesh
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.
36 mesh.update()
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):
45 faces = []
47 if not vertIdx1 or not vertIdx2:
48 return None
50 if len(vertIdx1) < 2 and len(vertIdx2) < 2:
51 return None
53 fan = False
54 if (len(vertIdx1) != len(vertIdx2)):
55 if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
56 fan = True
57 else:
58 return None
60 total = len(vertIdx2)
62 if closed:
63 # Bridge the start with the end
64 if flipped:
65 face = [
66 vertIdx1[0],
67 vertIdx2[0],
68 vertIdx2[total - 1]]
69 if not fan:
70 face.append(vertIdx1[total - 1])
71 faces.append(face)
73 else:
74 face = [vertIdx2[0], vertIdx1[0]]
75 if not fan:
76 face.append(vertIdx1[total - 1])
77 face.append(vertIdx2[total - 1])
78 faces.append(face)
80 # Bridge the rest of the faces
81 for num in range(total - 1):
82 if flipped:
83 if fan:
84 face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
85 else:
86 face = [vertIdx2[num], vertIdx1[num],
87 vertIdx1[num + 1], vertIdx2[num + 1]]
88 faces.append(face)
89 else:
90 if fan:
91 face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
92 else:
93 face = [vertIdx1[num], vertIdx2[num],
94 vertIdx2[num + 1], vertIdx1[num + 1]]
95 faces.append(face)
97 return faces
100 # @todo Clean up vertex&face creation process a bit.
101 def add_gem(r1, r2, seg, h1, h2):
103 r1 = pavilion radius
104 r2 = crown radius
105 seg = number of segments
106 h1 = pavilion height
107 h2 = crown height
108 Generates the vertices and faces of the gem
111 verts = []
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
121 # Tip
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)))
129 edgeloop_flat = []
130 for i in range(seg):
131 s1 = sin(i * a)
132 s2 = sin(offset + i * a)
133 c1 = cos(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
142 faces = []
144 for index in range(seg):
145 i = index * 4
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)
157 return verts, faces
160 def add_diamond(segments, girdle_radius, table_radius,
161 crown_height, pavilion_height):
163 PI_2 = pi * 2.0
164 z_axis = (0.0, 0.0, -1.0)
166 verts = []
167 faces = []
169 height_flat = crown_height
170 height_middle = 0.0
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)))
177 # Tip
178 vert_tip = len(verts)
179 verts.append(Vector((0.0, 0.0, height_tip)))
181 verts_flat = []
182 verts_girdle = []
184 for index in range(segments):
185 quat = Quaternion(z_axis, (index / segments) * PI_2)
187 # angle = PI_2 * index / segments # UNUSED
189 # Row for flat side
190 verts_flat.append(len(verts))
191 vec = quat @ Vector((table_radius, 0.0, height_flat))
192 verts.append(vec)
194 # Row for the middle/girdle
195 verts_girdle.append(len(verts))
196 vec = quat @ Vector((girdle_radius, 0.0, height_middle))
197 verts.append(vec)
199 # Flat face
200 faces_flat = createFaces([vert_flat], verts_flat, closed=True,
201 flipped=True)
202 # Side face
203 faces_side = createFaces(verts_girdle, verts_flat, closed=True)
204 # Tip faces
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)
211 return verts, faces
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",
221 default = True,
222 description = "Diamond")
224 #### change properties
225 name : StringProperty(name = "Name",
226 description = "Name")
228 change : BoolProperty(name = "Change",
229 default = False,
230 description = "change Diamond")
232 segments: IntProperty(
233 name="Segments",
234 description="Number of segments for the diamond",
235 min=3,
236 max=256,
237 default=32
239 girdle_radius: FloatProperty(
240 name="Girdle Radius",
241 description="Girdle radius of the diamond",
242 min=0.01,
243 max=9999.0,
244 default=1.0
246 table_radius: FloatProperty(
247 name="Table Radius",
248 description="Girdle radius of the diamond",
249 min=0.01,
250 max=9999.0,
251 default=0.6
253 crown_height: FloatProperty(
254 name="Crown Height",
255 description="Crown height of the diamond",
256 min=0.01,
257 max=9999.0,
258 default=0.35
260 pavilion_height: FloatProperty(
261 name="Pavilion Height",
262 description="Pavilion height of the diamond",
263 min=0.01,
264 max=9999.0,
265 default=0.8
268 def draw(self, context):
269 layout = self.layout
270 box = layout.box()
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
279 box = layout.box()
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
294 oldmesh = obj.data
295 oldmeshname = obj.data.name
297 verts, faces = add_diamond(self.segments,
298 self.girdle_radius,
299 self.table_radius,
300 self.crown_height,
301 self.pavilion_height)
302 mesh = bpy.data.meshes.new("TMP")
303 mesh.from_pydata(verts, [], faces)
304 mesh.update()
305 obj.data = mesh
307 for material in oldmesh.materials:
308 obj.data.materials.append(material)
310 bpy.data.meshes.remove(oldmesh)
311 obj.data.name = oldmeshname
312 else:
313 verts, faces = add_diamond(self.segments,
314 self.girdle_radius,
315 self.table_radius,
316 self.crown_height,
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,
331 self.girdle_radius,
332 self.table_radius,
333 self.crown_height,
334 self.pavilion_height)
336 obj = create_mesh_object(context, self, verts, [], faces, "TMP")
338 obj.select_set(True)
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
351 return {'FINISHED'}
353 def DiamondParameters():
354 DiamondParameters = [
355 "segments",
356 "girdle_radius",
357 "table_radius",
358 "crown_height",
359 "pavilion_height",
361 return DiamondParameters
364 class AddGem(Operator, object_utils.AddObjectHelper):
365 bl_idname = "mesh.primitive_gem_add"
366 bl_label = "Add Gem"
367 bl_description = "Construct an offset faceted gem mesh"
368 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
370 Gem : BoolProperty(name = "Gem",
371 default = True,
372 description = "Gem")
374 #### change properties
375 name : StringProperty(name = "Name",
376 description = "Name")
378 change : BoolProperty(name = "Change",
379 default = False,
380 description = "change Gem")
382 segments: IntProperty(
383 name="Segments",
384 description="Longitudial segmentation",
385 min=3,
386 max=265,
387 default=8
389 pavilion_radius: FloatProperty(
390 name="Radius",
391 description="Radius of the gem",
392 min=0.01,
393 max=9999.0,
394 default=1.0
396 crown_radius: FloatProperty(
397 name="Table Radius",
398 description="Radius of the table(top)",
399 min=0.01,
400 max=9999.0,
401 default=0.6
403 crown_height: FloatProperty(
404 name="Table height",
405 description="Height of the top half",
406 min=0.01,
407 max=9999.0,
408 default=0.35
410 pavilion_height: FloatProperty(
411 name="Pavilion height",
412 description="Height of bottom half",
413 min=0.01,
414 max=9999.0,
415 default=0.8
418 def draw(self, context):
419 layout = self.layout
420 box = layout.box()
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
429 box = layout.box()
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
444 oldmesh = obj.data
445 oldmeshname = obj.data.name
446 verts, faces = add_gem(
447 self.pavilion_radius,
448 self.crown_radius,
449 self.segments,
450 self.pavilion_height,
451 self.crown_height)
452 mesh = bpy.data.meshes.new("TMP")
453 mesh.from_pydata(verts, [], faces)
454 mesh.update()
455 obj.data = mesh
456 for material in oldmesh.materials:
457 obj.data.materials.append(material)
458 bpy.data.meshes.remove(oldmesh)
459 obj.data.name = oldmeshname
460 else:
461 verts, faces = add_gem(
462 self.pavilion_radius,
463 self.crown_radius,
464 self.segments,
465 self.pavilion_height,
466 self.crown_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,
481 self.crown_radius,
482 self.segments,
483 self.pavilion_height,
484 self.crown_height)
486 obj = create_mesh_object(context, self, verts, [], faces, "TMP")
488 obj.select_set(True)
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
501 return {'FINISHED'}
503 def GemParameters():
504 GemParameters = [
505 "segments",
506 "pavilion_radius",
507 "crown_radius",
508 "crown_height",
509 "pavilion_height",
511 return GemParameters