2026-03-11_4
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,979 @@
|
||||
# SPDX-FileCopyrightText: 2016-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# authors: dudecon, jambay
|
||||
|
||||
# This module contains the UI definition, display,
|
||||
# and processing (create mesh) functions.
|
||||
# The routines to generate the vertices for the wall
|
||||
# are found in the "Blocks" module.
|
||||
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
FloatProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from .Blocks import (
|
||||
NOTZERO, PI,
|
||||
dims,
|
||||
settings,
|
||||
shelfSpecs,
|
||||
stepSpecs,
|
||||
createWall,
|
||||
radialized,
|
||||
slope,
|
||||
openingSpecs,
|
||||
bigBlock,
|
||||
shelfExt,
|
||||
stepMod,
|
||||
stepLeft,
|
||||
shelfBack,
|
||||
stepOnly,
|
||||
stepBack,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
class add_mesh_wallb(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.wall_add"
|
||||
bl_label = "Add a Masonry Wall"
|
||||
bl_description = "Create a block (masonry) wall mesh"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
# UI items - API for properties - User accessible variables...
|
||||
# not all options are via UI, and some operations just don't work yet
|
||||
|
||||
Wall : BoolProperty(name = "Wall",
|
||||
default = True,
|
||||
description = "Wall")
|
||||
|
||||
#### change properties
|
||||
name : StringProperty(name = "Name",
|
||||
description = "Name")
|
||||
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Wall")
|
||||
|
||||
# only create object when True
|
||||
# False allows modifying several parameters without creating object
|
||||
ConstructTog: BoolProperty(
|
||||
name="Construct",
|
||||
description="Generate the object",
|
||||
default=True
|
||||
)
|
||||
# need to modify so radial makes a tower (normal);
|
||||
# want "flat" setting to make disk (alternate)
|
||||
# make the wall circular - if not sloped it's a flat disc
|
||||
RadialTog: BoolProperty(
|
||||
name="Radial",
|
||||
description="Make masonry radial",
|
||||
default=False
|
||||
)
|
||||
# curve the wall - if radial creates dome.
|
||||
SlopeTog: BoolProperty(
|
||||
name="Curved",
|
||||
description="Make masonry sloped, or curved",
|
||||
default=False
|
||||
)
|
||||
# need to review defaults and limits for all of these UI objects
|
||||
|
||||
# wall area/size
|
||||
WallStart: FloatProperty(
|
||||
name="Start",
|
||||
description="Left side, or start angle",
|
||||
default=-10.0,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
WallEnd: FloatProperty(
|
||||
name="End",
|
||||
description="Right side, or end angle",
|
||||
default=10.0,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
WallBottom: FloatProperty(
|
||||
name="Bottom",
|
||||
description="Lower height or radius",
|
||||
default=0.0,
|
||||
min=-100, max=100
|
||||
)
|
||||
WallTop: FloatProperty(
|
||||
name="Top",
|
||||
description="Upper height or radius",
|
||||
default=15.0,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
EdgeOffset: FloatProperty(
|
||||
name="Edging",
|
||||
description="Block staggering on wall sides",
|
||||
default=0.6, min=0.0, max=100.0
|
||||
)
|
||||
# block sizing
|
||||
Width: FloatProperty(
|
||||
name="Width",
|
||||
description="Average width of each block",
|
||||
default=1.5,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
WidthVariance: FloatProperty(
|
||||
name="Variance",
|
||||
description="Random variance of block width",
|
||||
default=0.5,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
WidthMinimum: FloatProperty(
|
||||
name="Minimum",
|
||||
description="Absolute minimum block width",
|
||||
default=0.5,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
Height: FloatProperty(
|
||||
name="Height",
|
||||
description="Average Height of each block",
|
||||
default=0.7,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
HeightVariance: FloatProperty(
|
||||
name="Variance",
|
||||
description="Random variance of block Height",
|
||||
default=0.3,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
HeightMinimum: FloatProperty(
|
||||
name="Minimum",
|
||||
description="Absolute minimum block Height",
|
||||
default=0.25,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
Depth: FloatProperty(
|
||||
name="Depth",
|
||||
description="Average Depth of each block",
|
||||
default=2.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
DepthVariance: FloatProperty(
|
||||
name="Variance",
|
||||
description="Random variance of block Depth",
|
||||
default=0.1,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
DepthMinimum: FloatProperty(
|
||||
name="Minimum",
|
||||
description="Absolute minimum block Depth",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
MergeBlock: BoolProperty(
|
||||
name="Merge Blocks",
|
||||
description="Make big blocks (merge closely adjoining blocks)",
|
||||
default=False
|
||||
)
|
||||
# edging for blocks
|
||||
Grout: FloatProperty(
|
||||
name="Thickness",
|
||||
description="Distance between blocks",
|
||||
default=0.1,
|
||||
min=-10.0, max=10.0
|
||||
)
|
||||
GroutVariance: FloatProperty(
|
||||
name="Variance",
|
||||
description="Random variance of block Grout",
|
||||
default=0.03,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
GroutDepth: FloatProperty(
|
||||
name="Depth",
|
||||
description="Grout Depth from the face of the blocks",
|
||||
default=0.1,
|
||||
min=0.0001, max=10.0
|
||||
)
|
||||
GroutDepthVariance: FloatProperty(
|
||||
name="Variance",
|
||||
description="Random variance of block Grout Depth",
|
||||
default=0.03,
|
||||
min=0.0, max=100.0
|
||||
)
|
||||
GroutEdge: BoolProperty(
|
||||
name="Edging",
|
||||
description="Grout perimiter",
|
||||
default=False
|
||||
)
|
||||
# properties for openings
|
||||
Opening1Tog: BoolProperty(
|
||||
name="Opening(s)",
|
||||
description="Make windows or doors",
|
||||
default=True
|
||||
)
|
||||
Opening1Width: FloatProperty(
|
||||
name="Width",
|
||||
description="The Width of the first opening",
|
||||
default=2.5,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
Opening1Height: FloatProperty(
|
||||
name="Height",
|
||||
description="The Height of the first opening",
|
||||
default=3.5,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
Opening1X: FloatProperty(
|
||||
name="Indent",
|
||||
description="The x position or spacing of the first opening",
|
||||
default=5.0,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
Opening1Z: FloatProperty(
|
||||
name="Bottom",
|
||||
description="The z position of the First opening",
|
||||
default=5.0,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
Opening1Repeat: BoolProperty(
|
||||
name="Repeat",
|
||||
description="make multiple openings, with spacing X1",
|
||||
default=False
|
||||
)
|
||||
Opening1TopArchTog: BoolProperty(
|
||||
name="Top Arch",
|
||||
description="Add an arch to the top of the first opening",
|
||||
default=True
|
||||
)
|
||||
Opening1TopArch: FloatProperty(
|
||||
name="Curve",
|
||||
description="Height of the arch on the top of the opening",
|
||||
default=2.5,
|
||||
min=0.001, max=100.0
|
||||
)
|
||||
Opening1TopArchThickness: FloatProperty(
|
||||
name="Thickness",
|
||||
description="Thickness of the arch on the top of the opening",
|
||||
default=0.75,
|
||||
min=0.001, max=100.0
|
||||
)
|
||||
Opening1BtmArchTog: BoolProperty(
|
||||
name="Bottom Arch",
|
||||
description="Add an arch to the bottom of opening 1",
|
||||
default=False
|
||||
)
|
||||
Opening1BtmArch: FloatProperty(
|
||||
name="Curve",
|
||||
description="Height of the arch on the bottom of the opening",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
Opening1BtmArchThickness: FloatProperty(
|
||||
name="Thickness",
|
||||
description="Thickness of the arch on the bottom of the opening",
|
||||
default=0.5,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
Opening1Bevel: FloatProperty(
|
||||
name="Bevel",
|
||||
description="Angle block face",
|
||||
default=0.25,
|
||||
min=-10.0, max=10.0
|
||||
)
|
||||
# openings on top of wall
|
||||
CrenelTog: BoolProperty(
|
||||
name="Crenels",
|
||||
description="Make openings along top of wall",
|
||||
default=False
|
||||
)
|
||||
CrenelXP: FloatProperty(
|
||||
name="Width",
|
||||
description="Gap width in wall based the percentage of wall width",
|
||||
default=0.25,
|
||||
min=0.10, max=1.0,
|
||||
subtype="PERCENTAGE"
|
||||
)
|
||||
CrenelZP: FloatProperty(
|
||||
name="Height",
|
||||
description="Crenel Height as the percentage of wall height",
|
||||
default=0.10,
|
||||
min=0.10, max=1.0,
|
||||
subtype="PERCENTAGE"
|
||||
)
|
||||
# narrow openings in wall.
|
||||
# need to prevent overlap with arch openings - though inversion is an interesting effect.
|
||||
SlotTog: BoolProperty(
|
||||
name="Slots",
|
||||
description="Make narrow openings in wall",
|
||||
default=False
|
||||
)
|
||||
SlotRpt: BoolProperty(
|
||||
name="Repeat",
|
||||
description="Repeat slots along wall",
|
||||
default=False
|
||||
)
|
||||
SlotWdg: BoolProperty(
|
||||
name="Wedged (n/a)",
|
||||
description="Bevel edges of slots",
|
||||
default=False
|
||||
)
|
||||
SlotX: FloatProperty(
|
||||
name="Indent",
|
||||
description="The x position or spacing of slots",
|
||||
default=0.0, min=-100, max=100.0
|
||||
)
|
||||
SlotGap: FloatProperty(
|
||||
name="Opening",
|
||||
description="The opening size of slots",
|
||||
default=0.5, min=0.10, max=100.0
|
||||
)
|
||||
SlotV: BoolProperty(
|
||||
name="Vertical",
|
||||
description="Vertical slots",
|
||||
default=True
|
||||
)
|
||||
SlotVH: FloatProperty(
|
||||
name="Height",
|
||||
description="Height of vertical slot",
|
||||
default=3.5,
|
||||
min=0.10, max=100.0
|
||||
)
|
||||
SlotVBtm: FloatProperty(
|
||||
name="Bottom",
|
||||
description="Z position for slot",
|
||||
default=5.00,
|
||||
min=-100.0, max=100.0
|
||||
)
|
||||
SlotH: BoolProperty(
|
||||
name="Horizontal",
|
||||
description="Horizontal slots",
|
||||
default=False
|
||||
)
|
||||
SlotHW: FloatProperty(
|
||||
name="Width",
|
||||
description="Width of horizontal slot",
|
||||
default=2.5,
|
||||
min=0.10, max=100.0
|
||||
)
|
||||
# this should offset from VBtm... maybe make a % like crenels?
|
||||
SlotHBtm: FloatProperty(
|
||||
name="Bottom",
|
||||
description="Z position for horizontal slot",
|
||||
default=5.50,
|
||||
min=-100.0, max=100.0
|
||||
)
|
||||
# properties for shelf (extend blocks in area)
|
||||
ShelfTog: BoolProperty(
|
||||
name="Shelf",
|
||||
description="Add blocks in area by depth to make shelf/platform",
|
||||
default=False
|
||||
)
|
||||
ShelfX: FloatProperty(
|
||||
name="Left",
|
||||
description="The x position of Shelf",
|
||||
default=-5.00,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
ShelfZ: FloatProperty(
|
||||
name="Bottom",
|
||||
description="The z position of Shelf",
|
||||
default=10.0,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
ShelfH: FloatProperty(
|
||||
name="Height",
|
||||
description="The Height of Shelf area",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
ShelfW: FloatProperty(
|
||||
name="Width",
|
||||
description="The Width of shelf area",
|
||||
default=5.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
ShelfD: FloatProperty(
|
||||
name="Depth",
|
||||
description="Depth of each block for shelf (from cursor + 1/2 wall depth)",
|
||||
default=2.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
ShelfBack: BoolProperty(
|
||||
name="Backside",
|
||||
description="Shelf on backside of wall",
|
||||
default=False
|
||||
)
|
||||
# properties for steps (extend blocks in area, progressive width)
|
||||
StepTog: BoolProperty(
|
||||
name="Steps",
|
||||
description="Add blocks in area by depth with progressive width to make steps",
|
||||
default=False
|
||||
)
|
||||
StepX: FloatProperty(
|
||||
name="Left",
|
||||
description="The x position of steps",
|
||||
default=-9.00,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
StepZ: FloatProperty(
|
||||
name="Bottom",
|
||||
description="The z position of steps",
|
||||
default=0.0,
|
||||
min=-100, max=100.0
|
||||
)
|
||||
StepH: FloatProperty(
|
||||
name="Height",
|
||||
description="The Height of step area",
|
||||
default=10.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
StepW: FloatProperty(
|
||||
name="Width",
|
||||
description="The Width of step area",
|
||||
default=8.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
StepD: FloatProperty(
|
||||
name="Depth",
|
||||
description="Depth of each block for steps (from cursor + 1/2 wall depth)",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
StepV: FloatProperty(
|
||||
name="Riser",
|
||||
description="Height of each step",
|
||||
default=0.70,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
StepT: FloatProperty(
|
||||
name="Tread",
|
||||
description="Width of each step",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
StepLeft: BoolProperty(
|
||||
name="Direction",
|
||||
description="If checked, flip steps direction towards the -X axis",
|
||||
default=False
|
||||
)
|
||||
StepOnly: BoolProperty(
|
||||
name="Steps Only",
|
||||
description="Steps only, no supporting blocks",
|
||||
default=False
|
||||
)
|
||||
StepBack: BoolProperty(
|
||||
name="Backside",
|
||||
description="Steps on backside of wall",
|
||||
default=False
|
||||
)
|
||||
|
||||
# Display the toolbox options
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
# Wall shape modifiers
|
||||
layout.prop(self, 'ConstructTog')
|
||||
|
||||
# Wall area (size/position)
|
||||
header, panel = layout.panel("WALLFACTORY_PT_AREA", default_closed=False)
|
||||
header.label(text="Wall Size")
|
||||
if panel:
|
||||
panel.prop(self, "RadialTog")
|
||||
panel.prop(self, "SlopeTog")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "WallStart")
|
||||
col.prop(self, "WallEnd")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "WallBottom")
|
||||
col.prop(self, "WallTop")
|
||||
panel.prop(self, "EdgeOffset")
|
||||
|
||||
# Wall block sizing
|
||||
header, panel = layout.panel("WALLFACTORY_PT_BLOCKS", default_closed=False)
|
||||
header.label(text="Block Size")
|
||||
if panel:
|
||||
panel.prop(self, "MergeBlock")
|
||||
# add checkbox for "fixed" sizing (ignore variance) a.k.a. bricks
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "Width")
|
||||
col.prop(self, "WidthVariance")
|
||||
col.prop(self, "WidthMinimum")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "Height")
|
||||
col.prop(self, "HeightVariance")
|
||||
col.prop(self, "HeightMinimum")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "Depth")
|
||||
col.prop(self, "DepthVariance")
|
||||
col.prop(self, "DepthMinimum")
|
||||
|
||||
# grout settings
|
||||
header, panel = layout.panel("WALLFACTORY_PT_GROUT", default_closed=True)
|
||||
header.label(text="Grout")
|
||||
if panel:
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "Grout")
|
||||
col.prop(self, "GroutVariance")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "GroutDepth")
|
||||
col.prop(self, "GroutDepthVariance")
|
||||
|
||||
# Openings (doors, windows; arched)
|
||||
header, panel = layout.panel("WALLFACTORY_PT_OPENINGS", default_closed=True)
|
||||
header.use_property_split = False
|
||||
header.prop(self, 'Opening1Tog', text='')
|
||||
header.label(text="Openings")
|
||||
if panel:
|
||||
openings_col = panel.column()
|
||||
openings_col.enabled = self.Opening1Tog
|
||||
openings_col.use_property_split = True
|
||||
col = openings_col.column(align=True)
|
||||
col.prop(self, "Opening1Width")
|
||||
col.prop(self, "Opening1Height")
|
||||
col.prop(self, "Opening1X")
|
||||
col.prop(self, "Opening1Z")
|
||||
col.prop(self, "Opening1Bevel")
|
||||
openings_col.prop(self, "Opening1Repeat", toggle=True)
|
||||
openings_col.prop(self, "Opening1TopArchTog")
|
||||
col = openings_col.column(align=True)
|
||||
col.enabled = self.Opening1TopArchTog
|
||||
col.prop(self, "Opening1TopArch")
|
||||
col.prop(self, "Opening1TopArchThickness")
|
||||
openings_col.prop(self, "Opening1BtmArchTog")
|
||||
col = openings_col.column(align=True)
|
||||
col.enabled = self.Opening1BtmArchTog
|
||||
col.prop(self, "Opening1BtmArch")
|
||||
col.prop(self, "Opening1BtmArchThickness")
|
||||
|
||||
# Slots (narrow openings)
|
||||
header, panel = layout.panel("WALLFACTORY_PT_SLOTS", default_closed=True)
|
||||
header.use_property_split = False
|
||||
header.prop(self, 'SlotTog', text='')
|
||||
header.label(text="Slots")
|
||||
if panel:
|
||||
panel.enabled = self.SlotTog
|
||||
panel.prop(self, "SlotRpt")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "SlotX")
|
||||
col.prop(self, "SlotGap")
|
||||
panel.prop(self, "SlotV")
|
||||
col = panel.column(align=True)
|
||||
col.enabled = self.SlotV
|
||||
col.prop(self, "SlotVH")
|
||||
col.prop(self, "SlotVBtm")
|
||||
panel.prop(self, "SlotH")
|
||||
col = panel.column(align=True)
|
||||
col.enabled = self.SlotH
|
||||
col.prop(self, "SlotHW")
|
||||
col.prop(self, "SlotHBtm")
|
||||
|
||||
# Crenels, gaps in top of wall
|
||||
header, panel = layout.panel("WALLFACTORY_PT_CRENELS", default_closed=True)
|
||||
header.use_property_split = False
|
||||
header.prop(self, 'CrenelTog', text='')
|
||||
header.label(text="Crenels")
|
||||
if panel:
|
||||
panel.enabled = self.CrenelTog
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "CrenelXP")
|
||||
col.prop(self, "CrenelZP")
|
||||
|
||||
# Shelfing (protrusions)
|
||||
header, panel = layout.panel("WALLFACTORY_PT_SHELF", default_closed=True)
|
||||
header.use_property_split = False
|
||||
header.prop(self, 'ShelfTog', text='')
|
||||
header.label(text="Shelf")
|
||||
if panel:
|
||||
panel.enabled = self.ShelfTog
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "ShelfX")
|
||||
col.prop(self, "ShelfZ")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "ShelfW")
|
||||
col.prop(self, "ShelfH")
|
||||
col.prop(self, "ShelfD")
|
||||
panel.prop(self, "ShelfBack")
|
||||
|
||||
# Steps
|
||||
header, panel = layout.panel("WALLFACTORY_PT_STEPS", default_closed=True)
|
||||
header.use_property_split = False
|
||||
header.prop(self, 'StepTog', text='')
|
||||
header.label(text="Steps")
|
||||
if panel:
|
||||
panel.enabled = self.StepTog
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "StepX")
|
||||
col.prop(self, "StepZ")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "StepH")
|
||||
col.prop(self, "StepW")
|
||||
col.prop(self, "StepD")
|
||||
col = panel.column(align=True)
|
||||
col.prop(self, "StepV")
|
||||
col.prop(self, "StepT")
|
||||
col = panel.column(align=True)
|
||||
panel.prop(self, "StepLeft")
|
||||
panel.prop(self, "StepOnly")
|
||||
panel.prop(self, "StepBack")
|
||||
|
||||
if self.change == False:
|
||||
header, panel = layout.panel("WALLFACTORY_PT_TRANSFORM", default_closed=True)
|
||||
header.label(text="Transform")
|
||||
if panel:
|
||||
draw_transform_props(self, panel)
|
||||
|
||||
# Respond to UI - get the properties set by user.
|
||||
# Check and process UI settings to generate masonry
|
||||
|
||||
def execute(self, context):
|
||||
global radialized
|
||||
global slope
|
||||
global openingSpecs
|
||||
global bigBlock
|
||||
global shelfExt
|
||||
global stepMod
|
||||
global stepLeft
|
||||
global shelfBack
|
||||
global stepOnly
|
||||
global stepBack
|
||||
|
||||
# Create the wall when enabled (skip regen iterations when off)
|
||||
if not self.ConstructTog:
|
||||
return {'FINISHED'}
|
||||
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
# enter the settings for the wall dimensions (area)
|
||||
# start can't be zero - min/max don't matter [if max less than end] but zero don't workie.
|
||||
# start can't exceed end.
|
||||
if not self.WallStart or self.WallStart >= self.WallEnd:
|
||||
self.WallStart = NOTZERO # Reset UI if input out of bounds...
|
||||
|
||||
dims['s'] = self.WallStart
|
||||
dims['e'] = self.WallEnd
|
||||
dims['b'] = self.WallBottom
|
||||
dims['t'] = self.WallTop
|
||||
|
||||
settings['eoff'] = self.EdgeOffset
|
||||
|
||||
# retrieve the settings for the wall block properties
|
||||
settings['w'] = self.Width
|
||||
settings['wv'] = self.WidthVariance
|
||||
settings['wm'] = self.WidthMinimum
|
||||
|
||||
if not radialized:
|
||||
settings['sdv'] = settings['w']
|
||||
else:
|
||||
settings['sdv'] = 0.12
|
||||
|
||||
settings['h'] = self.Height
|
||||
settings['hv'] = self.HeightVariance
|
||||
settings['hm'] = self.HeightMinimum
|
||||
|
||||
settings['d'] = self.Depth
|
||||
settings['dv'] = self.DepthVariance
|
||||
settings['dm'] = self.DepthMinimum
|
||||
|
||||
if self.MergeBlock:
|
||||
bigBlock = 1
|
||||
else:
|
||||
bigBlock = 0
|
||||
|
||||
settings['g'] = self.Grout
|
||||
settings['gv'] = self.GroutVariance
|
||||
settings['gd'] = self.GroutDepth
|
||||
settings['gdv'] = self.GroutDepthVariance
|
||||
|
||||
if self.GroutEdge:
|
||||
settings['ge'] = 1
|
||||
else:
|
||||
settings['ge'] = 0
|
||||
|
||||
# set wall shape modifiers
|
||||
if self.RadialTog:
|
||||
radialized = 1
|
||||
# eliminate to allow user control for start/completion?
|
||||
dims['s'] = 0.0 # complete radial
|
||||
if dims['e'] > PI * 2:
|
||||
dims['e'] = PI * 2 # max end for circle
|
||||
if dims['b'] < settings['g']:
|
||||
dims['b'] = settings['g'] # min bottom for grout extension
|
||||
else:
|
||||
radialized = 0
|
||||
|
||||
if self.SlopeTog:
|
||||
slope = 1
|
||||
else:
|
||||
slope = 0
|
||||
|
||||
shelfExt = 0
|
||||
shelfBack = 0
|
||||
|
||||
# Add shelf if enabled
|
||||
if self.ShelfTog:
|
||||
shelfExt = 1
|
||||
shelfSpecs['h'] = self.ShelfH
|
||||
shelfSpecs['w'] = self.ShelfW
|
||||
shelfSpecs['d'] = self.ShelfD
|
||||
shelfSpecs['x'] = self.ShelfX
|
||||
shelfSpecs['z'] = self.ShelfZ
|
||||
|
||||
if self.ShelfBack:
|
||||
shelfBack = 1
|
||||
stepMod = 0
|
||||
stepLeft = 0
|
||||
stepOnly = 0
|
||||
stepBack = 0
|
||||
|
||||
# Make steps if enabled
|
||||
if self.StepTog:
|
||||
stepMod = 1
|
||||
stepSpecs['x'] = self.StepX
|
||||
stepSpecs['z'] = self.StepZ
|
||||
stepSpecs['h'] = self.StepH
|
||||
stepSpecs['w'] = self.StepW
|
||||
stepSpecs['d'] = self.StepD
|
||||
stepSpecs['v'] = self.StepV
|
||||
stepSpecs['t'] = self.StepT
|
||||
|
||||
if self.StepLeft:
|
||||
stepLeft = 1
|
||||
|
||||
if self.StepOnly:
|
||||
stepOnly = 1
|
||||
|
||||
if self.StepBack:
|
||||
stepBack = 1
|
||||
|
||||
# enter the settings for the openings
|
||||
# when openings overlap they create inverse stonework - interesting but not the desired effect :)
|
||||
# if opening width == indent * 2 the edge blocks fail (row of blocks cross opening) - bug.
|
||||
openingSpecs = []
|
||||
openingIdx = 0 # track opening array references for multiple uses
|
||||
|
||||
# general openings with arch options - can be windows or doors.
|
||||
if self.Opening1Tog:
|
||||
# set defaults...
|
||||
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1,
|
||||
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
|
||||
|
||||
openingSpecs[openingIdx]['w'] = self.Opening1Width
|
||||
openingSpecs[openingIdx]['h'] = self.Opening1Height
|
||||
openingSpecs[openingIdx]['x'] = self.Opening1X
|
||||
openingSpecs[openingIdx]['z'] = self.Opening1Z
|
||||
openingSpecs[openingIdx]['rp'] = self.Opening1Repeat
|
||||
|
||||
if self.Opening1TopArchTog:
|
||||
openingSpecs[openingIdx]['v'] = self.Opening1TopArch
|
||||
openingSpecs[openingIdx]['t'] = self.Opening1TopArchThickness
|
||||
|
||||
if self.Opening1BtmArchTog:
|
||||
openingSpecs[openingIdx]['vl'] = self.Opening1BtmArch
|
||||
openingSpecs[openingIdx]['tl'] = self.Opening1BtmArchThickness
|
||||
|
||||
openingSpecs[openingIdx]['b'] = self.Opening1Bevel
|
||||
|
||||
openingIdx += 1 # count window/door/arch openings
|
||||
|
||||
# Slots (narrow openings)
|
||||
if self.SlotTog:
|
||||
|
||||
if self.SlotV: # vertical slots
|
||||
# set defaults...
|
||||
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
|
||||
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
|
||||
|
||||
openingSpecs[openingIdx]['w'] = self.SlotGap
|
||||
openingSpecs[openingIdx]['h'] = self.SlotVH
|
||||
openingSpecs[openingIdx]['x'] = self.SlotX
|
||||
openingSpecs[openingIdx]['z'] = self.SlotVBtm
|
||||
openingSpecs[openingIdx]['rp'] = self.SlotRpt
|
||||
|
||||
# make them pointy...
|
||||
openingSpecs[openingIdx]['v'] = self.SlotGap
|
||||
openingSpecs[openingIdx]['t'] = self.SlotGap / 2
|
||||
openingSpecs[openingIdx]['vl'] = self.SlotGap
|
||||
openingSpecs[openingIdx]['tl'] = self.SlotGap / 2
|
||||
|
||||
openingIdx += 1 # count vertical slot openings
|
||||
|
||||
# need to handle overlap of H and V slots...
|
||||
|
||||
if self.SlotH: # Horizontal slots
|
||||
# set defaults...
|
||||
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
|
||||
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
|
||||
|
||||
openingSpecs[openingIdx]['w'] = self.SlotHW
|
||||
openingSpecs[openingIdx]['h'] = self.SlotGap
|
||||
openingSpecs[openingIdx]['x'] = self.SlotX
|
||||
openingSpecs[openingIdx]['z'] = self.SlotHBtm
|
||||
# horizontal repeat isn't same spacing as vertical...
|
||||
openingSpecs[openingIdx]['rp'] = self.SlotRpt
|
||||
|
||||
# make them pointy...
|
||||
openingIdx += 1 # count horizontal slot openings
|
||||
|
||||
# Crenellations (top row openings)
|
||||
if self.CrenelTog:
|
||||
|
||||
# add bottom arch option?
|
||||
# perhaps a repeat toggle...
|
||||
# if crenel opening overlaps with arch opening it fills with blocks...
|
||||
|
||||
# set defaults...
|
||||
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 1,
|
||||
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
|
||||
|
||||
wallW = self.WallEnd - self.WallStart
|
||||
crenelW = wallW * self.CrenelXP # Width % opening.
|
||||
|
||||
wallH = self.WallTop - self.WallBottom
|
||||
crenelH = wallH * self.CrenelZP # % proportional height.
|
||||
|
||||
openingSpecs[openingIdx]['w'] = crenelW
|
||||
openingSpecs[openingIdx]['h'] = crenelH
|
||||
|
||||
# calculate the spacing between openings.
|
||||
# this isn't the absolute start (left),
|
||||
# it's opening center offset relative to cursor (space between openings)...
|
||||
openingSpecs[openingIdx]['x'] = crenelW * 2 - 1 # assume standard spacing
|
||||
|
||||
if not radialized: # normal wall?
|
||||
# set indent 0 (center) if opening is 50% or more of wall width, no repeat.
|
||||
if crenelW * 2 >= wallW:
|
||||
openingSpecs[openingIdx]['x'] = 0
|
||||
openingSpecs[openingIdx]['rp'] = 0
|
||||
# set bottom of opening (center of hole)
|
||||
openingSpecs[openingIdx]['z'] = self.WallTop - (crenelH / 2)
|
||||
|
||||
openingIdx += 1 # count crenel openings
|
||||
|
||||
# Process the user settings to generate a wall
|
||||
# generate the list of vertices for the wall...
|
||||
verts_array, faces_array = createWall(
|
||||
radialized, slope, openingSpecs, bigBlock,
|
||||
shelfExt, shelfBack, stepMod, stepLeft, stepOnly,
|
||||
stepBack
|
||||
)
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Wall' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
mesh = bpy.data.meshes.new("Wall")
|
||||
mesh.from_pydata(verts_array, [], faces_array)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
mesh = bpy.data.meshes.new("Wall")
|
||||
mesh.from_pydata(verts_array, [], faces_array)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
mesh.update()
|
||||
|
||||
obj.data["Wall"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in WallParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
mesh = bpy.data.meshes.new("TMP")
|
||||
mesh.from_pydata(verts_array, [], faces_array)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def WallParameters():
|
||||
WallParameters = [
|
||||
"ConstructTog",
|
||||
"RadialTog",
|
||||
"SlopeTog",
|
||||
"WallStart",
|
||||
"WallEnd",
|
||||
"WallBottom",
|
||||
"WallTop",
|
||||
"EdgeOffset",
|
||||
"Width",
|
||||
"WidthVariance",
|
||||
"WidthMinimum",
|
||||
"Height",
|
||||
"HeightVariance",
|
||||
"HeightMinimum",
|
||||
"Depth",
|
||||
"DepthVariance",
|
||||
"DepthMinimum",
|
||||
"MergeBlock",
|
||||
"Grout",
|
||||
"GroutVariance",
|
||||
"GroutDepth",
|
||||
"GroutDepthVariance",
|
||||
"GroutEdge",
|
||||
"Opening1Tog",
|
||||
"Opening1Width",
|
||||
"Opening1Height",
|
||||
"Opening1X",
|
||||
"Opening1Z",
|
||||
"Opening1Repeat",
|
||||
"Opening1TopArchTog",
|
||||
"Opening1TopArch",
|
||||
"Opening1TopArchThickness",
|
||||
"Opening1BtmArchTog",
|
||||
"Opening1BtmArch",
|
||||
"Opening1BtmArchThickness",
|
||||
"CrenelTog",
|
||||
"CrenelXP",
|
||||
"CrenelZP",
|
||||
"SlotTog",
|
||||
"SlotRpt",
|
||||
"SlotWdg",
|
||||
"SlotX",
|
||||
"SlotGap",
|
||||
"SlotV",
|
||||
"SlotVH",
|
||||
"SlotVBtm",
|
||||
"SlotH",
|
||||
"SlotHW",
|
||||
"SlotHBtm",
|
||||
"ShelfTog",
|
||||
"ShelfX",
|
||||
"ShelfZ",
|
||||
"ShelfH",
|
||||
"ShelfW",
|
||||
"ShelfD",
|
||||
"ShelfBack",
|
||||
"StepTog",
|
||||
"StepX",
|
||||
"StepZ",
|
||||
"StepH",
|
||||
"StepW",
|
||||
"StepD",
|
||||
"StepV",
|
||||
"StepT",
|
||||
"StepLeft",
|
||||
"StepOnly",
|
||||
"StepBack",
|
||||
]
|
||||
return WallParameters
|
||||
@@ -0,0 +1,460 @@
|
||||
# SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Contributed to by:
|
||||
# Pontiac, Fourmadmen, varkenvarken, tuga3d, meta-androcto, metalliandy #
|
||||
# dreampainter, cotejrp1, liero, Kayo Phoenix, sugiany, dommetysk, Jambay #
|
||||
# Phymec, Anthony D'Agostino, Pablo Vazquez, Richard Wilks, lijenstina, #
|
||||
# Sjaak-de-Draak, Phil Cote, cotejrp1, xyz presets by elfnor, revolt_randy, #
|
||||
# Vladimir Spivak (cwolf3d), Jonathan Lampel #
|
||||
|
||||
# Note: Blocks has to be loaded before the WallFactory or the script
|
||||
# will not work properly after (F8) reload
|
||||
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
importlib.reload(add_mesh_star)
|
||||
importlib.reload(add_mesh_twisted_torus)
|
||||
importlib.reload(add_mesh_gemstones)
|
||||
importlib.reload(add_mesh_gears)
|
||||
importlib.reload(add_mesh_3d_function_surface)
|
||||
importlib.reload(add_mesh_round_cube)
|
||||
importlib.reload(add_mesh_supertoroid)
|
||||
importlib.reload(add_mesh_pyramid)
|
||||
importlib.reload(add_mesh_torusknot)
|
||||
importlib.reload(add_mesh_honeycomb)
|
||||
importlib.reload(add_mesh_teapot)
|
||||
importlib.reload(add_mesh_pipe_joint)
|
||||
importlib.reload(add_mesh_solid)
|
||||
importlib.reload(add_mesh_round_brilliant)
|
||||
importlib.reload(add_mesh_menger_sponge)
|
||||
importlib.reload(add_mesh_vertex)
|
||||
importlib.reload(add_empty_as_parent)
|
||||
importlib.reload(add_mesh_beam_builder)
|
||||
importlib.reload(Blocks)
|
||||
importlib.reload(Wallfactory)
|
||||
importlib.reload(add_mesh_triangles)
|
||||
importlib.reload(preferences)
|
||||
else:
|
||||
from . import add_mesh_star
|
||||
from . import add_mesh_twisted_torus
|
||||
from . import add_mesh_gemstones
|
||||
from . import add_mesh_gears
|
||||
from . import add_mesh_3d_function_surface
|
||||
from . import add_mesh_round_cube
|
||||
from . import add_mesh_supertoroid
|
||||
from . import add_mesh_pyramid
|
||||
from . import add_mesh_torusknot
|
||||
from . import add_mesh_honeycomb
|
||||
from . import add_mesh_teapot
|
||||
from . import add_mesh_pipe_joint
|
||||
from . import add_mesh_solid
|
||||
from . import add_mesh_round_brilliant
|
||||
from . import add_mesh_menger_sponge
|
||||
from . import add_mesh_vertex
|
||||
from . import add_empty_as_parent
|
||||
from . import add_mesh_beam_builder
|
||||
from . import Blocks
|
||||
from . import Wallfactory
|
||||
from . import add_mesh_triangles
|
||||
from . import preferences
|
||||
|
||||
from .add_mesh_rocks import __init__
|
||||
from .add_mesh_rocks import rockgen
|
||||
|
||||
import bpy
|
||||
from bpy.types import Menu
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_vert_add(Menu):
|
||||
# Define the "Single Vert" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_vert_add"
|
||||
bl_label = "Single Vert"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
layout.operator("mesh.primitive_vert_add",
|
||||
text="Add Single Vert")
|
||||
layout.separator()
|
||||
layout.operator("mesh.primitive_emptyvert_add",
|
||||
text="Object Origin Only")
|
||||
layout.operator("mesh.primitive_symmetrical_vert_add",
|
||||
text="Origin & Vert Mirrored")
|
||||
layout.operator("mesh.primitive_symmetrical_empty_add",
|
||||
text="Object Origin Mirrored")
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_gears_add(Menu):
|
||||
# Define the "Gears" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_gears_add"
|
||||
bl_label = "Gears"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
oper = layout.operator("mesh.primitive_gear", text="Gear")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_worm_gear", text="Worm")
|
||||
oper.change = False
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_gemstones_add(Menu):
|
||||
# Define the "Gemstones" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_gemstones_add"
|
||||
bl_label = "Gemstones"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
oper = layout.operator("mesh.primitive_brilliant_add", text="Brilliant")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_diamond_add", text="Diamond")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_gem_add", text="Gem")
|
||||
oper.change = False
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_math_add(Menu):
|
||||
# Define the "Math Function" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_math_add"
|
||||
bl_label = "Math Functions"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
layout.operator("mesh.primitive_z_function_surface",
|
||||
text="Z Math Surface")
|
||||
layout.operator("mesh.primitive_xyz_function_surface",
|
||||
text="XYZ Math Surface")
|
||||
self.layout.operator("mesh.primitive_solid_add", text="Regular Solid")
|
||||
self.layout.operator("mesh.make_triangle", text="Triangle")
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_extras_add(Menu):
|
||||
# Define the "Extra Objects" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_extras_add"
|
||||
bl_label = "Extras"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
oper = layout.operator("mesh.add_mesh_rock", text="Rock Generator")
|
||||
oper = layout.operator("mesh.add_beam", text="Beam Builder")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.wall_add", text="Wall Factory")
|
||||
oper.change = False
|
||||
layout.separator()
|
||||
oper = layout.operator("mesh.primitive_star_add", text="Simple Star")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_steppyramid_add", text="Step Pyramid")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.honeycomb_add", text="Honeycomb")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_teapot_add", text="Teapot+")
|
||||
oper = layout.operator("mesh.menger_sponge_add", text="Menger Sponge")
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_torus_add(Menu):
|
||||
# Define the "Torus Objects" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_torus_add"
|
||||
bl_label = "Torus Objects"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
oper = layout.operator("mesh.primitive_twisted_torus_add", text="Twisted Torus")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_supertoroid_add", text="Supertoroid")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_torusknot_add", text="Torus Knot")
|
||||
oper.change = False
|
||||
|
||||
|
||||
class VIEW3D_MT_mesh_pipe_joints_add(Menu):
|
||||
# Define the "Pipe Joints" menu
|
||||
bl_idname = "VIEW3D_MT_mesh_pipe_joints_add"
|
||||
bl_label = "Pipe Joints"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
oper = layout.operator("mesh.primitive_elbow_joint_add", text="Elbow")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_tee_joint_add", text="T-Joint")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_wye_joint_add", text="Y-Joint")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_cross_joint_add", text="Cross-Joint")
|
||||
oper.change = False
|
||||
oper = layout.operator("mesh.primitive_n_joint_add", text="N-Joint")
|
||||
oper.change = False
|
||||
|
||||
# Register all operators and panels
|
||||
|
||||
|
||||
# Define "Extras" menu
|
||||
def menu_func(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
|
||||
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||
|
||||
if prefs.show_round_cube:
|
||||
oper = layout.operator("mesh.primitive_round_cube_add", text="Round Cube", icon='SPHERE')
|
||||
oper.change = False
|
||||
|
||||
layout.separator()
|
||||
|
||||
if prefs.show_single_vert:
|
||||
layout.menu("VIEW3D_MT_mesh_vert_add", text="Single Vert", icon='DECORATE')
|
||||
|
||||
if prefs.show_torus_objects:
|
||||
layout.menu("VIEW3D_MT_mesh_torus_add", text="Torus Objects", icon='MESH_TORUS')
|
||||
|
||||
if prefs.show_math_functions:
|
||||
layout.menu("VIEW3D_MT_mesh_math_add", text="Math Functions", icon='GRAPH')
|
||||
|
||||
if prefs.show_gears:
|
||||
layout.menu("VIEW3D_MT_mesh_gears_add", text="Gears", icon='PREFERENCES')
|
||||
|
||||
if prefs.show_pipe_joints:
|
||||
layout.menu("VIEW3D_MT_mesh_pipe_joints_add", text="Pipe Joints", icon='IPO_CONSTANT')
|
||||
|
||||
if prefs.show_gemstones:
|
||||
layout.menu("VIEW3D_MT_mesh_gemstones_add", text="Gemstones", icon="MESH_ICOSPHERE")
|
||||
|
||||
if prefs.show_extras:
|
||||
layout.menu("VIEW3D_MT_mesh_extras_add", text="Extras", icon="PACKAGE")
|
||||
|
||||
if prefs.show_parent_to_empty:
|
||||
layout.separator()
|
||||
layout.operator("object.parent_to_empty", text="Parent to Empty", icon="OUTLINER_OB_EMPTY")
|
||||
|
||||
|
||||
def Extras_contex_menu(self, context):
|
||||
bl_label = 'Change'
|
||||
|
||||
obj = context.object
|
||||
layout = self.layout
|
||||
|
||||
if obj is None or obj.data is None:
|
||||
return
|
||||
|
||||
if 'Gear' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_gear", text="Change Gear")
|
||||
props.change = True
|
||||
for prm in add_mesh_gears.GearParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'WormGear' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_worm_gear", text="Change WormGear")
|
||||
props.change = True
|
||||
for prm in add_mesh_gears.WormGearParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Beam' in obj.data.keys():
|
||||
props = layout.operator("mesh.add_beam", text="Change Beam")
|
||||
props.change = True
|
||||
for prm in add_mesh_beam_builder.BeamParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Wall' in obj.data.keys():
|
||||
props = layout.operator("mesh.wall_add", text="Change Wall")
|
||||
props.change = True
|
||||
for prm in Wallfactory.WallParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'ElbowJoint' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_elbow_joint_add", text="Change ElbowJoint")
|
||||
props.change = True
|
||||
for prm in add_mesh_pipe_joint.ElbowJointParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'TeeJoint' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_tee_joint_add", text="Change TeeJoint")
|
||||
props.change = True
|
||||
for prm in add_mesh_pipe_joint.TeeJointParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'WyeJoint' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_wye_joint_add", text="Change WyeJoint")
|
||||
props.change = True
|
||||
for prm in add_mesh_pipe_joint.WyeJointParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'CrossJoint' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_cross_joint_add", text="Change CrossJoint")
|
||||
props.change = True
|
||||
for prm in add_mesh_pipe_joint.CrossJointParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'NJoint' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_n_joint_add", text="Change NJoint")
|
||||
props.change = True
|
||||
for prm in add_mesh_pipe_joint.NJointParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Diamond' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_diamond_add", text="Change Diamond")
|
||||
props.change = True
|
||||
for prm in add_mesh_gemstones.DiamondParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Gem' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_gem_add", text="Change Gem")
|
||||
props.change = True
|
||||
for prm in add_mesh_gemstones.GemParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Brilliant' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_brilliant_add", text="Change Brilliant")
|
||||
props.change = True
|
||||
for prm in add_mesh_round_brilliant.BrilliantParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Roundcube' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_round_cube_add", text="Change Roundcube")
|
||||
props.change = True
|
||||
for prm in add_mesh_round_cube.RoundCubeParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'TorusKnot' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_torusknot_add", text="Change TorusKnot")
|
||||
props.change = True
|
||||
for prm in add_mesh_torusknot.TorusKnotParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'SuperToroid' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_supertoroid_add", text="Change SuperToroid")
|
||||
props.change = True
|
||||
for prm in add_mesh_supertoroid.SuperToroidParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'TwistedTorus' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_twisted_torus_add", text="Change TwistedTorus")
|
||||
props.change = True
|
||||
for prm in add_mesh_twisted_torus.TwistedTorusParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Star' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_star_add", text="Change Star")
|
||||
props.change = True
|
||||
for prm in add_mesh_star.StarParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'Pyramid' in obj.data.keys():
|
||||
props = layout.operator("mesh.primitive_steppyramid_add", text="Change Pyramid")
|
||||
props.change = True
|
||||
for prm in add_mesh_pyramid.PyramidParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
if 'HoneyComb' in obj.data.keys():
|
||||
props = layout.operator("mesh.honeycomb_add", text="Change HoneyComb")
|
||||
props.change = True
|
||||
for prm in add_mesh_honeycomb.HoneyCombParameters():
|
||||
setattr(props, prm, obj.data[prm])
|
||||
layout.separator()
|
||||
|
||||
|
||||
# Register
|
||||
classes = [
|
||||
VIEW3D_MT_mesh_vert_add,
|
||||
VIEW3D_MT_mesh_gears_add,
|
||||
VIEW3D_MT_mesh_gemstones_add,
|
||||
VIEW3D_MT_mesh_math_add,
|
||||
VIEW3D_MT_mesh_extras_add,
|
||||
VIEW3D_MT_mesh_torus_add,
|
||||
VIEW3D_MT_mesh_pipe_joints_add,
|
||||
add_mesh_star.AddStar,
|
||||
add_mesh_twisted_torus.AddTwistedTorus,
|
||||
add_mesh_gemstones.AddDiamond,
|
||||
add_mesh_gemstones.AddGem,
|
||||
add_mesh_gears.AddGear,
|
||||
add_mesh_gears.AddWormGear,
|
||||
add_mesh_3d_function_surface.AddZFunctionSurface,
|
||||
add_mesh_3d_function_surface.AddXYZFunctionSurface,
|
||||
add_mesh_round_cube.AddRoundCube,
|
||||
add_mesh_supertoroid.add_supertoroid,
|
||||
add_mesh_pyramid.AddPyramid,
|
||||
add_mesh_torusknot.AddTorusKnot,
|
||||
add_mesh_honeycomb.add_mesh_honeycomb,
|
||||
add_mesh_teapot.AddTeapot,
|
||||
add_mesh_pipe_joint.AddElbowJoint,
|
||||
add_mesh_pipe_joint.AddTeeJoint,
|
||||
add_mesh_pipe_joint.AddWyeJoint,
|
||||
add_mesh_pipe_joint.AddCrossJoint,
|
||||
add_mesh_pipe_joint.AddNJoint,
|
||||
add_mesh_solid.Solids,
|
||||
add_mesh_round_brilliant.MESH_OT_primitive_brilliant_add,
|
||||
add_mesh_menger_sponge.AddMengerSponge,
|
||||
add_mesh_vertex.AddVert,
|
||||
add_mesh_vertex.AddEmptyVert,
|
||||
add_mesh_vertex.AddSymmetricalEmpty,
|
||||
add_mesh_vertex.AddSymmetricalVert,
|
||||
add_empty_as_parent.P2E,
|
||||
add_empty_as_parent.PreFix,
|
||||
add_mesh_beam_builder.addBeam,
|
||||
Wallfactory.add_mesh_wallb,
|
||||
add_mesh_triangles.MakeTriangle,
|
||||
preferences.AddMeshExtraObjectsPreferences,
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
import os
|
||||
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
add_mesh_rocks.register()
|
||||
|
||||
# Add "Extras" menu to the "Add Mesh" menu and context menu.
|
||||
bpy.types.VIEW3D_MT_mesh_add.append(menu_func)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.prepend(Extras_contex_menu)
|
||||
|
||||
# Part of 4.3 may be back-ported to 4.2.
|
||||
if register_preset_path := getattr(bpy.utils, "register_preset_path", None):
|
||||
register_preset_path(os.path.join(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def unregister():
|
||||
import os
|
||||
|
||||
# Remove "Extras" menu from the "Add Mesh" menu and context menu.
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(Extras_contex_menu)
|
||||
bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)
|
||||
|
||||
from bpy.utils import unregister_class
|
||||
for cls in reversed(classes):
|
||||
unregister_class(cls)
|
||||
|
||||
add_mesh_rocks.unregister()
|
||||
|
||||
# Part of 4.3 may be back-ported to 4.2.
|
||||
if unregister_preset_path := getattr(bpy.utils, "unregister_preset_path", None):
|
||||
unregister_preset_path(os.path.join(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
@@ -0,0 +1,133 @@
|
||||
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Original Author Liero
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
|
||||
def centro(sel):
|
||||
x = sum([obj.location[0] for obj in sel]) / len(sel)
|
||||
y = sum([obj.location[1] for obj in sel]) / len(sel)
|
||||
z = sum([obj.location[2] for obj in sel]) / len(sel)
|
||||
return (x, y, z)
|
||||
|
||||
|
||||
class P2E(Operator):
|
||||
bl_idname = "object.parent_to_empty"
|
||||
bl_label = "Parent to Empty"
|
||||
bl_description = "Parent selected objects to a new Empty"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
nombre: StringProperty(
|
||||
name="",
|
||||
default='OBJECTS',
|
||||
description='Give the empty / group a name'
|
||||
)
|
||||
grupo: BoolProperty(
|
||||
name="Create Group",
|
||||
default=False,
|
||||
description="Also add objects to a group"
|
||||
)
|
||||
locat: EnumProperty(
|
||||
name='',
|
||||
items=[('CURSOR', 'Cursor', 'Cursor'), ('ACTIVE', 'Active', 'Active'),
|
||||
('CENTER', 'Center', 'Selection Center')],
|
||||
description='Empty location',
|
||||
default='CENTER'
|
||||
)
|
||||
renom: BoolProperty(
|
||||
name="Add Prefix",
|
||||
default=False,
|
||||
description="Add prefix to objects name"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
objs = context.selected_objects
|
||||
return (len(objs) > 0)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "nombre")
|
||||
column = layout.column(align=True)
|
||||
column.prop(self, "locat")
|
||||
column.prop(self, "grupo")
|
||||
column.prop(self, "renom")
|
||||
|
||||
def execute(self, context):
|
||||
objs = context.selected_objects
|
||||
act = context.object
|
||||
sce = context.scene
|
||||
|
||||
try:
|
||||
bpy.ops.object.mode_set()
|
||||
except:
|
||||
pass
|
||||
|
||||
if self.locat == 'CURSOR':
|
||||
loc = sce.cursor.location
|
||||
elif self.locat == 'ACTIVE':
|
||||
loc = act.location
|
||||
else:
|
||||
loc = centro(objs)
|
||||
|
||||
bpy.ops.object.add(type='EMPTY', location=loc)
|
||||
context.object.name = self.nombre
|
||||
context.object.show_name = True
|
||||
context.object.show_in_front = True
|
||||
|
||||
if self.grupo:
|
||||
bpy.ops.collection.create(name=self.nombre)
|
||||
bpy.ops.collection.objects_add_active()
|
||||
|
||||
for o in objs:
|
||||
o.select_set(True)
|
||||
if not o.parent:
|
||||
bpy.ops.object.parent_set(type='OBJECT')
|
||||
if self.grupo:
|
||||
bpy.ops.collection.objects_add_active()
|
||||
o.select_set(False)
|
||||
for o in objs:
|
||||
if self.renom:
|
||||
o.name = self.nombre + '_' + o.name
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class PreFix(Operator):
|
||||
bl_idname = "object.toggle_prefix"
|
||||
bl_label = "Toggle Sufix"
|
||||
bl_description = "Toggle parent name as sufix for c"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
act = context.object
|
||||
return (act and act.type == 'EMPTY')
|
||||
|
||||
def execute(self, context):
|
||||
act = context.object
|
||||
objs = act.children
|
||||
prefix = act.name + '_'
|
||||
remove = False
|
||||
for o in objs:
|
||||
if o.name.startswith(prefix):
|
||||
remove = True
|
||||
break
|
||||
|
||||
if remove is True:
|
||||
for o in objs:
|
||||
if o.name.startswith(prefix):
|
||||
o.name = o.name.partition(prefix)[2]
|
||||
else:
|
||||
for o in objs:
|
||||
o.name = prefix + o.name
|
||||
|
||||
return {'FINISHED'}
|
||||
@@ -0,0 +1,612 @@
|
||||
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Original by Buerbaum Martin (Pontiac), Elod Csirmaz
|
||||
|
||||
import bpy
|
||||
import math
|
||||
import numpy
|
||||
from mathutils import *
|
||||
from math import *
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
BoolProperty,
|
||||
)
|
||||
|
||||
|
||||
# List of safe functions for eval()
|
||||
safe_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
|
||||
'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot',
|
||||
'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians',
|
||||
'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'gcd']
|
||||
|
||||
# Use the list to filter the local namespace
|
||||
safe_dict = dict((k, globals().get(k, None)) for k in safe_list)
|
||||
safe_dict['math'] = math
|
||||
safe_dict['numpy'] = safe_dict['np'] = numpy
|
||||
safe_dict['lcm'] = numpy.lcm
|
||||
safe_dict['max'] = max
|
||||
safe_dict['min'] = min
|
||||
|
||||
|
||||
# Stores the values of a list of properties and the
|
||||
# operator id in a property group ('recall_op') inside the object
|
||||
# Could (in theory) be used for non-objects.
|
||||
# Note: Replaces any existing property group with the same name!
|
||||
# ob ... Object to store the properties in
|
||||
# op ... The operator that should be used
|
||||
# op_args ... A dictionary with valid Blender
|
||||
# properties (operator arguments/parameters)
|
||||
|
||||
|
||||
# Create a new mesh (object) from verts/edges/faces
|
||||
# verts/edges/faces ... List of vertices/edges/faces for the
|
||||
# new mesh (as used in from_pydata)
|
||||
# name ... Name of the new mesh (& object)
|
||||
|
||||
def create_mesh_object(context, verts, edges, faces, name):
|
||||
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
|
||||
# Make a mesh from a list of verts/edges/faces
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Update mesh geometry after adding stuff
|
||||
mesh.update()
|
||||
|
||||
from bpy_extras import object_utils
|
||||
return object_utils.object_data_add(context, mesh, operator=None)
|
||||
|
||||
|
||||
# A very simple "bridge" tool
|
||||
|
||||
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
||||
faces = []
|
||||
|
||||
if not vertIdx1 or not vertIdx2:
|
||||
return None
|
||||
|
||||
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
||||
return None
|
||||
|
||||
fan = False
|
||||
if (len(vertIdx1) != len(vertIdx2)):
|
||||
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
||||
fan = True
|
||||
else:
|
||||
return None
|
||||
|
||||
total = len(vertIdx2)
|
||||
|
||||
if closed:
|
||||
# Bridge the start with the end
|
||||
if flipped:
|
||||
face = [
|
||||
vertIdx1[0],
|
||||
vertIdx2[0],
|
||||
vertIdx2[total - 1]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
else:
|
||||
face = [vertIdx2[0], vertIdx1[0]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
face.append(vertIdx2[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
# Bridge the rest of the faces
|
||||
for num in range(total - 1):
|
||||
if flipped:
|
||||
if fan:
|
||||
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx2[num], vertIdx1[num],
|
||||
vertIdx1[num + 1], vertIdx2[num + 1]]
|
||||
faces.append(face)
|
||||
else:
|
||||
if fan:
|
||||
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx1[num], vertIdx2[num],
|
||||
vertIdx2[num + 1], vertIdx1[num + 1]]
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
class AddZFunctionSurface(Operator):
|
||||
bl_idname = "mesh.primitive_z_function_surface"
|
||||
bl_label = "Add Z Function Surface"
|
||||
bl_description = "Add a surface defined defined by a function z=f(x,y)"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
equation: StringProperty(
|
||||
name="Z Equation",
|
||||
description="Equation for z=f(x,y)",
|
||||
default="1 - ( x**2 + y**2 )"
|
||||
)
|
||||
div_x: IntProperty(
|
||||
name="X Subdivisions",
|
||||
description="Number of vertices in x direction",
|
||||
default=16,
|
||||
min=3,
|
||||
max=256
|
||||
)
|
||||
div_y: IntProperty(
|
||||
name="Y Subdivisions",
|
||||
description="Number of vertices in y direction",
|
||||
default=16,
|
||||
min=3,
|
||||
max=256
|
||||
)
|
||||
size_x: FloatProperty(
|
||||
name="X Size",
|
||||
description="Size of the x axis",
|
||||
default=2.0,
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
unit="LENGTH"
|
||||
)
|
||||
size_y: FloatProperty(
|
||||
name="Y Size",
|
||||
description="Size of the y axis",
|
||||
default=2.0,
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
unit="LENGTH"
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'equation')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'div_x', text='Subdivisions X')
|
||||
col.prop(self, 'div_y', text='Y')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'size_x', text='Size X')
|
||||
col.prop(self, 'size_y', text='Y')
|
||||
|
||||
def execute(self, context):
|
||||
equation = self.equation
|
||||
div_x = self.div_x
|
||||
div_y = self.div_y
|
||||
size_x = self.size_x
|
||||
size_y = self.size_y
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
delta_x = size_x / (div_x - 1)
|
||||
delta_y = size_y / (div_y - 1)
|
||||
start_x = -(size_x / 2.0)
|
||||
start_y = -(size_y / 2.0)
|
||||
|
||||
edgeloop_prev = []
|
||||
|
||||
if equation:
|
||||
try:
|
||||
expr_args = (
|
||||
compile(equation, __file__, 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
except:
|
||||
import traceback
|
||||
# WARNING is used to prevent the constant pop-up spam
|
||||
self.report({'WARNING'},
|
||||
"Error parsing expression: {} "
|
||||
"(Check the console for more info)".format(equation))
|
||||
print("\n[Add Z Function Surface]:\n\n", traceback.format_exc(limit=1))
|
||||
|
||||
return {'CANCELLED'}
|
||||
|
||||
for row_x in range(div_x):
|
||||
edgeloop_cur = []
|
||||
x = start_x + row_x * delta_x
|
||||
|
||||
for row_y in range(div_y):
|
||||
y = start_y + row_y * delta_y
|
||||
z = 0.0
|
||||
|
||||
safe_dict['x'] = x
|
||||
safe_dict['y'] = y
|
||||
|
||||
# Try to evaluate the equation.
|
||||
try:
|
||||
z = float(eval(*expr_args))
|
||||
except:
|
||||
import traceback
|
||||
self.report({'WARNING'},
|
||||
"Error evaluating expression: {} "
|
||||
"(Check the console for more info)".format(equation))
|
||||
print("\n[Add Z Function Surface]:\n\n", traceback.format_exc(limit=1))
|
||||
|
||||
return {'CANCELLED'}
|
||||
|
||||
edgeloop_cur.append(len(verts))
|
||||
verts.append((x, y, z))
|
||||
|
||||
if len(edgeloop_prev) > 0:
|
||||
faces_row = createFaces(edgeloop_prev, edgeloop_cur)
|
||||
faces.extend(faces_row)
|
||||
|
||||
edgeloop_prev = edgeloop_cur
|
||||
|
||||
base = create_mesh_object(context, verts, [], faces, "Z Function")
|
||||
else:
|
||||
self.report({'WARNING'}, "Z Equation - No expression is given")
|
||||
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def xyz_function_surface_faces(self, x_eq, y_eq, z_eq,
|
||||
range_u_min, range_u_max, range_u_step, wrap_u,
|
||||
range_v_min, range_v_max, range_v_step, wrap_v,
|
||||
a_eq, b_eq, c_eq, f_eq, g_eq, h_eq, n, close_v):
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
# Distance of each step in Blender Units
|
||||
uStep = (range_u_max - range_u_min) / range_u_step
|
||||
vStep = (range_v_max - range_v_min) / range_v_step
|
||||
|
||||
# Number of steps in the vertex creation loops.
|
||||
# Number of steps is the number of faces
|
||||
# => Number of points is +1 unless wrapped.
|
||||
uRange = range_u_step + 1
|
||||
vRange = range_v_step + 1
|
||||
|
||||
if wrap_u:
|
||||
uRange = uRange - 1
|
||||
|
||||
if wrap_v:
|
||||
vRange = vRange - 1
|
||||
|
||||
try:
|
||||
expr_args_x = (
|
||||
compile(x_eq, __file__.replace(".py", "_x.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_y = (
|
||||
compile(y_eq, __file__.replace(".py", "_y.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_z = (
|
||||
compile(z_eq, __file__.replace(".py", "_z.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_a = (
|
||||
compile(a_eq, __file__.replace(".py", "_a.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_b = (
|
||||
compile(b_eq, __file__.replace(".py", "_b.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_c = (
|
||||
compile(c_eq, __file__.replace(".py", "_c.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_f = (
|
||||
compile(f_eq, __file__.replace(".py", "_f.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_g = (
|
||||
compile(g_eq, __file__.replace(".py", "_g.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
expr_args_h = (
|
||||
compile(h_eq, __file__.replace(".py", "_h.py"), 'eval'),
|
||||
{"__builtins__": None},
|
||||
safe_dict)
|
||||
except:
|
||||
import traceback
|
||||
self.report({'WARNING'}, "Error parsing expression(s) - "
|
||||
"Check the console for more info")
|
||||
print("\n[Add X, Y, Z Function Surface]:\n\n", traceback.format_exc(limit=1))
|
||||
return [], []
|
||||
|
||||
for vN in range(vRange):
|
||||
v = range_v_min + (vN * vStep)
|
||||
|
||||
for uN in range(uRange):
|
||||
u = range_u_min + (uN * uStep)
|
||||
|
||||
safe_dict['u'] = u
|
||||
safe_dict['v'] = v
|
||||
|
||||
safe_dict['n'] = n
|
||||
|
||||
# Try to evaluate the equations.
|
||||
try:
|
||||
safe_dict['a'] = float(eval(*expr_args_a))
|
||||
safe_dict['b'] = float(eval(*expr_args_b))
|
||||
safe_dict['c'] = float(eval(*expr_args_c))
|
||||
safe_dict['f'] = float(eval(*expr_args_f))
|
||||
safe_dict['g'] = float(eval(*expr_args_g))
|
||||
safe_dict['h'] = float(eval(*expr_args_h))
|
||||
|
||||
verts.append((
|
||||
float(eval(*expr_args_x)),
|
||||
float(eval(*expr_args_y)),
|
||||
float(eval(*expr_args_z))))
|
||||
except:
|
||||
import traceback
|
||||
self.report({'WARNING'}, "Error evaluating expression(s) - "
|
||||
"Check the console for more info")
|
||||
print("\n[Add X, Y, Z Function Surface]:\n\n", traceback.format_exc(limit=1))
|
||||
return [], []
|
||||
|
||||
for vN in range(range_v_step):
|
||||
vNext = vN + 1
|
||||
|
||||
if wrap_v and (vNext >= vRange):
|
||||
vNext = 0
|
||||
|
||||
for uN in range(range_u_step):
|
||||
uNext = uN + 1
|
||||
|
||||
if wrap_u and (uNext >= uRange):
|
||||
uNext = 0
|
||||
|
||||
faces.append([(vNext * uRange) + uNext,
|
||||
(vNext * uRange) + uN,
|
||||
(vN * uRange) + uN,
|
||||
(vN * uRange) + uNext])
|
||||
|
||||
if close_v and wrap_u and (not wrap_v):
|
||||
for uN in range(1, range_u_step - 1):
|
||||
faces.append([
|
||||
range_u_step - 1,
|
||||
range_u_step - 1 - uN,
|
||||
range_u_step - 2 - uN])
|
||||
faces.append([
|
||||
range_v_step * uRange,
|
||||
range_v_step * uRange + uN,
|
||||
range_v_step * uRange + uN + 1])
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
# Original Script "Parametric.py" by Ed Mackey.
|
||||
# -> http://www.blinken.com/blender-plugins.php
|
||||
# Partly converted for Blender 2.5 by tuga3d.
|
||||
#
|
||||
# Sphere:
|
||||
# x = sin(2*pi*u)*sin(pi*v)
|
||||
# y = cos(2*pi*u)*sin(pi*v)
|
||||
# z = cos(pi*v)
|
||||
# u_min = v_min = 0
|
||||
# u_max = v_max = 1
|
||||
#
|
||||
# "Snail shell"
|
||||
# x = 1.2**v*(sin(u)**2 *sin(v))
|
||||
# y = 1.2**v*(sin(u)*cos(u))
|
||||
# z = 1.2**v*(sin(u)**2 *cos(v))
|
||||
# u_min = 0
|
||||
# u_max = pi
|
||||
# v_min = -pi/4,
|
||||
# v max = 5*pi/2
|
||||
|
||||
class AddXYZFunctionSurface(Operator):
|
||||
bl_idname = "mesh.primitive_xyz_function_surface"
|
||||
bl_label = "Add XYZ Function Surface"
|
||||
bl_description = ("Add a surface defined defined by 3 functions:\n"
|
||||
"x=F1(u,v), y=F2(u,v) and z=F3(u,v)")
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
x_eq: StringProperty(
|
||||
name="X Equation",
|
||||
description="Equation for x=F(u,v). "
|
||||
"Also available: n, a, b, c, f, g, h",
|
||||
default="cos(v)*(1+cos(u))*sin(v/8)"
|
||||
)
|
||||
y_eq: StringProperty(
|
||||
name="Y Equation",
|
||||
description="Equation for y=F(u,v). "
|
||||
"Also available: n, a, b, c, f, g, h",
|
||||
default="sin(u)*sin(v/8)+cos(v/8)*1.5"
|
||||
)
|
||||
z_eq: StringProperty(
|
||||
name="Z Equation",
|
||||
description="Equation for z=F(u,v). "
|
||||
"Also available: n, a, b, c, f, g, h",
|
||||
default="sin(v)*(1+cos(u))*sin(v/8)"
|
||||
)
|
||||
range_u_min: FloatProperty(
|
||||
name="U Min",
|
||||
description="Minimum U value. Lower boundary of U range",
|
||||
min=-100.00,
|
||||
max=0.00,
|
||||
default=0.00
|
||||
)
|
||||
range_u_max: FloatProperty(
|
||||
name="U Max",
|
||||
description="Maximum U value. Upper boundary of U range",
|
||||
min=0.00,
|
||||
max=100.00,
|
||||
default=2 * pi
|
||||
)
|
||||
range_u_step: IntProperty(
|
||||
name="U Step",
|
||||
description="U Subdivisions",
|
||||
min=1,
|
||||
max=1024,
|
||||
default=32
|
||||
)
|
||||
wrap_u: BoolProperty(
|
||||
name="U Wrap",
|
||||
description="U Wrap around",
|
||||
default=True
|
||||
)
|
||||
range_v_min: FloatProperty(
|
||||
name="V Min",
|
||||
description="Minimum V value. Lower boundary of V range",
|
||||
min=-100.00,
|
||||
max=0.00,
|
||||
default=0.00
|
||||
)
|
||||
range_v_max: FloatProperty(
|
||||
name="V Max",
|
||||
description="Maximum V value. Upper boundary of V range",
|
||||
min=0.00,
|
||||
max=100.00,
|
||||
default=4 * pi
|
||||
)
|
||||
range_v_step: IntProperty(
|
||||
name="V Step",
|
||||
description="V Subdivisions",
|
||||
min=1,
|
||||
max=1024,
|
||||
default=128
|
||||
)
|
||||
wrap_v: BoolProperty(
|
||||
name="V Wrap",
|
||||
description="V Wrap around",
|
||||
default=False
|
||||
)
|
||||
close_v: BoolProperty(
|
||||
name="Close V",
|
||||
description="Create faces for first and last "
|
||||
"V values (only if U is wrapped)",
|
||||
default=False
|
||||
)
|
||||
n_eq: IntProperty(
|
||||
name="Number of Objects (n=0..N-1)",
|
||||
description="The parameter n will be the index "
|
||||
"of the current object, 0 to N-1",
|
||||
min=1,
|
||||
max=100,
|
||||
default=1
|
||||
)
|
||||
a_eq: StringProperty(
|
||||
name="A Helper Function",
|
||||
description="Equation for a=F(u,v). Also available: n",
|
||||
default="0"
|
||||
)
|
||||
b_eq: StringProperty(
|
||||
name="B Helper Function",
|
||||
description="Equation for b=F(u,v). Also available: n",
|
||||
default="0"
|
||||
)
|
||||
c_eq: StringProperty(
|
||||
name="C Helper Function",
|
||||
description="Equation for c=F(u,v). Also available: n",
|
||||
default="0"
|
||||
)
|
||||
f_eq: StringProperty(
|
||||
name="F Helper Function",
|
||||
description="Equation for f=F(u,v). Also available: n, a, b, c",
|
||||
default="0"
|
||||
)
|
||||
g_eq: StringProperty(
|
||||
name="G Helper Function",
|
||||
description="Equation for g=F(u,v). Also available: n, a, b, c",
|
||||
default="0"
|
||||
)
|
||||
h_eq: StringProperty(
|
||||
name="H Helper Function",
|
||||
description="Equation for h=F(u,v). Also available: n, a, b, c",
|
||||
default="0"
|
||||
)
|
||||
show_wire : BoolProperty(
|
||||
name="Show Wireframe",
|
||||
default=True,
|
||||
description="Add the object’s wireframe over solid drawing"
|
||||
)
|
||||
edit_mode : BoolProperty(
|
||||
name="Show in Edit Mode",
|
||||
default=True,
|
||||
description="Show in Edit Mode"
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
col = layout.column()
|
||||
col.prop(self, 'x_eq', text='Equation X')
|
||||
col.prop(self, 'y_eq', text='Y')
|
||||
col.prop(self, 'z_eq', text='Z')
|
||||
layout.separator()
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'range_u_min', text='U Min')
|
||||
col.prop(self, 'range_u_max', text='Max')
|
||||
col.prop(self, 'range_u_step', text='Step')
|
||||
col.prop(self, 'wrap_u', text='Wrap')
|
||||
layout.separator()
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'range_v_min', text='V Min')
|
||||
col.prop(self, 'range_v_max', text='Max')
|
||||
col.prop(self, 'range_v_step', text='Step')
|
||||
col.prop(self, 'wrap_v', text='Wrap')
|
||||
col.prop(self, 'close_v', text='Close')
|
||||
layout.separator()
|
||||
col = layout.column()
|
||||
col.prop(self, 'n_eq', text='Objects')
|
||||
col.prop(self, 'a_eq', text='Helper Function A')
|
||||
col.prop(self, 'b_eq', text='B')
|
||||
col.prop(self, 'c_eq', text='C')
|
||||
col.prop(self, 'f_eq', text='F')
|
||||
col.prop(self, 'g_eq', text='G')
|
||||
col.prop(self, 'h_eq', text='H')
|
||||
layout.separator()
|
||||
row = layout.row(heading='Show')
|
||||
row.prop(self, 'show_wire', text='Wireframe')
|
||||
layout.prop(self, 'edit_mode', text='In Edit Mode')
|
||||
|
||||
def execute(self, context):
|
||||
for n in range(0, self.n_eq):
|
||||
verts, faces = xyz_function_surface_faces(
|
||||
self,
|
||||
self.x_eq,
|
||||
self.y_eq,
|
||||
self.z_eq,
|
||||
self.range_u_min,
|
||||
self.range_u_max,
|
||||
self.range_u_step,
|
||||
self.wrap_u,
|
||||
self.range_v_min,
|
||||
self.range_v_max,
|
||||
self.range_v_step,
|
||||
self.wrap_v,
|
||||
self.a_eq,
|
||||
self.b_eq,
|
||||
self.c_eq,
|
||||
self.f_eq,
|
||||
self.g_eq,
|
||||
self.h_eq,
|
||||
n,
|
||||
self.close_v
|
||||
)
|
||||
if not verts:
|
||||
return {'CANCELLED'}
|
||||
|
||||
obj = create_mesh_object(context, verts, [], faces, "XYZ Function")
|
||||
|
||||
if self.show_wire:
|
||||
context.active_object.show_wire = True
|
||||
else:
|
||||
context.active_object.show_wire = False
|
||||
|
||||
if self.edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
else:
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
|
||||
return {'FINISHED'}
|
||||
@@ -0,0 +1,822 @@
|
||||
# SPDX-FileCopyrightText: 2016-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: revolt_randy, Jambay
|
||||
|
||||
# Create "Beam" primitives. Based on original script by revolt_randy
|
||||
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
# #####################
|
||||
# Create vertices for end of mesh
|
||||
#
|
||||
# y_off - verts y-axis origin
|
||||
#
|
||||
# returns:
|
||||
# endVs - x,y,z list
|
||||
|
||||
def beamEndVs(sRef, y_off):
|
||||
thick = sRef.beamW * 2
|
||||
|
||||
if sRef.Type == '2': # swap width and height for C shape
|
||||
bEndX2 = sRef.beamZ / 2
|
||||
bEndXInr = ((sRef.beamZ - thick) / 2)
|
||||
bEndZ2 = sRef.beamX / 2
|
||||
bEndZInr = ((sRef.beamX - thick) / 2)
|
||||
else:
|
||||
bEndX2 = sRef.beamX / 2
|
||||
bEndXInr = ((sRef.beamX - thick) / 2)
|
||||
bEndZ2 = sRef.beamZ / 2
|
||||
bEndZInr = ((sRef.beamZ - thick) / 2)
|
||||
|
||||
endVs = []
|
||||
|
||||
# outer ...
|
||||
endVs.append((bEndX2, y_off, bEndZ2))
|
||||
endVs.append((-bEndX2, y_off, bEndZ2))
|
||||
endVs.append((-bEndX2, y_off, -bEndZ2))
|
||||
endVs.append((bEndX2, y_off, -bEndZ2))
|
||||
# innner ...
|
||||
endVs.append((bEndXInr, y_off, bEndZInr))
|
||||
endVs.append((-bEndXInr, y_off, bEndZInr))
|
||||
endVs.append((-bEndXInr, y_off, -bEndZInr))
|
||||
endVs.append((bEndXInr, y_off, -bEndZInr))
|
||||
|
||||
return endVs
|
||||
|
||||
|
||||
# #####################
|
||||
# Create End Faces
|
||||
#
|
||||
# verts_list - list of vertices
|
||||
#
|
||||
# returns:
|
||||
# beamFs, a list of tuples defining the end faces.
|
||||
|
||||
def beamEndFaces(verts_list):
|
||||
|
||||
beamFs = []
|
||||
|
||||
num_of_verts = int(len(verts_list) / 2)
|
||||
|
||||
# Create list of faces
|
||||
for index in range(num_of_verts):
|
||||
faces_temp = []
|
||||
|
||||
if index == (num_of_verts - 1):
|
||||
faces_temp.append(verts_list[index])
|
||||
faces_temp.append(verts_list[index - index])
|
||||
faces_temp.append(verts_list[index + 1])
|
||||
faces_temp.append(verts_list[index * 2 + 1])
|
||||
else:
|
||||
faces_temp.append(verts_list[index])
|
||||
faces_temp.append(verts_list[index + 1])
|
||||
faces_temp.append(verts_list[index + num_of_verts + 1])
|
||||
faces_temp.append(verts_list[index + num_of_verts])
|
||||
|
||||
beamFs.append(tuple(faces_temp))
|
||||
|
||||
return beamFs
|
||||
|
||||
|
||||
# #####################
|
||||
# Bridge vertices to create side faces.
|
||||
#
|
||||
# front_verts - front face vertices
|
||||
# back_verts - back face vertices
|
||||
# front & back must be ordered in same direction
|
||||
# with respect to y-axis
|
||||
#
|
||||
# returns:
|
||||
# sideFaces, a list of the bridged faces
|
||||
|
||||
def beamSides(front_verts, back_verts):
|
||||
sideFaces = []
|
||||
|
||||
num_of_faces = (len(front_verts))
|
||||
|
||||
# add first value to end of lists for looping
|
||||
front_verts.append(front_verts[0])
|
||||
back_verts.append(back_verts[0])
|
||||
|
||||
# Build the faces
|
||||
for index in range(num_of_faces):
|
||||
facestemp = (front_verts[index], front_verts[index + 1], back_verts[index + 1], back_verts[index])
|
||||
sideFaces.append(facestemp)
|
||||
|
||||
return sideFaces
|
||||
|
||||
|
||||
# #####################
|
||||
# Creates a box beam
|
||||
#
|
||||
# returns:
|
||||
# beamVs - x, y, z, location of each vertice
|
||||
# beamFs - vertices that make up each face
|
||||
|
||||
def create_beam(sRef):
|
||||
|
||||
frontVs = []
|
||||
frontFs = []
|
||||
backVs = []
|
||||
|
||||
y_off = sRef.beamY / 2 # offset from center for vertices
|
||||
|
||||
frontVs = beamEndVs(sRef, y_off)
|
||||
backVs = beamEndVs(sRef, -y_off)
|
||||
|
||||
# Combine vertices
|
||||
beamVs = frontVs + backVs
|
||||
|
||||
# Create front face
|
||||
numofverts = len(frontVs)
|
||||
verts_front_list = []
|
||||
for index in range(numofverts):
|
||||
verts_front_list.append(index)
|
||||
|
||||
frontFs = beamEndFaces(verts_front_list)
|
||||
|
||||
# Create back face
|
||||
faces_back_temp = []
|
||||
verts_back_list = []
|
||||
|
||||
numofverts = len(backVs)
|
||||
for index in range(numofverts):
|
||||
verts_back_list.append(index + numofverts)
|
||||
|
||||
faces_back_temp = beamEndFaces(verts_back_list)
|
||||
|
||||
# Create side faces
|
||||
faces_side_temp = []
|
||||
|
||||
# Object has thickness, create list of outside vertices
|
||||
numofverts = len(verts_front_list)
|
||||
halfVerts = int(numofverts / 2)
|
||||
frontVs = verts_front_list[0:halfVerts]
|
||||
backVs = verts_back_list[0:halfVerts]
|
||||
|
||||
faces_side_temp = beamSides(frontVs, backVs)
|
||||
|
||||
# Create list of inside vertices
|
||||
frontVs = verts_front_list[halfVerts:numofverts]
|
||||
backVs = verts_back_list[halfVerts:numofverts]
|
||||
|
||||
faces_side_temp += beamSides(frontVs, backVs)
|
||||
|
||||
# Combine all faces
|
||||
beamFs = frontFs + faces_back_temp + faces_side_temp
|
||||
|
||||
return beamVs, beamFs
|
||||
|
||||
|
||||
# #####################
|
||||
# Taper/angle faces of beam.
|
||||
# inner vert toward outer vert
|
||||
# based on percentage of taper.
|
||||
#
|
||||
# returns:
|
||||
# adVert - the calculated vertex
|
||||
|
||||
def beamSlant(sRef, outV, inV):
|
||||
bTaper = 100 - sRef.edgeA
|
||||
|
||||
# calculate variance & adjust vertex
|
||||
deltaV = ((inV - outV) / 100)
|
||||
adVert = outV + (deltaV * bTaper)
|
||||
|
||||
return adVert
|
||||
|
||||
|
||||
# #####################
|
||||
# Modify location to shape beam.
|
||||
#
|
||||
# verts - tuples for one end of beam
|
||||
#
|
||||
# returns:
|
||||
# verts - modified tuples for beam shape.
|
||||
|
||||
def beamSquareEnds(sRef, verts):
|
||||
|
||||
# match 5th & 6th z locations to 1st & 2nd
|
||||
|
||||
vert_orig = verts[0]
|
||||
vert_temp = verts[4]
|
||||
vert_x = beamSlant(sRef, vert_orig[0], vert_temp[0])
|
||||
verts[4] = (vert_x, vert_temp[1], vert_orig[2])
|
||||
|
||||
vert_orig = verts[1]
|
||||
vert_temp = verts[5]
|
||||
vert_x = beamSlant(sRef, vert_orig[0], vert_temp[0])
|
||||
verts[5] = (vert_x, vert_temp[1], vert_orig[2])
|
||||
|
||||
return verts
|
||||
|
||||
|
||||
# #####################
|
||||
#
|
||||
# Create U shaped beam
|
||||
# Shared with C shape - see beamEndVs
|
||||
# for sizing and rotate in addBeamObj.
|
||||
#
|
||||
# returns:
|
||||
# beamVs - vertice x, y, z, locations
|
||||
# beamFs - face vertices
|
||||
|
||||
def create_u_beam(sRef):
|
||||
|
||||
# offset vertices from center
|
||||
y_off = sRef.beamY / 2
|
||||
|
||||
frontVtemp = []
|
||||
frontFtemp = []
|
||||
frontVlist = []
|
||||
|
||||
backVtemp = []
|
||||
backFtemp = []
|
||||
backVlist = []
|
||||
|
||||
sideFs = []
|
||||
|
||||
frontVtemp = beamEndVs(sRef, y_off) # Box beam
|
||||
frontVtemp = beamSquareEnds(sRef, frontVtemp) # U shape
|
||||
|
||||
backVtemp = beamEndVs(sRef, -y_off)
|
||||
backVtemp = beamSquareEnds(sRef, backVtemp)
|
||||
|
||||
beamVs = frontVtemp + backVtemp
|
||||
|
||||
# Create front face
|
||||
for index in range(len(frontVtemp)): # Build vert list
|
||||
frontVlist.append(index)
|
||||
|
||||
frontFtemp = beamEndFaces(frontVlist)
|
||||
frontFtemp = frontFtemp[1:4] # Remove 1st face
|
||||
|
||||
# Create back face
|
||||
numofverts = len(backVtemp)
|
||||
for index in range(numofverts): # Build vertex list
|
||||
backVlist.append(index + numofverts)
|
||||
|
||||
backFtemp = beamEndFaces(backVlist)
|
||||
backFtemp = backFtemp[1:4] # Remove face
|
||||
|
||||
# Create list vertices for outside faces
|
||||
numofverts = int(len(frontVlist))
|
||||
halfVerts = int(numofverts / 2)
|
||||
frontVtemp = frontVlist[0:halfVerts]
|
||||
backVtemp = backVlist[0:halfVerts]
|
||||
|
||||
sideFs = beamSides(frontVtemp, backVtemp)
|
||||
sideFs = sideFs[1:] # Remove face
|
||||
|
||||
# Create inside verts
|
||||
frontVtemp = frontVlist[halfVerts:numofverts]
|
||||
backVtemp = backVlist[halfVerts:numofverts]
|
||||
|
||||
sideFs += beamSides(frontVtemp, backVtemp)
|
||||
sideFs = sideFs[0:3] + sideFs[4:] # Remove face
|
||||
|
||||
# fill in faces
|
||||
sideFs.append((0, 4, 12, 8))
|
||||
sideFs.append((5, 1, 9, 13))
|
||||
|
||||
beamFs = frontFtemp + backFtemp + sideFs # Combine faces
|
||||
|
||||
return beamVs, beamFs
|
||||
|
||||
|
||||
# #####################
|
||||
# returns:
|
||||
# verts_final - x, y, z, location of each vertice
|
||||
# faces_final - vertices that make up each face
|
||||
|
||||
def create_L_beam(sRef):
|
||||
|
||||
thick = sRef.beamW
|
||||
|
||||
# offset vertices from center
|
||||
x_off = sRef.beamX / 2
|
||||
y_off = sRef.beamY / 2
|
||||
z_off = sRef.beamZ / 2
|
||||
|
||||
# Create temporarylists to hold vertices locations
|
||||
verts_front_temp = []
|
||||
verts_back_temp = []
|
||||
|
||||
# Create front vertices by calculation
|
||||
verts_front_temp = [
|
||||
(-x_off, -y_off, z_off),
|
||||
(-(x_off - thick), -y_off, z_off),
|
||||
(-(x_off - thick), -y_off, -(z_off - thick)),
|
||||
(x_off, -y_off, -(z_off - thick)),
|
||||
(x_off, -y_off, -z_off),
|
||||
(-x_off, -y_off, -z_off)
|
||||
]
|
||||
|
||||
# Adjust taper
|
||||
vert_outside = verts_front_temp[0]
|
||||
vert_inside = verts_front_temp[1]
|
||||
vert_taper = beamSlant(sRef, vert_outside[0], vert_inside[0])
|
||||
verts_front_temp[1] = [vert_taper, vert_inside[1], vert_inside[2]]
|
||||
|
||||
vert_outside = verts_front_temp[4]
|
||||
vert_inside = verts_front_temp[3]
|
||||
vert_taper = beamSlant(sRef, vert_outside[2], vert_inside[2])
|
||||
verts_front_temp[3] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
# Create back vertices by calculation
|
||||
verts_back_temp = [
|
||||
(-x_off, y_off, z_off),
|
||||
(-(x_off - thick), y_off, z_off),
|
||||
(-(x_off - thick), y_off, -(z_off - thick)),
|
||||
(x_off, y_off, -(z_off - thick)),
|
||||
(x_off, y_off, -z_off),
|
||||
(-x_off, y_off, -z_off)
|
||||
]
|
||||
|
||||
# Adjust taper
|
||||
vert_outside = verts_back_temp[0]
|
||||
vert_inside = verts_back_temp[1]
|
||||
vert_taper = beamSlant(sRef, vert_outside[0], vert_inside[0])
|
||||
verts_back_temp[1] = [vert_taper, vert_inside[1], vert_inside[2]]
|
||||
|
||||
vert_outside = verts_back_temp[4]
|
||||
vert_inside = verts_back_temp[3]
|
||||
vert_taper = beamSlant(sRef, vert_outside[2], vert_inside[2])
|
||||
verts_back_temp[3] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
verts_final = verts_front_temp + verts_back_temp
|
||||
|
||||
# define end faces, only 4 so just coded
|
||||
faces_front_temp = []
|
||||
faces_back_temp = []
|
||||
faces_side_temp = []
|
||||
|
||||
faces_front_temp = [(0, 1, 2, 5), (2, 3, 4, 5)]
|
||||
faces_back_temp = [(6, 7, 8, 11), (8, 9, 10, 11)]
|
||||
|
||||
verts_front_list = []
|
||||
verts_back_list = []
|
||||
num_of_verts = len(verts_front_temp)
|
||||
|
||||
# build lists of back and front verts for beamSides function
|
||||
for index in range(num_of_verts):
|
||||
verts_front_list.append(index)
|
||||
for index in range(num_of_verts):
|
||||
verts_back_list.append(index + 6)
|
||||
|
||||
faces_side_temp = beamSides(verts_front_list, verts_back_list)
|
||||
|
||||
faces_final = faces_front_temp + faces_back_temp + faces_side_temp
|
||||
|
||||
return verts_final, faces_final
|
||||
|
||||
|
||||
# #####################
|
||||
# returns:
|
||||
# verts_final - a list of tuples of the x, y, z, location of each vertice
|
||||
# faces_final - a list of tuples of the vertices that make up each face
|
||||
|
||||
def create_T_beam(sRef):
|
||||
|
||||
thick = sRef.beamW
|
||||
|
||||
# Get offset of vertices from center
|
||||
x_off = sRef.beamX / 2
|
||||
y_off = sRef.beamY / 2
|
||||
z_off = sRef.beamZ / 2
|
||||
thick_off = thick / 2
|
||||
|
||||
# Create temporarylists to hold vertices locations
|
||||
verts_front_temp = []
|
||||
verts_back_temp = []
|
||||
|
||||
# Create front vertices
|
||||
verts_front_temp = [
|
||||
(-x_off, -y_off, z_off),
|
||||
(-thick_off, -y_off, z_off),
|
||||
(thick_off, -y_off, z_off),
|
||||
(x_off, -y_off, z_off),
|
||||
(x_off, -y_off, z_off - thick),
|
||||
(thick_off, -y_off, z_off - thick),
|
||||
(thick_off, -y_off, -z_off),
|
||||
(-thick_off, -y_off, -z_off),
|
||||
(-thick_off, -y_off, z_off - thick),
|
||||
(-x_off, -y_off, z_off - thick)
|
||||
]
|
||||
|
||||
# Adjust taper
|
||||
vert_outside = verts_front_temp[0]
|
||||
vert_inside = verts_front_temp[9]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_front_temp[9] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_front_temp[3]
|
||||
vert_inside = verts_front_temp[4]
|
||||
verts_front_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
# Adjust taper of bottom of beam, so 0 the center
|
||||
# now becomes vert_outside, and vert_inside is calculated
|
||||
# 1/2 way towards center
|
||||
vert_outside = (0, -y_off, -z_off)
|
||||
vert_inside = verts_front_temp[6]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[0], vert_inside[0]))
|
||||
verts_front_temp[6] = [vert_taper, vert_inside[1], vert_inside[2]]
|
||||
|
||||
vert_outside = (0, -y_off, -z_off)
|
||||
vert_inside = verts_front_temp[7]
|
||||
vert_taper = beamSlant(sRef, vert_outside[0], vert_inside[0])
|
||||
verts_front_temp[7] = [vert_taper, vert_inside[1], vert_inside[2]]
|
||||
|
||||
# Create fack vertices by calculation
|
||||
verts_back_temp = [
|
||||
(-x_off, y_off, z_off),
|
||||
(-thick_off, y_off, z_off),
|
||||
(thick_off, y_off, z_off),
|
||||
(x_off, y_off, z_off),
|
||||
(x_off, y_off, z_off - thick),
|
||||
(thick_off, y_off, z_off - thick),
|
||||
(thick_off, y_off, -z_off),
|
||||
(-thick_off, y_off, -z_off),
|
||||
(-thick_off, y_off, z_off - thick),
|
||||
(-x_off, y_off, z_off - thick)
|
||||
]
|
||||
|
||||
# Adjust taper
|
||||
vert_outside = verts_back_temp[0]
|
||||
vert_inside = verts_back_temp[9]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_back_temp[9] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_back_temp[3]
|
||||
vert_inside = verts_back_temp[4]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_back_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
# Adjust taper of bottom of beam, so 0 the center
|
||||
# now becomes vert_outside, and vert_inside is calculated
|
||||
# 1/2 way towards center
|
||||
vert_outside = (0, -y_off, -z_off)
|
||||
vert_inside = verts_back_temp[6]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[0], vert_inside[0]))
|
||||
verts_back_temp[6] = [vert_taper, vert_inside[1], vert_inside[2]]
|
||||
|
||||
vert_outside = (0, -y_off, -z_off)
|
||||
vert_inside = verts_back_temp[7]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[0], vert_inside[0]))
|
||||
verts_back_temp[7] = [vert_taper, vert_inside[1], vert_inside[2]]
|
||||
|
||||
verts_final = verts_front_temp + verts_back_temp
|
||||
|
||||
# define end faces, only 8 so just coded
|
||||
faces_front_temp = []
|
||||
faces_back_temp = []
|
||||
faces_side_temp = []
|
||||
|
||||
faces_front_temp = [(0, 1, 8, 9), (1, 2, 5, 8),
|
||||
(2, 3, 4, 5), (5, 6, 7, 8)]
|
||||
|
||||
faces_back_temp = [(10, 11, 18, 19), (11, 12, 15, 18),
|
||||
(12, 13, 14, 15), (15, 16, 17, 18)]
|
||||
|
||||
verts_front_list = []
|
||||
verts_back_list = []
|
||||
num_of_verts = len(verts_front_temp)
|
||||
|
||||
# build lists of back and front verts for beamSides function
|
||||
for index in range(num_of_verts):
|
||||
verts_front_list.append(index)
|
||||
for index in range(num_of_verts):
|
||||
verts_back_list.append(index + 10)
|
||||
|
||||
faces_side_temp = beamSides(verts_front_list, verts_back_list)
|
||||
|
||||
faces_final = faces_front_temp + faces_back_temp + faces_side_temp
|
||||
|
||||
return verts_final, faces_final
|
||||
|
||||
|
||||
# #####################
|
||||
# returns:
|
||||
# verts_final - a list of tuples of the x, y, z, location of each vertice
|
||||
# faces_final - a list of tuples of the vertices that make up each face
|
||||
|
||||
def create_I_beam(sRef):
|
||||
|
||||
thick = sRef.beamW
|
||||
|
||||
# Get offset of vertices from center
|
||||
x_off = sRef.beamX / 2
|
||||
y_off = sRef.beamY / 2
|
||||
z_off = sRef.beamZ / 2
|
||||
thick_off = thick / 2
|
||||
|
||||
# Create temporarylists to hold vertices locations
|
||||
verts_front_temp = []
|
||||
verts_back_temp = []
|
||||
|
||||
# Create front vertices by calculation
|
||||
verts_front_temp = [
|
||||
(-x_off, -y_off, z_off),
|
||||
(-thick_off, -y_off, z_off),
|
||||
(thick_off, -y_off, z_off),
|
||||
(x_off, -y_off, z_off),
|
||||
(x_off, -y_off, z_off - thick),
|
||||
(thick_off, -y_off, z_off - thick),
|
||||
(thick_off, -y_off, -z_off + thick),
|
||||
(x_off, -y_off, -z_off + thick),
|
||||
(x_off, -y_off, -z_off),
|
||||
(thick_off, -y_off, -z_off),
|
||||
(-thick_off, -y_off, -z_off),
|
||||
(-x_off, -y_off, -z_off),
|
||||
(-x_off, -y_off, -z_off + thick),
|
||||
(-thick_off, -y_off, -z_off + thick),
|
||||
(-thick_off, -y_off, z_off - thick),
|
||||
(-x_off, -y_off, z_off - thick)
|
||||
]
|
||||
|
||||
# Adjust taper
|
||||
vert_outside = verts_front_temp[0]
|
||||
vert_inside = verts_front_temp[15]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_front_temp[15] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_front_temp[3]
|
||||
vert_inside = verts_front_temp[4]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_front_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_front_temp[8]
|
||||
vert_inside = verts_front_temp[7]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_front_temp[7] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_front_temp[11]
|
||||
vert_inside = verts_front_temp[12]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_front_temp[12] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
# Create back vertices by calculation
|
||||
verts_back_temp = [
|
||||
(-x_off, y_off, z_off),
|
||||
(-thick_off, y_off, z_off),
|
||||
(thick_off, y_off, z_off),
|
||||
(x_off, y_off, z_off),
|
||||
(x_off, y_off, z_off - thick),
|
||||
(thick_off, y_off, z_off - thick),
|
||||
(thick_off, y_off, -z_off + thick),
|
||||
(x_off, y_off, -z_off + thick),
|
||||
(x_off, y_off, -z_off),
|
||||
(thick_off, y_off, -z_off),
|
||||
(-thick_off, y_off, -z_off),
|
||||
(-x_off, y_off, -z_off),
|
||||
(-x_off, y_off, -z_off + thick),
|
||||
(-thick_off, y_off, -z_off + thick),
|
||||
(-thick_off, y_off, z_off - thick),
|
||||
(-x_off, y_off, z_off - thick)
|
||||
]
|
||||
|
||||
# Adjust taper
|
||||
vert_outside = verts_back_temp[0]
|
||||
vert_inside = verts_back_temp[15]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_back_temp[15] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_back_temp[3]
|
||||
vert_inside = verts_back_temp[4]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_back_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_back_temp[8]
|
||||
vert_inside = verts_back_temp[7]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_back_temp[7] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
vert_outside = verts_back_temp[11]
|
||||
vert_inside = verts_back_temp[12]
|
||||
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
|
||||
verts_back_temp[12] = [vert_inside[0], vert_inside[1], vert_taper]
|
||||
|
||||
verts_final = verts_front_temp + verts_back_temp
|
||||
|
||||
# define end faces, only 7 per end, so just coded
|
||||
faces_front_temp = []
|
||||
faces_back_temp = []
|
||||
faces_side_temp = []
|
||||
|
||||
faces_front_temp = [(0, 1, 14, 15), (1, 2, 5, 14),
|
||||
(2, 3, 4, 5), (6, 7, 8, 9),
|
||||
(6, 9, 10, 13), (12, 13, 10, 11),
|
||||
(5, 6, 13, 14)]
|
||||
|
||||
faces_back_temp = [(16, 17, 30, 31), (17, 18, 21, 30),
|
||||
(18, 19, 20, 21), (22, 23, 24, 25),
|
||||
(22, 25, 26, 29), (28, 29, 26, 27),
|
||||
(21, 22, 29, 30)]
|
||||
|
||||
verts_front_list = []
|
||||
verts_back_list = []
|
||||
num_of_verts = len(verts_front_temp)
|
||||
|
||||
# build lists of back and front verts for beamSides function
|
||||
for index in range(num_of_verts):
|
||||
verts_front_list.append(index)
|
||||
for index in range(num_of_verts):
|
||||
verts_back_list.append(index + 16)
|
||||
|
||||
faces_side_temp = beamSides(verts_front_list, verts_back_list)
|
||||
|
||||
faces_final = faces_front_temp + faces_back_temp + faces_side_temp
|
||||
|
||||
return verts_final, faces_final
|
||||
|
||||
|
||||
# ######################
|
||||
#
|
||||
# Generate beam mesh.
|
||||
|
||||
def addBeamMesh(sRef, context):
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
# type of beam to add
|
||||
if sRef.Type == '0':
|
||||
verts, faces = create_beam(sRef)
|
||||
elif sRef.Type == '1':
|
||||
verts, faces = create_u_beam(sRef)
|
||||
elif sRef.Type == '2':
|
||||
verts, faces = create_u_beam(sRef)
|
||||
elif sRef.Type == '3':
|
||||
verts, faces = create_L_beam(sRef)
|
||||
elif sRef.Type == '4':
|
||||
verts, faces = create_I_beam(sRef)
|
||||
elif sRef.Type == '5':
|
||||
verts, faces = create_T_beam(sRef)
|
||||
else: # unknown type, use default.
|
||||
verts, faces = create_beam(sRef)
|
||||
|
||||
beamMesh = bpy.data.meshes.new("Beam")
|
||||
|
||||
beamMesh.from_pydata(verts, [], faces)
|
||||
beamMesh.update(calc_edges=True)
|
||||
|
||||
return beamMesh
|
||||
|
||||
|
||||
# ######################
|
||||
# Create a beam primitive.
|
||||
#
|
||||
# UI functions and object creation.
|
||||
|
||||
class addBeam(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.add_beam"
|
||||
bl_label = "Beam Builder"
|
||||
bl_description = "Create beam meshes of various profiles"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Beam : BoolProperty(name = "Beam",
|
||||
default = True,
|
||||
description = "Beam")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Beam")
|
||||
|
||||
Type: EnumProperty(
|
||||
name="Beam Type",
|
||||
items=(
|
||||
('0', "Box Profile", "Square Beam"),
|
||||
("1", "U Profile", "U Profile Beam"),
|
||||
("2", "C Profile", "C Profile Beam"),
|
||||
("3", "L Profile", "L Profile Beam"),
|
||||
("4", "I Profile", "I Profile Beam"),
|
||||
("5", "T Profile", "T Profile Beam")
|
||||
),
|
||||
description="Beam form"
|
||||
)
|
||||
beamZ: FloatProperty(
|
||||
name="Height",
|
||||
min=0.01,
|
||||
#max=100,
|
||||
default=1
|
||||
)
|
||||
beamX: FloatProperty(
|
||||
name="Width",
|
||||
min=0.01,
|
||||
#max=100,
|
||||
default=.5
|
||||
)
|
||||
beamY: FloatProperty(
|
||||
name="Depth",
|
||||
min=0.01,
|
||||
#max=100,
|
||||
default=2
|
||||
)
|
||||
beamW: FloatProperty(
|
||||
name="Thickness",
|
||||
min=0.01,
|
||||
#max=1,
|
||||
default=0.1
|
||||
)
|
||||
edgeA: IntProperty(
|
||||
name="Taper",
|
||||
min=0,
|
||||
#max=100,
|
||||
default=0,
|
||||
description="Angle beam edges"
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, "Type")
|
||||
layout.separator()
|
||||
layout.prop(self, "beamZ")
|
||||
layout.prop(self, "beamX")
|
||||
layout.prop(self, "beamY")
|
||||
layout.prop(self, "beamW")
|
||||
if self.Type != '0':
|
||||
layout.prop(self, "edgeA")
|
||||
|
||||
if self.change == False:
|
||||
# generic transform props
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Beam' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
mesh = addBeamMesh(self, context)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
mesh = addBeamMesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
if self.Type == '2': # Rotate C shape
|
||||
bpy.ops.transform.rotate(value=1.570796, constraint_axis=[False, True, False])
|
||||
bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
|
||||
|
||||
obj.data["Beam"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in BeamParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
mesh = addBeamMesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def BeamParameters():
|
||||
BeamParameters = [
|
||||
"Type",
|
||||
"beamZ",
|
||||
"beamX",
|
||||
"beamY",
|
||||
"beamW",
|
||||
"edgeA",
|
||||
]
|
||||
return BeamParameters
|
||||
@@ -0,0 +1,987 @@
|
||||
# SPDX-FileCopyrightText: 2009-2010 Michel J. Anders (varkenvarken)
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from math import (
|
||||
atan, asin, cos,
|
||||
sin, tan, pi,
|
||||
radians,
|
||||
)
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
StringProperty,
|
||||
FloatVectorProperty
|
||||
)
|
||||
from mathutils import (
|
||||
Vector,
|
||||
Matrix,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
# A very simple "bridge" tool.
|
||||
# Connects two equally long vertex rows with faces.
|
||||
# Returns a list of the new faces (list of lists)
|
||||
#
|
||||
# vertIdx1 ... First vertex list (list of vertex indices)
|
||||
# vertIdx2 ... Second vertex list (list of vertex indices)
|
||||
# closed ... Creates a loop (first & last are closed)
|
||||
# flipped ... Invert the normal of the face(s)
|
||||
#
|
||||
# Note: You can set vertIdx1 to a single vertex index to create
|
||||
# a fan/star of faces
|
||||
# Note: If both vertex idx list are the same length they have
|
||||
# to have at least 2 vertices
|
||||
|
||||
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
||||
faces = []
|
||||
|
||||
if not vertIdx1 or not vertIdx2:
|
||||
return None
|
||||
|
||||
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
||||
return None
|
||||
|
||||
fan = False
|
||||
if (len(vertIdx1) != len(vertIdx2)):
|
||||
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
||||
fan = True
|
||||
else:
|
||||
return None
|
||||
|
||||
total = len(vertIdx2)
|
||||
|
||||
if closed:
|
||||
# Bridge the start with the end.
|
||||
if flipped:
|
||||
face = [
|
||||
vertIdx1[0],
|
||||
vertIdx2[0],
|
||||
vertIdx2[total - 1]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
else:
|
||||
face = [vertIdx2[0], vertIdx1[0]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
face.append(vertIdx2[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
# Bridge the rest of the faces.
|
||||
for num in range(total - 1):
|
||||
if flipped:
|
||||
if fan:
|
||||
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx2[num], vertIdx1[num],
|
||||
vertIdx1[num + 1], vertIdx2[num + 1]]
|
||||
faces.append(face)
|
||||
else:
|
||||
if fan:
|
||||
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx1[num], vertIdx2[num],
|
||||
vertIdx2[num + 1], vertIdx1[num + 1]]
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
# Calculate the vertex coordinates for a single
|
||||
# section of a gear tooth.
|
||||
# Returns 4 lists of vertex coords (list of tuples):
|
||||
# *-*---*---* (1.) verts_inner_base
|
||||
# | | | |
|
||||
# *-*---*---* (2.) verts_outer_base
|
||||
# | | |
|
||||
# *---*---* (3.) verts_middle_tooth
|
||||
# \ | /
|
||||
# *-*-* (4.) verts_tip_tooth
|
||||
#
|
||||
# a
|
||||
# t
|
||||
# d
|
||||
# radius
|
||||
# Ad
|
||||
# De
|
||||
# base
|
||||
# p_angle
|
||||
# rack
|
||||
# crown
|
||||
|
||||
def add_tooth(a, t, d, radius, Ad, De, base, p_angle, rack=0, crown=0.0):
|
||||
A = [a, a + t / 4, a + t / 2, a + 3 * t / 4]
|
||||
C = [cos(i) for i in A]
|
||||
S = [sin(i) for i in A]
|
||||
|
||||
Ra = radius + Ad
|
||||
Rd = radius - De
|
||||
Rb = Rd - base
|
||||
|
||||
# Pressure angle calc
|
||||
O = Ad * tan(p_angle)
|
||||
if Ra != 0:
|
||||
p_angle = atan(O / Ra)
|
||||
else:
|
||||
p_angle = atan(O)
|
||||
|
||||
if radius < 0:
|
||||
p_angle = -p_angle
|
||||
|
||||
if rack:
|
||||
S = [sin(t / 4) * I for I in range(-2, 3)]
|
||||
Sp = [0, sin(-t / 4 + p_angle), 0, sin(t / 4 - p_angle)]
|
||||
|
||||
verts_inner_base = [(Rb, radius * S[I], d) for I in range(4)]
|
||||
verts_outer_base = [(Rd, radius * S[I], d) for I in range(4)]
|
||||
verts_middle_tooth = [(radius, radius * S[I], d) for I in range(1, 4)]
|
||||
verts_tip_tooth = [(Ra, radius * Sp[I], d) for I in range(1, 4)]
|
||||
|
||||
else:
|
||||
Cp = [
|
||||
0,
|
||||
cos(a + t / 4 + p_angle),
|
||||
cos(a + t / 2),
|
||||
cos(a + 3 * t / 4 - p_angle)]
|
||||
Sp = [0,
|
||||
sin(a + t / 4 + p_angle),
|
||||
sin(a + t / 2),
|
||||
sin(a + 3 * t / 4 - p_angle)]
|
||||
|
||||
verts_inner_base = [(Rb * C[I], Rb * S[I], d)
|
||||
for I in range(4)]
|
||||
verts_outer_base = [(Rd * C[I], Rd * S[I], d)
|
||||
for I in range(4)]
|
||||
verts_middle_tooth = [(radius * C[I], radius * S[I], d + crown / 3)
|
||||
for I in range(1, 4)]
|
||||
verts_tip_tooth = [(Ra * Cp[I], Ra * Sp[I], d + crown)
|
||||
for I in range(1, 4)]
|
||||
|
||||
return (verts_inner_base, verts_outer_base,
|
||||
verts_middle_tooth, verts_tip_tooth)
|
||||
|
||||
|
||||
# EXPERIMENTAL Calculate the vertex coordinates for a single
|
||||
# section of a gearspoke.
|
||||
# Returns them as a list of tuples
|
||||
#
|
||||
# a
|
||||
# t
|
||||
# d
|
||||
# radius
|
||||
# De
|
||||
# base
|
||||
# s
|
||||
# w
|
||||
# l
|
||||
# gap
|
||||
# width
|
||||
#
|
||||
# @todo Finish this.
|
||||
|
||||
def add_spoke(a, t, d, radius, De, base, s, w, l, gap=0, width=19):
|
||||
Rd = radius - De
|
||||
Rb = Rd - base
|
||||
|
||||
verts = []
|
||||
edgefaces = []
|
||||
edgefaces2 = []
|
||||
sf = []
|
||||
|
||||
if not gap:
|
||||
for N in range(width, 1, -2):
|
||||
edgefaces.append(len(verts))
|
||||
ts = t / 4
|
||||
tm = a + 2 * ts
|
||||
te = asin(w / Rb)
|
||||
td = te - ts
|
||||
t4 = ts + td * (width - N) / (width - 3.0)
|
||||
A = [tm + (i - int(N / 2)) * t4 for i in range(N)]
|
||||
C = [cos(i) for i in A]
|
||||
S = [sin(i) for i in A]
|
||||
|
||||
verts.extend((Rb * I, Rb * J, d) for (I, J) in zip(C, S))
|
||||
edgefaces2.append(len(verts) - 1)
|
||||
|
||||
Rb = Rb - s
|
||||
|
||||
n = 0
|
||||
for N in range(width, 3, -2):
|
||||
sf.extend([(i + n, i + 1 + n, i + 2 + n, i + N + n)
|
||||
for i in range(0, N - 1, 2)])
|
||||
sf.extend([(i + 2 + n, i + N + n, i + N + 1 + n, i + N + 2 + n)
|
||||
for i in range(0, N - 3, 2)])
|
||||
|
||||
n = n + N
|
||||
|
||||
return verts, edgefaces, edgefaces2, sf
|
||||
|
||||
|
||||
# Create gear geometry.
|
||||
# Returns:
|
||||
# * A list of vertices (list of tuples)
|
||||
# * A list of faces (list of lists)
|
||||
# * A list (group) of vertices of the tip (list of vertex indices)
|
||||
# * A list (group) of vertices of the valley (list of vertex indices)
|
||||
#
|
||||
# teethNum ... Number of teeth on the gear
|
||||
# radius ... Radius of the gear, negative for crown gear
|
||||
# Ad ... Addendum, extent of tooth above radius
|
||||
# De ... Dedendum, extent of tooth below radius
|
||||
# base ... Base, extent of gear below radius
|
||||
# p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
|
||||
# width ... Width, thickness of gear
|
||||
# skew ... Skew of teeth. (radiant)
|
||||
# conangle ... Conical angle of gear. (radiant)
|
||||
# rack
|
||||
# crown ... Inward pointing extend of crown teeth
|
||||
#
|
||||
# inner radius = radius - (De + base)
|
||||
|
||||
def add_gear(teethNum, radius, Ad, De, base, p_angle,
|
||||
width=1, skew=0, conangle=0, rack=0, crown=0.0):
|
||||
|
||||
if teethNum < 2:
|
||||
return None, None, None, None
|
||||
|
||||
t = 2 * pi / teethNum
|
||||
|
||||
if rack:
|
||||
teethNum = 1
|
||||
|
||||
#print(radius, width, conangle)
|
||||
if radius != 0:
|
||||
scale = (radius - 2 * width * tan(conangle)) / radius
|
||||
else:
|
||||
scale = radius - 2 * width * tan(conangle)
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
vgroup_top = [] # Vertex group of top/tip? vertices.
|
||||
vgroup_valley = [] # Vertex group of valley vertices
|
||||
|
||||
verts_bridge_prev = []
|
||||
for toothCnt in range(teethNum):
|
||||
a = toothCnt * t
|
||||
|
||||
verts_bridge_start = []
|
||||
verts_bridge_end = []
|
||||
|
||||
verts_outside_top = []
|
||||
verts_outside_bottom = []
|
||||
for (s, d, c, top) \
|
||||
in [(0, -width, 1, True), (skew, width, scale, False)]:
|
||||
|
||||
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
|
||||
radius * c, Ad * c, De * c, base * c, p_angle,
|
||||
rack, crown)
|
||||
|
||||
vertsIdx1 = list(range(len(verts), len(verts) + len(verts1)))
|
||||
verts.extend(verts1)
|
||||
vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
|
||||
verts.extend(verts2)
|
||||
vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
|
||||
verts.extend(verts3)
|
||||
vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
|
||||
verts.extend(verts4)
|
||||
|
||||
verts_outside = []
|
||||
verts_outside.extend(vertsIdx2[:2])
|
||||
verts_outside.append(vertsIdx3[0])
|
||||
verts_outside.extend(vertsIdx4)
|
||||
verts_outside.append(vertsIdx3[-1])
|
||||
verts_outside.append(vertsIdx2[-1])
|
||||
|
||||
if top:
|
||||
# verts_inside_top = vertsIdx1
|
||||
verts_outside_top = verts_outside
|
||||
|
||||
verts_bridge_start.append(vertsIdx1[0])
|
||||
verts_bridge_start.append(vertsIdx2[0])
|
||||
verts_bridge_end.append(vertsIdx1[-1])
|
||||
verts_bridge_end.append(vertsIdx2[-1])
|
||||
|
||||
else:
|
||||
# verts_inside_bottom = vertsIdx1
|
||||
verts_outside_bottom = verts_outside
|
||||
|
||||
verts_bridge_start.append(vertsIdx2[0])
|
||||
verts_bridge_start.append(vertsIdx1[0])
|
||||
verts_bridge_end.append(vertsIdx2[-1])
|
||||
verts_bridge_end.append(vertsIdx1[-1])
|
||||
|
||||
# Valley = first 2 vertices of outer base:
|
||||
vgroup_valley.extend(vertsIdx2[:1])
|
||||
# Top/tip vertices:
|
||||
vgroup_top.extend(vertsIdx4)
|
||||
|
||||
faces_tooth_middle_top = createFaces(vertsIdx2[1:], vertsIdx3,
|
||||
flipped=top)
|
||||
faces_tooth_outer_top = createFaces(vertsIdx3, vertsIdx4,
|
||||
flipped=top)
|
||||
|
||||
faces_base_top = createFaces(vertsIdx1, vertsIdx2, flipped=top)
|
||||
faces.extend(faces_base_top)
|
||||
|
||||
faces.extend(faces_tooth_middle_top)
|
||||
faces.extend(faces_tooth_outer_top)
|
||||
|
||||
# faces_inside = createFaces(verts_inside_top, verts_inside_bottom)
|
||||
# faces.extend(faces_inside)
|
||||
|
||||
faces_outside = createFaces(verts_outside_top, verts_outside_bottom,
|
||||
flipped=True)
|
||||
faces.extend(faces_outside)
|
||||
|
||||
if toothCnt == 0:
|
||||
verts_bridge_first = verts_bridge_start
|
||||
|
||||
# Bridge one tooth to the next
|
||||
if verts_bridge_prev:
|
||||
faces_bridge = createFaces(verts_bridge_prev, verts_bridge_start)
|
||||
faces.extend(faces_bridge)
|
||||
|
||||
# Remember "end" vertices for next tooth.
|
||||
verts_bridge_prev = verts_bridge_end
|
||||
|
||||
# Bridge the first to the last tooth.
|
||||
faces_bridge_f_l = createFaces(verts_bridge_prev, verts_bridge_first)
|
||||
faces.extend(faces_bridge_f_l)
|
||||
|
||||
return verts, faces, vgroup_top, vgroup_valley
|
||||
|
||||
|
||||
# Create spokes geometry
|
||||
# Returns:
|
||||
# * A list of vertices (list of tuples)
|
||||
# * A list of faces (list of lists)
|
||||
#
|
||||
# teethNum ... Number of teeth on the gear.
|
||||
# radius ... Radius of the gear, negative for crown gear
|
||||
# De ... Dedendum, extent of tooth below radius
|
||||
# base ... Base, extent of gear below radius
|
||||
# width ... Width, thickness of gear
|
||||
# conangle ... Conical angle of gear. (radiant)
|
||||
# rack
|
||||
# spoke
|
||||
# spbevel
|
||||
# spwidth
|
||||
# splength
|
||||
# spresol
|
||||
#
|
||||
# @todo Finish this
|
||||
# @todo Create a function that takes a "Gear" and creates a
|
||||
# matching "Gear Spokes" object
|
||||
|
||||
def add_spokes(teethNum, radius, De, base, width=1, conangle=0, rack=0,
|
||||
spoke=3, spbevel=0.1, spwidth=0.2, splength=1.0, spresol=9):
|
||||
|
||||
if teethNum < 2:
|
||||
return None, None, None, None
|
||||
|
||||
if spoke < 2:
|
||||
return None, None, None, None
|
||||
|
||||
t = 2 * pi / teethNum
|
||||
|
||||
if rack:
|
||||
teethNum = 1
|
||||
|
||||
scale = (radius - 2 * width * tan(conangle)) / radius
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
c = scale # debug
|
||||
|
||||
fl = len(verts)
|
||||
for toothCnt in range(teethNum):
|
||||
a = toothCnt * t
|
||||
s = 0 # For test
|
||||
|
||||
if toothCnt % spoke == 0:
|
||||
for d in (-width, width):
|
||||
sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
|
||||
radius * c, De * c, base * c,
|
||||
spbevel, spwidth, splength, 0, spresol)
|
||||
verts.extend(sv)
|
||||
faces.extend([j + fl for j in i] for i in sf)
|
||||
fl += len(sv)
|
||||
|
||||
d1 = fl - len(sv)
|
||||
d2 = fl - 2 * len(sv)
|
||||
|
||||
faces.extend([(i + d2, j + d2, j + d1, i + d1)
|
||||
for (i, j) in zip(edgefaces[:-1], edgefaces[1:])])
|
||||
faces.extend([(i + d2, j + d2, j + d1, i + d1)
|
||||
for (i, j) in zip(edgefaces2[:-1], edgefaces2[1:])])
|
||||
|
||||
else:
|
||||
for d in (-width, width):
|
||||
sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
|
||||
radius * c, De * c, base * c,
|
||||
spbevel, spwidth, splength, 1, spresol)
|
||||
|
||||
verts.extend(sv)
|
||||
fl += len(sv)
|
||||
|
||||
d1 = fl - len(sv)
|
||||
d2 = fl - 2 * len(sv)
|
||||
|
||||
faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
|
||||
for (i) in range(0, 3)])
|
||||
faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
|
||||
for (i) in range(5, 8)])
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
# Create worm geometry.
|
||||
# Returns:
|
||||
# * A list of vertices
|
||||
# * A list of faces
|
||||
# * A list (group) of vertices of the tip
|
||||
# * A list (group) of vertices of the valley
|
||||
#
|
||||
# teethNum ... Number of teeth on the worm
|
||||
# radius ... Radius of the gear, negative for crown gear
|
||||
# Ad ... Addendum, extent of tooth above radius
|
||||
# De ... Dedendum, extent of tooth below radius
|
||||
# p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
|
||||
# width ... Width, thickness of gear
|
||||
# crown ... Inward pointing extend of crown teeth
|
||||
#
|
||||
# @todo: Fix teethNum. Some numbers are not possible yet
|
||||
# @todo: Create start & end geometry (closing faces)
|
||||
|
||||
def add_worm(teethNum, rowNum, radius, Ad, De, p_angle,
|
||||
width=1, skew=radians(11.25), crown=0.0):
|
||||
|
||||
worm = teethNum
|
||||
teethNum = 24
|
||||
|
||||
t = 2 * pi / teethNum
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
vgroup_top = [] # Vertex group of top/tip? vertices.
|
||||
vgroup_valley = [] # Vertex group of valley vertices
|
||||
|
||||
# width = width / 2.0
|
||||
|
||||
edgeloop_prev = []
|
||||
for Row in range(rowNum):
|
||||
edgeloop = []
|
||||
|
||||
for toothCnt in range(teethNum):
|
||||
a = toothCnt * t
|
||||
|
||||
s = Row * skew
|
||||
d = Row * width
|
||||
c = 1
|
||||
|
||||
isTooth = False
|
||||
if toothCnt % (teethNum / worm) != 0:
|
||||
# Flat
|
||||
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
|
||||
radius - De, 0.0, 0.0, 0, p_angle)
|
||||
|
||||
# Ignore other verts than the "other base".
|
||||
verts1 = verts3 = verts4 = []
|
||||
|
||||
else:
|
||||
# Tooth
|
||||
isTooth = True
|
||||
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
|
||||
radius * c, Ad * c, De * c, 0 * c, p_angle, 0, crown)
|
||||
|
||||
# Remove various unneeded verts (if we are "inside" the tooth)
|
||||
del(verts2[2]) # Central vertex in the base of the tooth.
|
||||
del(verts3[1]) # Central vertex in the middle of the tooth.
|
||||
|
||||
vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
|
||||
verts.extend(verts2)
|
||||
vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
|
||||
verts.extend(verts3)
|
||||
vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
|
||||
verts.extend(verts4)
|
||||
|
||||
if isTooth:
|
||||
verts_current = []
|
||||
verts_current.extend(vertsIdx2[:2])
|
||||
verts_current.append(vertsIdx3[0])
|
||||
verts_current.extend(vertsIdx4)
|
||||
verts_current.append(vertsIdx3[-1])
|
||||
verts_current.append(vertsIdx2[-1])
|
||||
|
||||
# Valley = first 2 vertices of outer base:
|
||||
vgroup_valley.extend(vertsIdx2[:1])
|
||||
# Top/tip vertices:
|
||||
vgroup_top.extend(vertsIdx4)
|
||||
|
||||
else:
|
||||
# Flat
|
||||
verts_current = vertsIdx2
|
||||
|
||||
# Valley - all of them.
|
||||
vgroup_valley.extend(vertsIdx2)
|
||||
|
||||
edgeloop.extend(verts_current)
|
||||
|
||||
# Create faces between rings/rows.
|
||||
if edgeloop_prev:
|
||||
faces_row = createFaces(edgeloop, edgeloop_prev, closed=True)
|
||||
faces.extend(faces_row)
|
||||
|
||||
# Remember last ring/row of vertices for next ring/row iteration.
|
||||
edgeloop_prev = edgeloop
|
||||
|
||||
return verts, faces, vgroup_top, vgroup_valley
|
||||
|
||||
def AddGearMesh(self, context):
|
||||
|
||||
verts, faces, verts_tip, verts_valley = add_gear(
|
||||
self.number_of_teeth,
|
||||
self.radius,
|
||||
self.addendum,
|
||||
self.dedendum,
|
||||
self.base,
|
||||
self.angle,
|
||||
width=self.width,
|
||||
skew=self.skew,
|
||||
conangle=self.conangle,
|
||||
crown=self.crown
|
||||
)
|
||||
|
||||
mesh = bpy.data.meshes.new("Gear")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
return mesh, verts_tip, verts_valley
|
||||
|
||||
|
||||
class AddGear(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_gear"
|
||||
bl_label = "Add Gear"
|
||||
bl_description = "Construct a gear mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Gear : BoolProperty(name = "Gear",
|
||||
default = True,
|
||||
description = "Gear")
|
||||
|
||||
#### change properties
|
||||
name : StringProperty(name = "Name",
|
||||
description = "Name")
|
||||
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Gear")
|
||||
|
||||
number_of_teeth: IntProperty(name="Teeth",
|
||||
description="Number of teeth on the gear",
|
||||
min=2,
|
||||
soft_max=1000,
|
||||
default=12
|
||||
)
|
||||
radius: FloatProperty(name="Radius",
|
||||
description="Radius of the gear, negative for crown gear",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=1.0
|
||||
)
|
||||
addendum: FloatProperty(name="Addendum",
|
||||
description="Addendum, extent of tooth above radius",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.1
|
||||
)
|
||||
dedendum: FloatProperty(name="Dedendum",
|
||||
description="Dedendum, extent of tooth below radius",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.1
|
||||
)
|
||||
angle: FloatProperty(name="Pressure Angle",
|
||||
description="Pressure angle, skewness of tooth tip",
|
||||
soft_min=radians(-45.0),
|
||||
soft_max=radians(45.0),
|
||||
unit='ROTATION',
|
||||
default=radians(20.0)
|
||||
)
|
||||
base: FloatProperty(name="Base",
|
||||
description="Base, extent of gear below radius",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.2
|
||||
)
|
||||
width: FloatProperty(name="Width",
|
||||
description="Width, thickness of gear",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.2
|
||||
)
|
||||
skew: FloatProperty(name="Skewness",
|
||||
description="Skew of teeth",
|
||||
soft_min=radians(-360.0),
|
||||
soft_max=radians(360.0),
|
||||
unit='ROTATION',
|
||||
default=radians(0.0)
|
||||
)
|
||||
conangle: FloatProperty(name="Conical Angle",
|
||||
description="Conical angle of gear",
|
||||
soft_min=radians(-360.0),
|
||||
soft_max=radians(360.0),
|
||||
unit='ROTATION',
|
||||
default=radians(0.0)
|
||||
)
|
||||
crown: FloatProperty(name="Crown",
|
||||
description="Inward pointing extend of crown teeth",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.0
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'number_of_teeth')
|
||||
layout.separator()
|
||||
layout.prop(self, 'radius')
|
||||
layout.prop(self, 'width')
|
||||
layout.prop(self, 'base')
|
||||
layout.separator()
|
||||
layout.prop(self, 'dedendum')
|
||||
layout.prop(self, 'addendum')
|
||||
layout.separator()
|
||||
layout.prop(self, 'angle')
|
||||
layout.prop(self, 'skew')
|
||||
layout.prop(self, 'conangle')
|
||||
layout.prop(self, 'crown')
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene is not None
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Gear' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
|
||||
obj.data = mesh
|
||||
try:
|
||||
bpy.ops.object.vertex_group_remove(all=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
# Create vertex groups from stored vertices.
|
||||
tipGroup = obj.vertex_groups.new(name='Tips')
|
||||
tipGroup.add(verts_tip, 1.0, 'ADD')
|
||||
|
||||
valleyGroup = obj.vertex_groups.new(name='Valleys')
|
||||
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
||||
|
||||
obj.data["Gear"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in GearParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
# Create vertex groups from stored vertices.
|
||||
tipGroup = obj.vertex_groups.new(name='Tips')
|
||||
tipGroup.add(verts_tip, 1.0, 'ADD')
|
||||
|
||||
valleyGroup = obj.vertex_groups.new(name='Valleys')
|
||||
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
||||
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.execute(context)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def GearParameters():
|
||||
GearParameters = [
|
||||
"number_of_teeth",
|
||||
"radius",
|
||||
"addendum",
|
||||
"dedendum",
|
||||
"base",
|
||||
"angle",
|
||||
"width",
|
||||
"skew",
|
||||
"conangle",
|
||||
"crown",
|
||||
]
|
||||
return GearParameters
|
||||
|
||||
def AddWormGearMesh(self, context):
|
||||
|
||||
verts, faces, verts_tip, verts_valley = add_worm(
|
||||
self.number_of_teeth,
|
||||
self.number_of_rows,
|
||||
self.radius,
|
||||
self.addendum,
|
||||
self.dedendum,
|
||||
self.angle,
|
||||
width=self.row_height,
|
||||
skew=self.skew,
|
||||
crown=self.crown
|
||||
)
|
||||
|
||||
mesh = bpy.data.meshes.new("Worm Gear")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
return mesh, verts_tip, verts_valley
|
||||
|
||||
|
||||
class AddWormGear(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_worm_gear"
|
||||
bl_label = "Add Worm Gear"
|
||||
bl_description = "Construct a worm gear mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
WormGear : BoolProperty(name = "WormGear",
|
||||
default = True,
|
||||
description = "WormGear")
|
||||
|
||||
#### change properties
|
||||
name : StringProperty(name = "Name",
|
||||
description = "Name")
|
||||
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change WormGear")
|
||||
|
||||
number_of_teeth: IntProperty(
|
||||
name="Teeth",
|
||||
description="Number of teeth on the gear",
|
||||
min=1,
|
||||
soft_max=1000,
|
||||
default=12
|
||||
)
|
||||
number_of_rows: IntProperty(
|
||||
name="Rows",
|
||||
description="Number of rows on the worm gear",
|
||||
min=0,
|
||||
soft_max=1000,
|
||||
default=32
|
||||
)
|
||||
radius: FloatProperty(
|
||||
name="Radius",
|
||||
description="Radius of the gear, negative for crown gear",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=1.0
|
||||
)
|
||||
addendum: FloatProperty(
|
||||
name="Addendum",
|
||||
description="Addendum, extent of tooth above radius",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.1
|
||||
)
|
||||
dedendum: FloatProperty(
|
||||
name="Dedendum",
|
||||
description="Dedendum, extent of tooth below radius",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.1
|
||||
)
|
||||
angle: FloatProperty(
|
||||
name="Pressure Angle",
|
||||
description="Pressure angle, skewness of tooth tip",
|
||||
soft_min=radians(-45.0),
|
||||
soft_max=radians(45.0),
|
||||
default=radians(20.0),
|
||||
unit='ROTATION'
|
||||
)
|
||||
row_height: FloatProperty(
|
||||
name="Row Height",
|
||||
description="Height of each Row",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.2
|
||||
)
|
||||
skew: FloatProperty(
|
||||
name="Skewness per Row",
|
||||
description="Skew of each row",
|
||||
soft_min=radians(-360.0),
|
||||
soft_max=radians(360.0),
|
||||
default=radians(11.25),
|
||||
unit='ROTATION'
|
||||
)
|
||||
crown: FloatProperty(
|
||||
name="Crown",
|
||||
description="Inward pointing extend of crown teeth",
|
||||
soft_min=-1000.0,
|
||||
soft_max=1000.0,
|
||||
unit='LENGTH',
|
||||
default=0.0
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, "number_of_teeth")
|
||||
layout.prop(self, "number_of_rows")
|
||||
layout.separator()
|
||||
layout.prop(self, "radius")
|
||||
layout.prop(self, "row_height")
|
||||
layout.separator()
|
||||
layout.prop(self, "addendum")
|
||||
layout.prop(self, "dedendum")
|
||||
layout.separator()
|
||||
layout.prop(self, "angle")
|
||||
layout.prop(self, "skew")
|
||||
layout.prop(self, "crown")
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('WormGear' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
|
||||
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
|
||||
obj.data = mesh
|
||||
try:
|
||||
bpy.ops.object.vertex_group_remove(all=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
# Create vertex groups from stored vertices.
|
||||
tipGroup = obj.vertex_groups.new(name = 'Tips')
|
||||
tipGroup.add(verts_tip, 1.0, 'ADD')
|
||||
|
||||
valleyGroup = obj.vertex_groups.new(name = 'Valleys')
|
||||
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
||||
|
||||
obj.data["WormGear"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in WormGearParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
# Create vertex groups from stored vertices.
|
||||
tipGroup = obj.vertex_groups.new(name = 'Tips')
|
||||
tipGroup.add(verts_tip, 1.0, 'ADD')
|
||||
|
||||
valleyGroup = obj.vertex_groups.new(name = 'Valleys')
|
||||
valleyGroup.add(verts_valley, 1.0, 'ADD')
|
||||
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def WormGearParameters():
|
||||
WormGearParameters = [
|
||||
"number_of_teeth",
|
||||
"number_of_rows",
|
||||
"radius",
|
||||
"addendum",
|
||||
"dedendum",
|
||||
"angle",
|
||||
"row_height",
|
||||
"skew",
|
||||
"crown",
|
||||
]
|
||||
return WormGearParameters
|
||||
@@ -0,0 +1,516 @@
|
||||
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: Pontiac, Fourmadmen, Dreampainter
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from mathutils import (
|
||||
Vector,
|
||||
Quaternion,
|
||||
)
|
||||
from math import cos, sin, pi
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
# Create a new mesh (object) from verts/edges/faces.
|
||||
# verts/edges/faces ... List of vertices/edges/faces for the
|
||||
# new mesh (as used in from_pydata)
|
||||
# name ... Name of the new mesh (& object)
|
||||
|
||||
def create_mesh_object(context, self, verts, edges, faces, name):
|
||||
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
|
||||
# Make a mesh from a list of verts/edges/faces.
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Update mesh geometry after adding stuff.
|
||||
mesh.update()
|
||||
|
||||
from bpy_extras import object_utils
|
||||
return object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
|
||||
# A very simple "bridge" tool.
|
||||
|
||||
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
||||
faces = []
|
||||
|
||||
if not vertIdx1 or not vertIdx2:
|
||||
return None
|
||||
|
||||
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
||||
return None
|
||||
|
||||
fan = False
|
||||
if (len(vertIdx1) != len(vertIdx2)):
|
||||
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
||||
fan = True
|
||||
else:
|
||||
return None
|
||||
|
||||
total = len(vertIdx2)
|
||||
|
||||
if closed:
|
||||
# Bridge the start with the end
|
||||
if flipped:
|
||||
face = [
|
||||
vertIdx1[0],
|
||||
vertIdx2[0],
|
||||
vertIdx2[total - 1]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
else:
|
||||
face = [vertIdx2[0], vertIdx1[0]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
face.append(vertIdx2[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
# Bridge the rest of the faces
|
||||
for num in range(total - 1):
|
||||
if flipped:
|
||||
if fan:
|
||||
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx2[num], vertIdx1[num],
|
||||
vertIdx1[num + 1], vertIdx2[num + 1]]
|
||||
faces.append(face)
|
||||
else:
|
||||
if fan:
|
||||
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx1[num], vertIdx2[num],
|
||||
vertIdx2[num + 1], vertIdx1[num + 1]]
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
# @todo Clean up vertex&face creation process a bit.
|
||||
def add_gem(r1, r2, seg, h1, h2):
|
||||
"""
|
||||
r1 = pavilion radius
|
||||
r2 = crown radius
|
||||
seg = number of segments
|
||||
h1 = pavilion height
|
||||
h2 = crown height
|
||||
Generates the vertices and faces of the gem
|
||||
"""
|
||||
|
||||
verts = []
|
||||
|
||||
a = 2.0 * pi / seg # Angle between segments
|
||||
offset = a / 2.0 # Middle between segments
|
||||
|
||||
r3 = ((r1 + r2) / 2.0) / cos(offset) # Middle of crown
|
||||
r4 = (r1 / 2.0) / cos(offset) # Middle of pavilion
|
||||
h3 = h2 / 2.0 # Middle of crown height
|
||||
h4 = -h1 / 2.0 # Middle of pavilion height
|
||||
|
||||
# Tip
|
||||
vert_tip = len(verts)
|
||||
verts.append(Vector((0.0, 0.0, -h1)))
|
||||
|
||||
# Middle vertex of the flat side (crown)
|
||||
vert_flat = len(verts)
|
||||
verts.append(Vector((0.0, 0.0, h2)))
|
||||
|
||||
edgeloop_flat = []
|
||||
for i in range(seg):
|
||||
s1 = sin(i * a)
|
||||
s2 = sin(offset + i * a)
|
||||
c1 = cos(i * a)
|
||||
c2 = cos(offset + i * a)
|
||||
|
||||
verts.append((r4 * s1, r4 * c1, h4)) # Middle of pavilion
|
||||
verts.append((r1 * s2, r1 * c2, 0.0)) # Pavilion
|
||||
verts.append((r3 * s1, r3 * c1, h3)) # Middle crown
|
||||
edgeloop_flat.append(len(verts))
|
||||
verts.append((r2 * s2, r2 * c2, h2)) # Crown
|
||||
|
||||
faces = []
|
||||
|
||||
for index in range(seg):
|
||||
i = index * 4
|
||||
j = ((index + 1) % seg) * 4
|
||||
|
||||
faces.append([j + 2, vert_tip, i + 2, i + 3]) # Tip -> Middle of pav
|
||||
faces.append([j + 2, i + 3, j + 3]) # Middle of pav -> pav
|
||||
faces.append([j + 3, i + 3, j + 4]) # Pav -> Middle crown
|
||||
faces.append([j + 4, i + 3, i + 4, i + 5]) # Crown quads
|
||||
faces.append([j + 4, i + 5, j + 5]) # Middle crown -> crown
|
||||
|
||||
faces_flat = createFaces([vert_flat], edgeloop_flat, closed=True, flipped=True)
|
||||
faces.extend(faces_flat)
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
def add_diamond(segments, girdle_radius, table_radius,
|
||||
crown_height, pavilion_height):
|
||||
|
||||
PI_2 = pi * 2.0
|
||||
z_axis = (0.0, 0.0, -1.0)
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
height_flat = crown_height
|
||||
height_middle = 0.0
|
||||
height_tip = -pavilion_height
|
||||
|
||||
# Middle vertex of the flat side (crown)
|
||||
vert_flat = len(verts)
|
||||
verts.append(Vector((0.0, 0.0, height_flat)))
|
||||
|
||||
# Tip
|
||||
vert_tip = len(verts)
|
||||
verts.append(Vector((0.0, 0.0, height_tip)))
|
||||
|
||||
verts_flat = []
|
||||
verts_girdle = []
|
||||
|
||||
for index in range(segments):
|
||||
quat = Quaternion(z_axis, (index / segments) * PI_2)
|
||||
|
||||
# angle = PI_2 * index / segments # UNUSED
|
||||
|
||||
# Row for flat side
|
||||
verts_flat.append(len(verts))
|
||||
vec = quat @ Vector((table_radius, 0.0, height_flat))
|
||||
verts.append(vec)
|
||||
|
||||
# Row for the middle/girdle
|
||||
verts_girdle.append(len(verts))
|
||||
vec = quat @ Vector((girdle_radius, 0.0, height_middle))
|
||||
verts.append(vec)
|
||||
|
||||
# Flat face
|
||||
faces_flat = createFaces([vert_flat], verts_flat, closed=True,
|
||||
flipped=True)
|
||||
# Side face
|
||||
faces_side = createFaces(verts_girdle, verts_flat, closed=True)
|
||||
# Tip faces
|
||||
faces_tip = createFaces([vert_tip], verts_girdle, closed=True)
|
||||
|
||||
faces.extend(faces_tip)
|
||||
faces.extend(faces_side)
|
||||
faces.extend(faces_flat)
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
class AddDiamond(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_diamond_add"
|
||||
bl_label = "Add Diamond"
|
||||
bl_description = "Construct a diamond mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Diamond : BoolProperty(name = "Diamond",
|
||||
default = True,
|
||||
description = "Diamond")
|
||||
|
||||
#### change properties
|
||||
name : StringProperty(name = "Name",
|
||||
description = "Name")
|
||||
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Diamond")
|
||||
|
||||
segments: IntProperty(
|
||||
name="Segments",
|
||||
description="Number of segments for the diamond",
|
||||
min=3,
|
||||
max=256,
|
||||
default=32
|
||||
)
|
||||
girdle_radius: FloatProperty(
|
||||
name="Girdle Radius",
|
||||
description="Girdle radius of the diamond",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=1.0
|
||||
)
|
||||
table_radius: FloatProperty(
|
||||
name="Table Radius",
|
||||
description="Girdle radius of the diamond",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.6
|
||||
)
|
||||
crown_height: FloatProperty(
|
||||
name="Crown Height",
|
||||
description="Crown height of the diamond",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.35
|
||||
)
|
||||
pavilion_height: FloatProperty(
|
||||
name="Pavilion Height",
|
||||
description="Pavilion height of the diamond",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.8
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, "segments")
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "girdle_radius", text='Radius Girdle')
|
||||
col.prop(self, "table_radius", text='Table')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "crown_height", text='Height Crown')
|
||||
col.prop(self, "pavilion_height", text='Pavilion')
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Diamond' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
|
||||
verts, faces = add_diamond(self.segments,
|
||||
self.girdle_radius,
|
||||
self.table_radius,
|
||||
self.crown_height,
|
||||
self.pavilion_height)
|
||||
mesh = bpy.data.meshes.new("TMP")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
obj.data = mesh
|
||||
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = add_diamond(self.segments,
|
||||
self.girdle_radius,
|
||||
self.table_radius,
|
||||
self.crown_height,
|
||||
self.pavilion_height)
|
||||
|
||||
obj = create_mesh_object(context, self, verts, [], faces, "Diamond")
|
||||
|
||||
obj.data["Diamond"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in DiamondParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = add_diamond(self.segments,
|
||||
self.girdle_radius,
|
||||
self.table_radius,
|
||||
self.crown_height,
|
||||
self.pavilion_height)
|
||||
|
||||
obj = create_mesh_object(context, self, verts, [], faces, "TMP")
|
||||
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def DiamondParameters():
|
||||
DiamondParameters = [
|
||||
"segments",
|
||||
"girdle_radius",
|
||||
"table_radius",
|
||||
"crown_height",
|
||||
"pavilion_height",
|
||||
]
|
||||
return DiamondParameters
|
||||
|
||||
|
||||
class AddGem(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_gem_add"
|
||||
bl_label = "Add Gem"
|
||||
bl_description = "Construct an offset faceted gem mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Gem : BoolProperty(name = "Gem",
|
||||
default = True,
|
||||
description = "Gem")
|
||||
|
||||
#### change properties
|
||||
name : StringProperty(name = "Name",
|
||||
description = "Name")
|
||||
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Gem")
|
||||
|
||||
segments: IntProperty(
|
||||
name="Segments",
|
||||
description="Longitudial segmentation",
|
||||
min=3,
|
||||
max=265,
|
||||
default=8
|
||||
)
|
||||
pavilion_radius: FloatProperty(
|
||||
name="Radius",
|
||||
description="Radius of the gem",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=1.0
|
||||
)
|
||||
crown_radius: FloatProperty(
|
||||
name="Table Radius",
|
||||
description="Radius of the table(top)",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.6
|
||||
)
|
||||
crown_height: FloatProperty(
|
||||
name="Table height",
|
||||
description="Height of the top half",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.35
|
||||
)
|
||||
pavilion_height: FloatProperty(
|
||||
name="Pavilion height",
|
||||
description="Height of bottom half",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.8
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, "segments")
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "crown_radius", text='Radius Crown')
|
||||
col.prop(self, "pavilion_radius", text='Pavilion')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "crown_height", text='Height Crown')
|
||||
col.prop(self, "pavilion_height", text='Pavilion')
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Gem' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
verts, faces = add_gem(
|
||||
self.pavilion_radius,
|
||||
self.crown_radius,
|
||||
self.segments,
|
||||
self.pavilion_height,
|
||||
self.crown_height)
|
||||
mesh = bpy.data.meshes.new("TMP")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = add_gem(
|
||||
self.pavilion_radius,
|
||||
self.crown_radius,
|
||||
self.segments,
|
||||
self.pavilion_height,
|
||||
self.crown_height)
|
||||
|
||||
obj = create_mesh_object(context, self, verts, [], faces, "Gem")
|
||||
|
||||
obj.data["Gem"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in GemParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = add_gem(
|
||||
self.pavilion_radius,
|
||||
self.crown_radius,
|
||||
self.segments,
|
||||
self.pavilion_height,
|
||||
self.crown_height)
|
||||
|
||||
obj = create_mesh_object(context, self, verts, [], faces, "TMP")
|
||||
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def GemParameters():
|
||||
GemParameters = [
|
||||
"segments",
|
||||
"pavilion_radius",
|
||||
"crown_radius",
|
||||
"crown_height",
|
||||
"pavilion_height",
|
||||
]
|
||||
return GemParameters
|
||||
@@ -0,0 +1,341 @@
|
||||
# SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: Kayo Phoenix
|
||||
|
||||
import bpy
|
||||
from bpy_extras import object_utils
|
||||
from math import (
|
||||
pi, sin,
|
||||
cos,
|
||||
)
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
BoolVectorProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
class honeycomb_geometry():
|
||||
def __init__(self, rows, cols, D, E):
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
self.D = D
|
||||
self.E = E
|
||||
|
||||
self.hE = 0.5 * self.E
|
||||
self.R = 0.5 * self.D
|
||||
|
||||
self.a = sin(pi / 3)
|
||||
|
||||
self.d = self.a * self.D
|
||||
self.hd = 0.5 * self.d
|
||||
self.e = self.hE / self.a
|
||||
self.he = 0.5 * self.e
|
||||
self.r = self.R - self.e
|
||||
self.hr = 0.5 * self.r
|
||||
|
||||
self.H = self.R * (1.5 * self.rows + 0.5) + self.e
|
||||
if self.rows > 1:
|
||||
self.W = self.d * (self.cols + 0.5) + self.E
|
||||
else:
|
||||
self.W = self.d * self.cols + self.E
|
||||
|
||||
self.hH = 0.5 * self.H
|
||||
self.hW = 0.5 * self.W
|
||||
|
||||
self.sy = -self.hH + self.he + self.R
|
||||
self.sx = -self.hW + self.hE + self.hd
|
||||
|
||||
self.gx = self.hd
|
||||
|
||||
self.dy = 1.5 * self.R
|
||||
self.dx = self.d
|
||||
|
||||
def vert(self, row, col):
|
||||
# full cell
|
||||
if row >= 0 and row < self.rows and col >= 0 and col < self.cols:
|
||||
return [0, 1, 2, 3, 4, 5]
|
||||
# right down corner
|
||||
if row == -1 and col == self.cols - 1:
|
||||
return [1, 2]
|
||||
if row == 0 and self.rows > 1 and col == self.cols:
|
||||
return [1, 2, 3]
|
||||
# left down corner
|
||||
if row == -1 and col == -1:
|
||||
return [0, 1]
|
||||
if self.rows % 2:
|
||||
# left up corner
|
||||
if row == self.rows and col == -1:
|
||||
return [4, 5]
|
||||
# right up corner
|
||||
if row == self.rows and col == self.cols - 1:
|
||||
return [3, 4]
|
||||
if row == self.rows - 1 and self.rows > 1 and col == self.cols:
|
||||
return [2, 3, 4]
|
||||
else:
|
||||
# left up corner
|
||||
if row == self.rows and col == 0:
|
||||
return [4, 5]
|
||||
if row == self.rows - 1 and self.rows > 1 and col == -1:
|
||||
return [0, 4, 5]
|
||||
# right up corner
|
||||
if row == self.rows and col == self.cols:
|
||||
return [3, 4]
|
||||
# horizontal lines
|
||||
if col >= 0 and col < self.cols:
|
||||
if row == -1:
|
||||
return [0, 1, 2]
|
||||
if row == self.rows:
|
||||
return [3, 4, 5]
|
||||
# vertical lines
|
||||
if row >= 0 and row < self.rows:
|
||||
if col == -1:
|
||||
if row % 2:
|
||||
return [0, 1, 4, 5]
|
||||
else:
|
||||
return [0, 5]
|
||||
if col == self.cols:
|
||||
if row % 2 or self.rows == 1:
|
||||
return [2, 3]
|
||||
else:
|
||||
return [1, 2, 3, 4]
|
||||
return []
|
||||
|
||||
def cell(self, row, col, idx):
|
||||
cp = [self.sx + self.dx * col, self.sy + self.dy * row, 0] # central point
|
||||
if row % 2:
|
||||
cp[0] += self.gx
|
||||
co = [] # vertices coords
|
||||
vi = self.vert(row, col)
|
||||
ap = {}
|
||||
|
||||
for i in vi:
|
||||
a = pi / 6 + i * pi / 3 # angle
|
||||
ap[i] = idx + len(co)
|
||||
co.append((cp[0] + cos(a) * self.r, cp[1] + sin(a) * self.r, cp[2]))
|
||||
return co, ap
|
||||
|
||||
def generate(self):
|
||||
ar = 1
|
||||
ac = 1
|
||||
|
||||
cells = []
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
for row in range(-ar, self.rows + ar):
|
||||
level = []
|
||||
for col in range(-ac, self.cols + ac):
|
||||
co, ap = self.cell(row, col, len(verts))
|
||||
verts += co
|
||||
level.append(ap)
|
||||
cells.append(level)
|
||||
|
||||
# bottom row
|
||||
row = 0
|
||||
for col in range(1, len(cells[row]) - 1):
|
||||
s = cells[row][col]
|
||||
l = cells[row][col - 1]
|
||||
u = cells[row + 1][col]
|
||||
|
||||
faces.append((s[1], u[5], u[4], s[2]))
|
||||
faces.append((s[2], u[4], l[0]))
|
||||
|
||||
# top row
|
||||
row = len(cells) - 1
|
||||
cs = 0
|
||||
if row % 2:
|
||||
cs += 1
|
||||
for col in range(1 + cs, len(cells[row]) - 1):
|
||||
s = cells[row][col]
|
||||
l = cells[row][col - 1]
|
||||
d = cells[row - 1][col - cs]
|
||||
faces.append((s[3], l[5], d[1]))
|
||||
faces.append([s[3], d[1], d[0], s[4]])
|
||||
|
||||
# middle rows
|
||||
for row in range(1, len(cells) - 1):
|
||||
cs = 0
|
||||
if row % 2:
|
||||
cs += 1
|
||||
for col in range(1, len(cells[row]) - 1):
|
||||
s = cells[row][col]
|
||||
l = cells[row][col - 1]
|
||||
u = cells[row + 1][col - cs]
|
||||
d = cells[row - 1][col - cs]
|
||||
|
||||
faces.append((s[1], u[5], u[4], s[2]))
|
||||
faces.append((s[2], u[4], l[0]))
|
||||
faces.append([s[2], l[0], l[5], s[3]])
|
||||
faces.append((s[3], l[5], d[1]))
|
||||
faces.append([s[3], d[1], d[0], s[4]])
|
||||
|
||||
# right column
|
||||
row = 0
|
||||
col = len(cells[row]) - 1
|
||||
for row in range(1, len(cells) - 1):
|
||||
cs = 0
|
||||
if row % 2:
|
||||
cs += 1
|
||||
|
||||
s = cells[row][col]
|
||||
l = cells[row][col - 1]
|
||||
u = cells[row + 1][col - cs]
|
||||
d = cells[row - 1][col - cs]
|
||||
|
||||
if row % 2 and row < len(cells) - 2:
|
||||
faces.append((s[1], u[5], u[4], s[2]))
|
||||
faces.append((s[2], u[4], l[0]))
|
||||
faces.append([s[2], l[0], l[5], s[3]])
|
||||
faces.append((s[3], l[5], d[1]))
|
||||
if row % 2 and row > 1:
|
||||
faces.append([s[3], d[1], d[0], s[4]])
|
||||
|
||||
# final fix
|
||||
if not self.rows % 2:
|
||||
row = len(cells) - 1
|
||||
s = cells[row][col]
|
||||
l = cells[row][col - 1]
|
||||
d = cells[row - 1][col - 1]
|
||||
faces.append((s[3], l[5], d[1]))
|
||||
faces.append([s[3], d[1], d[0], s[4]])
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
def edge_max(diam):
|
||||
return diam * sin(pi / 3)
|
||||
|
||||
|
||||
class add_mesh_honeycomb(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.honeycomb_add"
|
||||
bl_label = "Add Honeycomb"
|
||||
bl_description = "Simple honeycomb mesh generator"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def fix_edge(self, context):
|
||||
m = edge_max(self.diam)
|
||||
if self.edge > m:
|
||||
self.edge = m
|
||||
|
||||
HoneyComb : BoolProperty(name = "HoneyComb",
|
||||
default = True,
|
||||
description = "HoneyComb")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change HoneyComb")
|
||||
|
||||
rows: IntProperty(
|
||||
name="Rows",
|
||||
default=2,
|
||||
min=1, max=100,
|
||||
description='Number of the rows'
|
||||
)
|
||||
cols: IntProperty(
|
||||
name='Columns',
|
||||
default=2,
|
||||
min=1, max=100,
|
||||
description='Number of the columns'
|
||||
)
|
||||
diam: FloatProperty(
|
||||
name='Cell Diameter',
|
||||
default=1.0,
|
||||
min=0.0, update=fix_edge,
|
||||
description='Diameter of the cell'
|
||||
)
|
||||
edge: FloatProperty(
|
||||
name='Edge Width',
|
||||
default=0.1,
|
||||
min=0.0, update=fix_edge,
|
||||
description='Width of the edge'
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.prop(self, 'rows', expand=True)
|
||||
layout.prop(self, 'cols', expand=True)
|
||||
layout.prop(self, 'diam', expand=True)
|
||||
layout.prop(self, 'edge', expand=True)
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene is not None
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('HoneyComb' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
|
||||
verts, faces = comb.generate()
|
||||
mesh = bpy.data.meshes.new('HoneyComb')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
|
||||
verts, faces = comb.generate()
|
||||
mesh = bpy.data.meshes.new('HoneyComb')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["HoneyComb"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in HoneyCombParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
|
||||
verts, faces = comb.generate()
|
||||
mesh = bpy.data.meshes.new('HoneyComb')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def HoneyCombParameters():
|
||||
HoneyCombParameters = [
|
||||
"rows",
|
||||
"cols",
|
||||
"diam",
|
||||
"edge",
|
||||
]
|
||||
return HoneyCombParameters
|
||||
@@ -0,0 +1,198 @@
|
||||
# SPDX-FileCopyrightText: 2015 Sugiany
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import bpy
|
||||
|
||||
from bpy_extras.object_utils import (
|
||||
AddObjectHelper,
|
||||
object_data_add,
|
||||
)
|
||||
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
BoolVectorProperty,
|
||||
FloatVectorProperty,
|
||||
FloatProperty,
|
||||
)
|
||||
|
||||
import mathutils
|
||||
import copy
|
||||
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
class MengerSponge(object):
|
||||
FACE_INDICES = [
|
||||
[3, 7, 4, 0],
|
||||
[5, 6, 2, 1],
|
||||
[1, 2, 3, 0],
|
||||
[7, 6, 5, 4],
|
||||
[4, 5, 1, 0],
|
||||
[2, 6, 7, 3],
|
||||
]
|
||||
|
||||
def __init__(self, level):
|
||||
self.__level = level
|
||||
self.__max_point_number = 3 ** level
|
||||
self.__vertices_map = {}
|
||||
self.__indices = []
|
||||
self.__face_visibility = {}
|
||||
self.__faces = []
|
||||
|
||||
for x in range(3):
|
||||
for y in range(3):
|
||||
for z in range(3):
|
||||
self.__face_visibility[(x, y, z)] = [
|
||||
x == 0 or x == 2 and (y == 1 or z == 1),
|
||||
x == 2 or x == 0 and (y == 1 or z == 1),
|
||||
y == 0 or y == 2 and (x == 1 or z == 1),
|
||||
y == 2 or y == 0 and (x == 1 or z == 1),
|
||||
z == 0 or z == 2 and (y == 1 or x == 1),
|
||||
z == 2 or z == 0 and (y == 1 or x == 1),
|
||||
]
|
||||
|
||||
def create(self, width, height):
|
||||
m = self.__max_point_number
|
||||
points = [
|
||||
(0, 0, 0),
|
||||
(m, 0, 0),
|
||||
(m, 0, m),
|
||||
(0, 0, m),
|
||||
(0, m, 0),
|
||||
(m, m, 0),
|
||||
(m, m, m),
|
||||
(0, m, m),
|
||||
]
|
||||
self.__make_sub_sponge(points, None, self.__level)
|
||||
vertices = self.__make_vertices(width, height)
|
||||
return vertices, self.__faces
|
||||
|
||||
def __get_vindex(self, p):
|
||||
if p in self.__vertices_map:
|
||||
return self.__vertices_map[p]
|
||||
index = len(self.__vertices_map)
|
||||
self.__vertices_map[p] = index
|
||||
return index
|
||||
|
||||
def __make_vertices(self, width, height):
|
||||
vertices = [None] * len(self.__vertices_map)
|
||||
w2 = width / 2
|
||||
h2 = height / 2
|
||||
w_step = width / self.__max_point_number
|
||||
h_step = height / self.__max_point_number
|
||||
for p, i in sorted(self.__vertices_map.items(), key=lambda x: x[1]):
|
||||
vertices[i] = mathutils.Vector([
|
||||
p[0] * w_step - w2,
|
||||
p[1] * w_step - w2,
|
||||
p[2] * h_step - h2,
|
||||
])
|
||||
return vertices
|
||||
|
||||
def __make_sub_sponge(self, cur_points, face_vis, depth):
|
||||
if depth <= 0:
|
||||
if not face_vis:
|
||||
face_vis = [True] * 6
|
||||
cur_point_indices = []
|
||||
for p in cur_points:
|
||||
cur_point_indices.append(self.__get_vindex(p))
|
||||
for i, vis in enumerate(face_vis):
|
||||
if vis:
|
||||
f = []
|
||||
for vi in self.FACE_INDICES[i]:
|
||||
f.append(cur_point_indices[vi])
|
||||
self.__faces.append(f)
|
||||
return
|
||||
|
||||
base = cur_points[0]
|
||||
width = (cur_points[1][0] - base[0]) / 3
|
||||
local_vert_map = {}
|
||||
for z in range(4):
|
||||
for y in range(4):
|
||||
for x in range(4):
|
||||
local_vert_map[(x, y, z)] = (
|
||||
width * x + base[0],
|
||||
width * y + base[1],
|
||||
width * z + base[2],
|
||||
)
|
||||
|
||||
for x in range(3):
|
||||
for y in range(3):
|
||||
for z in range(3):
|
||||
if [x, y, z].count(1) > 1:
|
||||
continue
|
||||
next_points = [
|
||||
local_vert_map[(x, y, z)],
|
||||
local_vert_map[(x + 1, y, z)],
|
||||
local_vert_map[(x + 1, y, z + 1)],
|
||||
local_vert_map[(x, y, z + 1)],
|
||||
local_vert_map[(x, y + 1, z)],
|
||||
local_vert_map[(x + 1, y + 1, z)],
|
||||
local_vert_map[(x + 1, y + 1, z + 1)],
|
||||
local_vert_map[(x, y + 1, z + 1)],
|
||||
]
|
||||
visibility = copy.copy(self.__face_visibility[(x, y, z)])
|
||||
if face_vis:
|
||||
visibility[0] = visibility[0] and (face_vis[0] or x != 0)
|
||||
visibility[1] = visibility[1] and (face_vis[1] or x != 2)
|
||||
visibility[2] = visibility[2] and (face_vis[2] or y != 0)
|
||||
visibility[3] = visibility[3] and (face_vis[3] or y != 2)
|
||||
visibility[4] = visibility[4] and (face_vis[4] or z != 0)
|
||||
visibility[5] = visibility[5] and (face_vis[5] or z != 2)
|
||||
self.__make_sub_sponge(
|
||||
next_points,
|
||||
visibility,
|
||||
depth - 1)
|
||||
|
||||
|
||||
class AddMengerSponge(bpy.types.Operator, AddObjectHelper):
|
||||
bl_idname = "mesh.menger_sponge_add"
|
||||
bl_label = "Menger Sponge"
|
||||
bl_description = "Construct a menger sponge mesh"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
level: IntProperty(
|
||||
name="Level",
|
||||
description="Sponge Level",
|
||||
min=0, max=4,
|
||||
default=1,
|
||||
)
|
||||
radius: FloatProperty(
|
||||
name="Width",
|
||||
description="Sponge Radius",
|
||||
min=0.01, max=100.0,
|
||||
default=1.0,
|
||||
)
|
||||
layers: BoolVectorProperty(
|
||||
name="Layers",
|
||||
size=20,
|
||||
subtype='LAYER',
|
||||
options={'HIDDEN', 'SKIP_SAVE'},
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.prop(self, 'level')
|
||||
layout.prop(self, 'radius')
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
sponger = MengerSponge(self.level)
|
||||
vertices, faces = sponger.create(self.radius * 2, self.radius * 2)
|
||||
del sponger
|
||||
|
||||
mesh = bpy.data.meshes.new(name='Sponge')
|
||||
mesh.from_pydata(vertices, [], faces)
|
||||
uvs = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)]
|
||||
mesh.uv_layers.new()
|
||||
for i, uvloop in enumerate(mesh.uv_layers.active.data):
|
||||
uvloop.uv = uvs[i % 4]
|
||||
|
||||
object_data_add(context, mesh, operator=self)
|
||||
|
||||
return {'FINISHED'}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,223 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: Phil Cote, cotejrp1, (http://www.blenderaddons.com)
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
)
|
||||
from math import pi
|
||||
from mathutils import (
|
||||
Quaternion,
|
||||
Vector,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
def create_step(width, base_level, step_height, num_sides):
|
||||
|
||||
axis = [0, 0, -1]
|
||||
PI2 = pi * 2
|
||||
rad = width / 2
|
||||
|
||||
quat_angles = [(cur_side / num_sides) * PI2
|
||||
for cur_side in range(num_sides)]
|
||||
|
||||
quaternions = [Quaternion(axis, quat_angle)
|
||||
for quat_angle in quat_angles]
|
||||
|
||||
init_vectors = [Vector([rad, 0, base_level])] * len(quaternions)
|
||||
|
||||
quat_vector_pairs = list(zip(quaternions, init_vectors))
|
||||
vectors = [quaternion @ vec for quaternion, vec in quat_vector_pairs]
|
||||
bottom_list = [(vec.x, vec.y, vec.z) for vec in vectors]
|
||||
top_list = [(vec.x, vec.y, vec.z + step_height) for vec in vectors]
|
||||
full_list = bottom_list + top_list
|
||||
|
||||
return full_list
|
||||
|
||||
|
||||
def split_list(l, n):
|
||||
"""
|
||||
split the blocks up. Credit to oremj for this one.
|
||||
http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
|
||||
"""
|
||||
n *= 2
|
||||
returned_list = [l[i: i + n] for i in range(0, len(l), n)]
|
||||
return returned_list
|
||||
|
||||
|
||||
def get_connector_pairs(lst, n_sides):
|
||||
# chop off the verts that get used for the base and top
|
||||
lst = lst[n_sides:]
|
||||
lst = lst[:-n_sides]
|
||||
lst = split_list(lst, n_sides)
|
||||
return lst
|
||||
|
||||
|
||||
def pyramid_mesh(self, context):
|
||||
all_verts = []
|
||||
|
||||
height_offset = 0
|
||||
cur_width = self.width
|
||||
|
||||
for i in range(self.num_steps):
|
||||
verts_loc = create_step(cur_width, height_offset, self.height,
|
||||
self.num_sides)
|
||||
height_offset += self.height
|
||||
cur_width -= self.reduce_by
|
||||
all_verts.extend(verts_loc)
|
||||
|
||||
mesh = bpy.data.meshes.new("Pyramid")
|
||||
bm = bmesh.new()
|
||||
|
||||
for v_co in all_verts:
|
||||
bm.verts.new(v_co)
|
||||
|
||||
def add_faces(n, block_vert_sets):
|
||||
for bvs in block_vert_sets:
|
||||
for i in range(self.num_sides - 1):
|
||||
bm.faces.new([bvs[i], bvs[i + n], bvs[i + n + 1], bvs[i + 1]])
|
||||
bm.faces.new([bvs[n - 1], bvs[(n * 2) - 1], bvs[n], bvs[0]])
|
||||
|
||||
# get the base and cap faces done.
|
||||
bm.faces.new(bm.verts[0:self.num_sides])
|
||||
bm.faces.new(reversed(bm.verts[-self.num_sides:])) # otherwise normal faces intern... T44619.
|
||||
|
||||
# side faces
|
||||
block_vert_sets = split_list(bm.verts, self.num_sides)
|
||||
add_faces(self.num_sides, block_vert_sets)
|
||||
|
||||
# connector faces between faces and faces of the block above it.
|
||||
connector_pairs = get_connector_pairs(bm.verts, self.num_sides)
|
||||
add_faces(self.num_sides, connector_pairs)
|
||||
|
||||
bm.to_mesh(mesh)
|
||||
mesh.update()
|
||||
|
||||
return mesh
|
||||
|
||||
|
||||
class AddPyramid(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_steppyramid_add"
|
||||
bl_label = "Pyramid"
|
||||
bl_description = "Construct a step pyramid mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Pyramid : BoolProperty(name = "Pyramid",
|
||||
default = True,
|
||||
description = "Pyramid")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Pyramid")
|
||||
|
||||
num_sides: IntProperty(
|
||||
name="Sides",
|
||||
description="How many sides each step will have",
|
||||
min=3,
|
||||
default=4
|
||||
)
|
||||
num_steps: IntProperty(
|
||||
name="Steps",
|
||||
description="How many steps for the overall pyramid",
|
||||
min=1,
|
||||
default=10
|
||||
)
|
||||
width: FloatProperty(
|
||||
name="Width",
|
||||
description="Initial base step width",
|
||||
min=0.01,
|
||||
default=2
|
||||
)
|
||||
height: FloatProperty(
|
||||
name="Height",
|
||||
description="How tall each step will be",
|
||||
min=0.01,
|
||||
default=0.1
|
||||
)
|
||||
reduce_by: FloatProperty(
|
||||
name="Taper",
|
||||
description="How much to reduce each succeeding step by",
|
||||
min=.01,
|
||||
default=.20
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'num_sides')
|
||||
layout.prop(self, 'num_steps')
|
||||
layout.prop(self, 'width')
|
||||
layout.prop(self, 'height')
|
||||
layout.prop(self, 'reduce_by')
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Pyramid' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
obj.data = pyramid_mesh(self, context)
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
mesh = pyramid_mesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["Pyramid"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in PyramidParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
mesh = pyramid_mesh(self, context)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def PyramidParameters():
|
||||
PyramidParameters = [
|
||||
"num_sides",
|
||||
"num_steps",
|
||||
"width",
|
||||
"height",
|
||||
"reduce_by",
|
||||
]
|
||||
return PyramidParameters
|
||||
@@ -0,0 +1,36 @@
|
||||
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Paul "BrikBot" Marshall
|
||||
# Created: July 1, 2011
|
||||
# Last Modified: September 26, 2013
|
||||
# Homepage (blog): http://post.darkarsenic.com/
|
||||
# //blog.darkarsenic.com/
|
||||
# Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas
|
||||
# and testing.
|
||||
#
|
||||
# Coded in IDLE, tested in Blender 2.68a. NumPy Recommended.
|
||||
# Search for "@todo" to quickly find sections that need work.
|
||||
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
importlib.reload(rockgen)
|
||||
|
||||
else:
|
||||
from . import rockgen
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
# Register:
|
||||
def register():
|
||||
rockgen.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
rockgen.unregister()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
@@ -0,0 +1,403 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE settings [
|
||||
<!ELEMENT settings (default,preset*)>
|
||||
<!ELEMENT default (title,size,shape,material,random)>
|
||||
<!ELEMENT preset (title,size,shape,material,random)>
|
||||
<!ELEMENT title (#PCDATA)>
|
||||
<!ELEMENT size (scale+,skew+,use_scale_dis,scale_fac)>
|
||||
<!ELEMENT scale (axis,lower,upper)>
|
||||
<!ELEMENT axis (#PCDATA)>
|
||||
<!ELEMENT lower (#PCDATA)>
|
||||
<!ELEMENT upper (#PCDATA)>
|
||||
<!ELEMENT skew (axis,value)>
|
||||
<!ELEMENT value (#PCDATA)>
|
||||
<!ELEMENT use_scale_dis (#PCDATA)>
|
||||
<!ELEMENT scale_fac (#PCDATA)>
|
||||
<!ELEMENT shape (deform,rough,detail,display_detail,smooth_fac,smooth_it)>
|
||||
<!ELEMENT deform (#PCDATA)>
|
||||
<!ELEMENT rough (#PCDATA)>
|
||||
<!ELEMENT detail (#PCDATA)>
|
||||
<!ELEMENT display_detail (#PCDATA)>
|
||||
<!ELEMENT smooth_fac (#PCDATA)>
|
||||
<!ELEMENT smooth_it (#PCDATA)>
|
||||
<!ELEMENT material (mat_enable,mat_color,mat_bright,mat_rough,mat_spec,mat_hard,mat_use_trans,mat_alpha,mat_cloudy,mat_IOR,mat_mossy)>
|
||||
<!ELEMENT mat_enable (#PCDATA)>
|
||||
<!ELEMENT mat_color (#PCDATA)>
|
||||
<!ELEMENT mat_bright (#PCDATA)>
|
||||
<!ELEMENT mat_rough (#PCDATA)>
|
||||
<!ELEMENT mat_spec (#PCDATA)>
|
||||
<!ELEMENT mat_hard (#PCDATA)>
|
||||
<!ELEMENT mat_use_trans (#PCDATA)>
|
||||
<!ELEMENT mat_alpha (#PCDATA)>
|
||||
<!ELEMENT mat_cloudy (#PCDATA)>
|
||||
<!ELEMENT mat_IOR (#PCDATA)>
|
||||
<!ELEMENT mat_mossy (#PCDATA)>
|
||||
<!ELEMENT random (use_random_seed,user_seed)>
|
||||
<!ELEMENT use_generate (#PCDATA)>
|
||||
<!ELEMENT use_random_seed (#PCDATA)>
|
||||
<!ELEMENT user_seed (#PCDATA)>
|
||||
|
||||
<!ATTLIST preset id ID #REQUIRED>
|
||||
]>
|
||||
<settings>
|
||||
<default>
|
||||
<title>Default</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>5.0</deform>
|
||||
<rough>2.5</rough>
|
||||
<detail>3</detail>
|
||||
<display_detail>2</display_detail>
|
||||
<smooth_fac>0.0</smooth_fac>
|
||||
<smooth_it>0</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>False</mat_enable>
|
||||
<mat_color>[0.5, 0.5, 0.5]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>1.0</mat_rough>
|
||||
<mat_spec>0.2</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</default>
|
||||
<preset id="1">
|
||||
<title>River Rock</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>0.5</lower>
|
||||
<upper>1.25</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>0.5</lower>
|
||||
<upper>1.25</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>0.5</lower>
|
||||
<upper>1.25</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>-0.5</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>-0.5</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>-0.5</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>3.0</deform>
|
||||
<rough>2.0</rough>
|
||||
<detail>2</detail>
|
||||
<display_detail>2</display_detail>
|
||||
<smooth_fac>2.0</smooth_fac>
|
||||
<smooth_it>2</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.5, 0.5, 0.5]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>0.125</mat_rough>
|
||||
<mat_spec>0.5</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="2">
|
||||
<title>Asteroid</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>5.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>5.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>5.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>7.5</deform>
|
||||
<rough>3.0</rough>
|
||||
<detail>4</detail>
|
||||
<display_detail>3</display_detail>
|
||||
<smooth_fac>0.0</smooth_fac>
|
||||
<smooth_it>0</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.3, 0.25, 0.2]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>1.5</mat_rough>
|
||||
<mat_spec>0.25</mat_spec>
|
||||
<mat_hard>30</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="3">
|
||||
<title>Sandstone</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>True</use_scale_dis>
|
||||
<scale_fac>[5.0, 5.0, 0.1]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>0.5</deform>
|
||||
<rough>1.0</rough>
|
||||
<detail>3</detail>
|
||||
<display_detail>3</display_detail>
|
||||
<smooth_fac>2.0</smooth_fac>
|
||||
<smooth_it>2</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.5, 0.4, 0.35]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>0.1</mat_rough>
|
||||
<mat_spec>0.2</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="4">
|
||||
<title>Ice</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>2.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>2.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>2.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>5.0</deform>
|
||||
<rough>1.0</rough>
|
||||
<detail>3</detail>
|
||||
<display_detail>2</display_detail>
|
||||
<smooth_fac>2.0</smooth_fac>
|
||||
<smooth_it>1</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.9, 0.95, 1.0]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>0.25</mat_rough>
|
||||
<mat_spec>0.2</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>True</mat_use_trans>
|
||||
<mat_alpha>0.9</mat_alpha>
|
||||
<mat_cloudy>0.1</mat_cloudy>
|
||||
<mat_IOR>1.31</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="5">
|
||||
<title>Fake Ocean</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>10.0</lower>
|
||||
<upper>10.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>10.0</lower>
|
||||
<upper>10.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>0.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>7.5</deform>
|
||||
<rough>3.0</rough>
|
||||
<detail>4</detail>
|
||||
<display_detail>3</display_detail>
|
||||
<smooth_fac>0.0</smooth_fac>
|
||||
<smooth_it>0</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.1, 0.12, 0.125]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>1.5</mat_rough>
|
||||
<mat_spec>0.25</mat_spec>
|
||||
<mat_hard>30</mat_hard>
|
||||
<mat_use_trans>True</mat_use_trans>
|
||||
<mat_alpha>0.5</mat_alpha>
|
||||
<mat_cloudy>0.5</mat_cloudy>
|
||||
<mat_IOR>1.333</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
</settings>
|
||||
@@ -0,0 +1,403 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE settings [
|
||||
<!ELEMENT settings (default,preset*)>
|
||||
<!ELEMENT default (title,size,shape,material,random)>
|
||||
<!ELEMENT preset (title,size,shape,material,random)>
|
||||
<!ELEMENT title (#PCDATA)>
|
||||
<!ELEMENT size (scale+,skew+,use_scale_dis,scale_fac)>
|
||||
<!ELEMENT scale (axis,lower,upper)>
|
||||
<!ELEMENT axis (#PCDATA)>
|
||||
<!ELEMENT lower (#PCDATA)>
|
||||
<!ELEMENT upper (#PCDATA)>
|
||||
<!ELEMENT skew (axis,value)>
|
||||
<!ELEMENT value (#PCDATA)>
|
||||
<!ELEMENT use_scale_dis (#PCDATA)>
|
||||
<!ELEMENT scale_fac (#PCDATA)>
|
||||
<!ELEMENT shape (deform,rough,detail,display_detail,smooth_fac,smooth_it)>
|
||||
<!ELEMENT deform (#PCDATA)>
|
||||
<!ELEMENT rough (#PCDATA)>
|
||||
<!ELEMENT detail (#PCDATA)>
|
||||
<!ELEMENT display_detail (#PCDATA)>
|
||||
<!ELEMENT smooth_fac (#PCDATA)>
|
||||
<!ELEMENT smooth_it (#PCDATA)>
|
||||
<!ELEMENT material (mat_enable,mat_color,mat_bright,mat_rough,mat_spec,mat_hard,mat_use_trans,mat_alpha,mat_cloudy,mat_IOR,mat_mossy)>
|
||||
<!ELEMENT mat_enable (#PCDATA)>
|
||||
<!ELEMENT mat_color (#PCDATA)>
|
||||
<!ELEMENT mat_bright (#PCDATA)>
|
||||
<!ELEMENT mat_rough (#PCDATA)>
|
||||
<!ELEMENT mat_spec (#PCDATA)>
|
||||
<!ELEMENT mat_hard (#PCDATA)>
|
||||
<!ELEMENT mat_use_trans (#PCDATA)>
|
||||
<!ELEMENT mat_alpha (#PCDATA)>
|
||||
<!ELEMENT mat_cloudy (#PCDATA)>
|
||||
<!ELEMENT mat_IOR (#PCDATA)>
|
||||
<!ELEMENT mat_mossy (#PCDATA)>
|
||||
<!ELEMENT random (use_random_seed,user_seed)>
|
||||
<!ELEMENT use_generate (#PCDATA)>
|
||||
<!ELEMENT use_random_seed (#PCDATA)>
|
||||
<!ELEMENT user_seed (#PCDATA)>
|
||||
|
||||
<!ATTLIST preset id ID #REQUIRED>
|
||||
]>
|
||||
<settings>
|
||||
<default>
|
||||
<title>Default</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>5.0</deform>
|
||||
<rough>2.5</rough>
|
||||
<detail>3</detail>
|
||||
<display_detail>2</display_detail>
|
||||
<smooth_fac>0.0</smooth_fac>
|
||||
<smooth_it>0</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>False</mat_enable>
|
||||
<mat_color>[0.5, 0.5, 0.5]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>1.0</mat_rough>
|
||||
<mat_spec>0.2</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</default>
|
||||
<preset id="1">
|
||||
<title>River Rock</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>0.5</lower>
|
||||
<upper>1.25</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>0.5</lower>
|
||||
<upper>1.25</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>0.5</lower>
|
||||
<upper>1.25</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>-0.5</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>-0.5</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>-0.5</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>3.0</deform>
|
||||
<rough>2.0</rough>
|
||||
<detail>2</detail>
|
||||
<display_detail>2</display_detail>
|
||||
<smooth_fac>2.0</smooth_fac>
|
||||
<smooth_it>2</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.5, 0.5, 0.5]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>0.125</mat_rough>
|
||||
<mat_spec>0.5</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="2">
|
||||
<title>Asteroid</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>5.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>5.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>5.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>7.5</deform>
|
||||
<rough>3.0</rough>
|
||||
<detail>4</detail>
|
||||
<display_detail>3</display_detail>
|
||||
<smooth_fac>0.0</smooth_fac>
|
||||
<smooth_it>0</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.3, 0.25, 0.2]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>1.5</mat_rough>
|
||||
<mat_spec>0.25</mat_spec>
|
||||
<mat_hard>30</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="3">
|
||||
<title>Sandstone</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>1.0</lower>
|
||||
<upper>1.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>True</use_scale_dis>
|
||||
<scale_fac>[5.0, 5.0, 0.1]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>0.5</deform>
|
||||
<rough>1.0</rough>
|
||||
<detail>3</detail>
|
||||
<display_detail>3</display_detail>
|
||||
<smooth_fac>2.0</smooth_fac>
|
||||
<smooth_it>2</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.5, 0.4, 0.35]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>0.1</mat_rough>
|
||||
<mat_spec>0.2</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>False</mat_use_trans>
|
||||
<mat_alpha>0.0</mat_alpha>
|
||||
<mat_cloudy>0.0</mat_cloudy>
|
||||
<mat_IOR>1.0</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="4">
|
||||
<title>Ice</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>2.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>2.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>2.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>5.0</deform>
|
||||
<rough>1.0</rough>
|
||||
<detail>3</detail>
|
||||
<display_detail>2</display_detail>
|
||||
<smooth_fac>2.0</smooth_fac>
|
||||
<smooth_it>1</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.9, 0.95, 1.0]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>0.25</mat_rough>
|
||||
<mat_spec>0.2</mat_spec>
|
||||
<mat_hard>50</mat_hard>
|
||||
<mat_use_trans>True</mat_use_trans>
|
||||
<mat_alpha>0.9</mat_alpha>
|
||||
<mat_cloudy>0.1</mat_cloudy>
|
||||
<mat_IOR>1.31</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
<preset id="5">
|
||||
<title>Fake Ocean</title>
|
||||
<size>
|
||||
<scale>
|
||||
<axis>X</axis>
|
||||
<lower>10.0</lower>
|
||||
<upper>10.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Y</axis>
|
||||
<lower>10.0</lower>
|
||||
<upper>10.0</upper>
|
||||
</scale>
|
||||
<scale>
|
||||
<axis>Z</axis>
|
||||
<lower>0.0</lower>
|
||||
<upper>0.0</upper>
|
||||
</scale>
|
||||
<skew>
|
||||
<axis>X</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Y</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<skew>
|
||||
<axis>Z</axis>
|
||||
<value>0.0</value>
|
||||
</skew>
|
||||
<use_scale_dis>False</use_scale_dis>
|
||||
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
|
||||
</size>
|
||||
<shape>
|
||||
<deform>7.5</deform>
|
||||
<rough>3.0</rough>
|
||||
<detail>4</detail>
|
||||
<display_detail>3</display_detail>
|
||||
<smooth_fac>0.0</smooth_fac>
|
||||
<smooth_it>0</smooth_it>
|
||||
</shape>
|
||||
<material>
|
||||
<mat_enable>True</mat_enable>
|
||||
<mat_color>[0.1, 0.12, 0.125]</mat_color>
|
||||
<mat_bright>0.85</mat_bright>
|
||||
<mat_rough>1.5</mat_rough>
|
||||
<mat_spec>0.25</mat_spec>
|
||||
<mat_hard>30</mat_hard>
|
||||
<mat_use_trans>True</mat_use_trans>
|
||||
<mat_alpha>0.5</mat_alpha>
|
||||
<mat_cloudy>0.5</mat_cloudy>
|
||||
<mat_IOR>1.333</mat_IOR>
|
||||
<mat_mossy>0.0</mat_mossy>
|
||||
</material>
|
||||
<random>
|
||||
<use_generate>True</use_generate>
|
||||
<use_random_seed>True</use_random_seed>
|
||||
<user_seed>1</user_seed>
|
||||
</random>
|
||||
</preset>
|
||||
</settings>
|
||||
@@ -0,0 +1,161 @@
|
||||
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# This try block allows for the script to psudo-intelligently select the
|
||||
# appropriate random to use. If Numpy's random is present it will use that.
|
||||
# If Numpy's random is not present, it will through a "module not found"
|
||||
# exception and instead use the slower built-in random that Python has.
|
||||
try:
|
||||
from numpy.random import random_integers as randint
|
||||
from numpy.random import normal as gauss
|
||||
from numpy.random import (
|
||||
beta,
|
||||
uniform,
|
||||
)
|
||||
except:
|
||||
from random import (
|
||||
randint,
|
||||
gauss,
|
||||
uniform,
|
||||
)
|
||||
from random import betavariate as beta
|
||||
|
||||
from .utils import skewedGauss
|
||||
|
||||
|
||||
def randomizeTexture(texture, level=1):
|
||||
'''
|
||||
Set the values for a texture from parameters.
|
||||
|
||||
param: texture - bpy.data.texture to modify.
|
||||
level - designated tweaked settings to use
|
||||
-> Below 10 is a displacement texture
|
||||
-> Between 10 and 20 is a base material texture
|
||||
'''
|
||||
noises = ['BLENDER_ORIGINAL', 'ORIGINAL_PERLIN', 'IMPROVED_PERLIN',
|
||||
'VORONOI_F1', 'VORONOI_F2', 'VORONOI_F3', 'VORONOI_F4',
|
||||
'VORONOI_F2_F1', 'VORONOI_CRACKLE']
|
||||
if texture.type == 'CLOUDS':
|
||||
if randint(0, 1) == 0:
|
||||
texture.noise_type = 'SOFT_NOISE'
|
||||
else:
|
||||
texture.noise_type = 'HARD_NOISE'
|
||||
if level != 11:
|
||||
tempInt = randint(0, 6)
|
||||
else:
|
||||
tempInt = randint(0, 8)
|
||||
texture.noise_basis = noises[tempInt]
|
||||
texture.noise_depth = 8
|
||||
|
||||
if level == 0:
|
||||
texture.noise_scale = gauss(0.625, 1 / 24)
|
||||
elif level == 2:
|
||||
texture.noise_scale = 0.15
|
||||
elif level == 11:
|
||||
texture.noise_scale = gauss(0.5, 1 / 24)
|
||||
|
||||
if texture.noise_basis in ['BLENDER_ORIGINAL', 'ORIGINAL_PERLIN',
|
||||
'IMPROVED_PERLIN', 'VORONOI_F1']:
|
||||
texture.intensity = gauss(1, 1 / 6)
|
||||
texture.contrast = gauss(4, 1 / 3)
|
||||
elif texture.noise_basis in ['VORONOI_F2', 'VORONOI_F3', 'VORONOI_F4']:
|
||||
texture.intensity = gauss(0.25, 1 / 12)
|
||||
texture.contrast = gauss(2, 1 / 6)
|
||||
elif texture.noise_basis == 'VORONOI_F2_F1':
|
||||
texture.intensity = gauss(0.5, 1 / 6)
|
||||
texture.contrast = gauss(2, 1 / 6)
|
||||
elif texture.noise_basis == 'VORONOI_CRACKLE':
|
||||
texture.intensity = gauss(0.5, 1 / 6)
|
||||
texture.contrast = gauss(2, 1 / 6)
|
||||
elif texture.type == 'MUSGRAVE':
|
||||
# musgraveType = ['MULTIFRACTAL', 'RIDGED_MULTIFRACTAL',
|
||||
# 'HYBRID_MULTIFRACTAL', 'FBM', 'HETERO_TERRAIN']
|
||||
texture.musgrave_type = 'MULTIFRACTAL'
|
||||
texture.dimension_max = abs(gauss(0, 0.6)) + 0.2
|
||||
texture.lacunarity = beta(3, 8) * 8.2 + 1.8
|
||||
|
||||
if level == 0:
|
||||
texture.noise_scale = gauss(0.625, 1 / 24)
|
||||
texture.noise_intensity = 0.2
|
||||
texture.octaves = 1.0
|
||||
elif level == 2:
|
||||
texture.intensity = gauss(1, 1 / 6)
|
||||
texture.contrast = 0.2
|
||||
texture.noise_scale = 0.15
|
||||
texture.octaves = 8.0
|
||||
elif level == 10:
|
||||
texture.intensity = gauss(0.25, 1 / 12)
|
||||
texture.contrast = gauss(1.5, 1 / 6)
|
||||
texture.noise_scale = 0.5
|
||||
texture.octaves = 8.0
|
||||
elif level == 12:
|
||||
texture.octaves = uniform(1, 3)
|
||||
elif level > 12:
|
||||
texture.octaves = uniform(2, 8)
|
||||
else:
|
||||
texture.intensity = gauss(1, 1 / 6)
|
||||
texture.contrast = 0.2
|
||||
texture.octaves = 8.0
|
||||
elif texture.type == 'DISTORTED_NOISE':
|
||||
tempInt = randint(0, 8)
|
||||
texture.noise_distortion = noises[tempInt]
|
||||
tempInt = randint(0, 8)
|
||||
texture.noise_basis = noises[tempInt]
|
||||
texture.distortion = skewedGauss(2.0, 2.6666, (0.0, 10.0), False)
|
||||
|
||||
if level == 0:
|
||||
texture.noise_scale = gauss(0.625, 1 / 24)
|
||||
elif level == 2:
|
||||
texture.noise_scale = 0.15
|
||||
elif level >= 12:
|
||||
texture.noise_scale = gauss(0.2, 1 / 48)
|
||||
elif texture.type == 'STUCCI':
|
||||
stucciTypes = ['PLASTIC', 'WALL_IN', 'WALL_OUT']
|
||||
if randint(0, 1) == 0:
|
||||
texture.noise_type = 'SOFT_NOISE'
|
||||
else:
|
||||
texture.noise_type = 'HARD_NOISE'
|
||||
tempInt = randint(0, 2)
|
||||
texture.stucci_type = stucciTypes[tempInt]
|
||||
|
||||
if level == 0:
|
||||
tempInt = randint(0, 6)
|
||||
texture.noise_basis = noises[tempInt]
|
||||
texture.noise_scale = gauss(0.625, 1 / 24)
|
||||
elif level == 2:
|
||||
tempInt = randint(0, 6)
|
||||
texture.noise_basis = noises[tempInt]
|
||||
texture.noise_scale = 0.15
|
||||
elif level >= 12:
|
||||
tempInt = randint(0, 6)
|
||||
texture.noise_basis = noises[tempInt]
|
||||
texture.noise_scale = gauss(0.2, 1 / 30)
|
||||
else:
|
||||
tempInt = randint(0, 6)
|
||||
texture.noise_basis = noises[tempInt]
|
||||
elif texture.type == 'VORONOI':
|
||||
metrics = ['DISTANCE', 'DISTANCE_SQUARED', 'MANHATTAN', 'CHEBYCHEV',
|
||||
'MINKOVSKY_HALF', 'MINKOVSKY_FOUR', 'MINKOVSKY']
|
||||
# Settings for first displacement level:
|
||||
if level == 0:
|
||||
tempInt = randint(0, 1)
|
||||
texture.distance_metric = metrics[tempInt]
|
||||
texture.noise_scale = gauss(0.625, 1 / 24)
|
||||
texture.contrast = 0.5
|
||||
texture.intensity = 0.7
|
||||
elif level == 2:
|
||||
texture.noise_scale = 0.15
|
||||
tempInt = randint(0, 6)
|
||||
texture.distance_metric = metrics[tempInt]
|
||||
elif level >= 12:
|
||||
tempInt = randint(0, 1)
|
||||
texture.distance_metric = metrics[tempInt]
|
||||
texture.noise_scale = gauss(0.125, 1 / 48)
|
||||
texture.contrast = 0.5
|
||||
texture.intensity = 0.7
|
||||
else:
|
||||
tempInt = randint(0, 6)
|
||||
texture.distance_metric = metrics[tempInt]
|
||||
|
||||
return
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,144 @@
|
||||
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Paul "BrikBot" Marshall
|
||||
# Created: July 1, 2011
|
||||
# Last Modified: November 17, 2011
|
||||
# Homepage (blog): http://post.darkarsenic.com/
|
||||
# //blog.darkarsenic.com/
|
||||
# Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas
|
||||
# and testing.
|
||||
#
|
||||
# Coded in IDLE, tested in Blender 2.59. NumPy Recommended.
|
||||
# Search for "@todo" to quickly find sections that need work.
|
||||
|
||||
import inspect
|
||||
import shutil
|
||||
from . import utils
|
||||
from xml.dom import minidom
|
||||
|
||||
basePath = inspect.getfile(inspect.currentframe())[0:-len("settings.py")]
|
||||
path = basePath + "add_mesh_rocks.xml"
|
||||
|
||||
try:
|
||||
source = minidom.parse(path)
|
||||
# print("Rock generator settings file found:\n" + path)
|
||||
except:
|
||||
print("Rock generator settings file not found. Creating settings file.")
|
||||
shutil.copy(basePath + "factory.xml", path)
|
||||
source = minidom.parse(path)
|
||||
|
||||
xmlDefault = source.getElementsByTagName('default')[0]
|
||||
xmlPresets = source.getElementsByTagName('preset')
|
||||
default = []
|
||||
presets = []
|
||||
|
||||
# ----- Gets and Sets -----#
|
||||
|
||||
|
||||
def getDefault():
|
||||
global default
|
||||
return default
|
||||
|
||||
|
||||
def getPresetLists():
|
||||
global presets
|
||||
return presets
|
||||
|
||||
|
||||
def getPreset(ID=0):
|
||||
global presets
|
||||
return presets[ID]
|
||||
|
||||
# ---------- Core ----------#
|
||||
|
||||
|
||||
def parse():
|
||||
global xmlDefault
|
||||
global xmlPresets
|
||||
global default
|
||||
global presets
|
||||
|
||||
# Parse default values
|
||||
default = parseNode(xmlDefault)
|
||||
|
||||
# Parse preset values
|
||||
for setting in xmlPresets:
|
||||
presets.append(parseNode(setting))
|
||||
|
||||
return '{FINISHED}'
|
||||
|
||||
|
||||
# Takes a node and parses it for data. Relies on that setting.xml has
|
||||
# a valid format as specified by the DTD.
|
||||
# For some reason minidom places an empty child node for every other node.
|
||||
def parseNode(setting, title=True):
|
||||
loc = 1
|
||||
|
||||
if title:
|
||||
# Preset name (xmlPreset.childNodes[1]):
|
||||
title = setting.childNodes[loc].childNodes[0].data
|
||||
loc += 2
|
||||
|
||||
# Preset size values (xmlPreset.childNodes[3]):
|
||||
scaleX = [float(setting.childNodes[loc].childNodes[1].childNodes[3].childNodes[0].data),
|
||||
float(setting.childNodes[loc].childNodes[1].childNodes[5].childNodes[0].data)]
|
||||
scaleY = [float(setting.childNodes[loc].childNodes[3].childNodes[3].childNodes[0].data),
|
||||
float(setting.childNodes[loc].childNodes[3].childNodes[5].childNodes[0].data)]
|
||||
scaleZ = [float(setting.childNodes[loc].childNodes[5].childNodes[3].childNodes[0].data),
|
||||
float(setting.childNodes[loc].childNodes[5].childNodes[5].childNodes[0].data)]
|
||||
skewX = float(setting.childNodes[loc].childNodes[7].childNodes[3].childNodes[0].data)
|
||||
skewY = float(setting.childNodes[loc].childNodes[9].childNodes[3].childNodes[0].data)
|
||||
skewZ = float(setting.childNodes[loc].childNodes[11].childNodes[3].childNodes[0].data)
|
||||
if setting.childNodes[loc].childNodes[13].childNodes[0].data == 'False':
|
||||
use_scale_dis = False
|
||||
else:
|
||||
use_scale_dis = True
|
||||
scale_fac = utils.toList(setting.childNodes[loc].childNodes[15].childNodes[0].data)
|
||||
loc += 2
|
||||
|
||||
# Presst shape values (xmlPreset.childNodes[5]):
|
||||
deform = float(setting.childNodes[loc].childNodes[1].childNodes[0].data)
|
||||
rough = float(setting.childNodes[loc].childNodes[3].childNodes[0].data)
|
||||
detail = int(setting.childNodes[loc].childNodes[5].childNodes[0].data)
|
||||
display_detail = int(setting.childNodes[loc].childNodes[7].childNodes[0].data)
|
||||
smooth_fac = float(setting.childNodes[loc].childNodes[9].childNodes[0].data)
|
||||
smooth_it = int(setting.childNodes[loc].childNodes[11].childNodes[0].data)
|
||||
loc += 2
|
||||
|
||||
# Preset material values (xmlPreset.childNodes[7]):
|
||||
loc += 2
|
||||
|
||||
# Preset random values (xmlPreset.childNodes[9]):
|
||||
if setting.childNodes[loc].childNodes[1].childNodes[0].data == 'True':
|
||||
use_generate = True
|
||||
else:
|
||||
use_generate = False
|
||||
if setting.childNodes[loc].childNodes[3].childNodes[0].data == 'False':
|
||||
use_random_seed = False
|
||||
else:
|
||||
use_random_seed = True
|
||||
user_seed = int(setting.childNodes[loc].childNodes[5].childNodes[0].data)
|
||||
|
||||
if title:
|
||||
parsed = [title, scaleX, scaleY, scaleZ, skewX, skewY, skewZ,
|
||||
use_scale_dis, scale_fac, deform, rough, detail,
|
||||
display_detail, smooth_fac, smooth_it,
|
||||
use_generate, use_random_seed, user_seed]
|
||||
else:
|
||||
parsed = [scaleX, scaleY, scaleZ, skewX, skewY, skewZ, use_scale_dis,
|
||||
scale_fac, deform, rough, detail, display_detail, smooth_fac,
|
||||
smooth_it, use_generate, use_random_seed, user_seed]
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
def save():
|
||||
return '{FINISHED}'
|
||||
|
||||
|
||||
def _print():
|
||||
for i in presets:
|
||||
print(i)
|
||||
return '{FINISHED}'
|
||||
@@ -0,0 +1,141 @@
|
||||
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Converts a formatted string to a float tuple:
|
||||
# IN - '(0.5, 0.2)' -> CONVERT -> OUT - (0.5, 0.2)
|
||||
def toTuple(stringIn):
|
||||
sTemp = str(stringIn)[1:len(str(stringIn)) - 1].split(', ')
|
||||
fTemp = []
|
||||
for i in sTemp:
|
||||
fTemp.append(float(i))
|
||||
return tuple(fTemp)
|
||||
|
||||
|
||||
# Converts a formatted string to a float tuple:
|
||||
# IN - '[0.5, 0.2]' -> CONVERT -> OUT - [0.5, 0.2]
|
||||
def toList(stringIn):
|
||||
sTemp = str(stringIn)[1:len(str(stringIn)) - 1].split(', ')
|
||||
fTemp = []
|
||||
for i in sTemp:
|
||||
fTemp.append(float(i))
|
||||
return fTemp
|
||||
|
||||
|
||||
# Converts each item of a list into a float:
|
||||
def toFloats(inList):
|
||||
outList = []
|
||||
for i in inList:
|
||||
outList.append(float(i))
|
||||
return outList
|
||||
|
||||
|
||||
# Converts each item of a list into an integer:
|
||||
def toInts(inList):
|
||||
outList = []
|
||||
for i in inList:
|
||||
outList.append(int(i))
|
||||
return outList
|
||||
|
||||
|
||||
# Sets all faces smooth. Done this way since I can't
|
||||
# find a simple way without using bpy.ops:
|
||||
def smooth(mesh):
|
||||
import bmesh
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
for f in bm.faces:
|
||||
f.smooth = True
|
||||
bm.to_mesh(mesh)
|
||||
return mesh
|
||||
|
||||
|
||||
# This try block allows for the script to psudo-intelligently select the
|
||||
# appropriate random to use. If Numpy's random is present it will use that.
|
||||
# If Numpy's random is not present, it will through a "module not found"
|
||||
# exception and instead use the slower built-in random that Python has.
|
||||
try:
|
||||
# from numpy.random import random_integers as randint
|
||||
from numpy.random import normal as gauss
|
||||
# from numpy.random import (beta,
|
||||
# uniform,
|
||||
# seed,
|
||||
# weibull)
|
||||
# print("Rock Generator: Numpy found.")
|
||||
numpy = True
|
||||
except:
|
||||
from random import (
|
||||
# randint,
|
||||
gauss,
|
||||
# uniform,
|
||||
# seed
|
||||
)
|
||||
# from random import betavariate as beta
|
||||
# from random import weibullvariate as weibull
|
||||
print("Rock Generator: Numpy not found. Using Python's random.")
|
||||
numpy = False
|
||||
# Artificially skews a normal (gaussian) distribution. This will not create
|
||||
# a continuous distribution curve but instead acts as a piecewise finction.
|
||||
# This linearly scales the output on one side to fit the bounds.
|
||||
#
|
||||
# Example output histograms:
|
||||
#
|
||||
# Upper skewed: Lower skewed:
|
||||
# | ▄ | _
|
||||
# | █ | █
|
||||
# | █_ | █
|
||||
# | ██ | _█
|
||||
# | _██ | ██
|
||||
# | _▄███_ | ██ _
|
||||
# | ▄██████ | ▄██▄█▄_
|
||||
# | _█▄███████ | ███████
|
||||
# | _██████████_ | ████████▄▄█_ _
|
||||
# | _▄▄████████████ | ████████████▄█_
|
||||
# | _▄_ ▄███████████████▄_ | _▄███████████████▄▄_
|
||||
# ------------------------- -----------------------
|
||||
# |mu |mu
|
||||
# Histograms were generated in R (http://www.r-project.org/) based on the
|
||||
# calculations below and manually duplicated here.
|
||||
#
|
||||
# param: mu - mu is the mean of the distribution.
|
||||
# sigma - sigma is the standard deviation of the distribution.
|
||||
# bounds - bounds[0] is the lower bound and bounds[1]
|
||||
# is the upper bound.
|
||||
# upperSkewed - if the distribution is upper skewed.
|
||||
# return: out - Rondomly generated value from the skewed distribution.
|
||||
#
|
||||
# @todo: Because NumPy's random value generators are faster when called
|
||||
# a bunch of times at once, maybe allow this to generate and return
|
||||
# multiple values at once?
|
||||
|
||||
|
||||
def skewedGauss(mu, sigma, bounds, upperSkewed=True):
|
||||
raw = gauss(mu, sigma)
|
||||
|
||||
# Quicker to check an extra condition than do unnecessary math. . . .
|
||||
if raw < mu and not upperSkewed:
|
||||
out = ((mu - bounds[0]) / (3 * sigma)) * raw + ((mu * (bounds[0] - (mu - 3 * sigma))) / (3 * sigma))
|
||||
elif raw > mu and upperSkewed:
|
||||
out = ((mu - bounds[1]) / (3 * -sigma)) * raw + ((mu * (bounds[1] - (mu + 3 * sigma))) / (3 * -sigma))
|
||||
else:
|
||||
out = raw
|
||||
|
||||
return out
|
||||
|
||||
|
||||
# @todo create a def for generating an alpha and beta for a beta distribution
|
||||
# given a mu, sigma, and an upper and lower bound. This proved faster in
|
||||
# profiling in addition to providing a much better distribution curve
|
||||
# provided multiple iterations happen within this function; otherwise it was
|
||||
# slower.
|
||||
# This might be a scratch because of the bounds placed on mu and sigma:
|
||||
#
|
||||
# For alpha > 1 and beta > 1:
|
||||
# mu^2 - mu^3 mu^3 - mu^2 + mu
|
||||
# ----------- < sigma < ----------------
|
||||
# 1 + mu 2 - mu
|
||||
#
|
||||
# def generateBeta(mu, sigma, scale, repitions=1):
|
||||
# results = []
|
||||
#
|
||||
# return results
|
||||
@@ -0,0 +1,500 @@
|
||||
# SPDX-FileCopyrightText: 2015-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from math import (
|
||||
pi, sin,
|
||||
cos, tan,
|
||||
)
|
||||
from bpy.types import Operator
|
||||
from mathutils import (
|
||||
Vector,
|
||||
Euler,
|
||||
)
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
BoolProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
# mesh generating function, returns mesh
|
||||
def add_mesh_Brilliant(context, s, table_w, crown_h, girdle_t, pavi_d, bezel_f,
|
||||
pavi_f, culet, girdle_real, keep_lga, g_real_smooth):
|
||||
|
||||
# # possible user inputs ( output 100% = 2 blender units )
|
||||
# s # no. of girdle facets (steps) default: 16
|
||||
# table_w # table width default: 0.530
|
||||
# crown_h # crown height default: 0.162
|
||||
# girdle_t # girdle thickness default: 0.017
|
||||
# pavi_d # pavilion depth default: 0.431
|
||||
# bezel_f # bezel factor default: 0.250
|
||||
# pavi_f # pavilion factor default: 0.400
|
||||
# culet # culet size default: 0.000
|
||||
# girdle_real # type of girdle flat/real default: True
|
||||
# g_real_smooth # smooth or flat shading default: False
|
||||
# keep_lga # when culet > 0, keep lga default: False
|
||||
|
||||
# variables / shortcuts
|
||||
if s % 2: # prevent odd number of steps (messes up mesh)
|
||||
s = s - 1
|
||||
if not girdle_real:
|
||||
g_real_smooth = False
|
||||
ang = 2 * pi / s # angle step size
|
||||
Verts = [] # collect all vertices
|
||||
Faces = [] # collect all faces
|
||||
ca = cos(ang)
|
||||
ca2 = cos(ang / 2)
|
||||
sa4 = sin(ang / 4)
|
||||
ta4 = tan(ang / 4)
|
||||
ta8 = tan(ang / 8)
|
||||
|
||||
def fa(*vs): # shortcut Faces.append
|
||||
v = []
|
||||
for u in vs:
|
||||
v.append(u)
|
||||
Faces.append(v)
|
||||
|
||||
def va(vx, vz, iang, sang, n): # shortcut Verts.append
|
||||
for i in range(n):
|
||||
v = Vector((vx, 0, vz))
|
||||
ai = sang + iang * i
|
||||
E_rot = Euler((0, 0, ai), 'XYZ')
|
||||
v.rotate(E_rot)
|
||||
Verts.append((v.x, v.y, v.z))
|
||||
|
||||
# upper girdle angle
|
||||
uga = (1 - bezel_f) * crown_h * 2 / (ca2 -
|
||||
(table_w + (1 - table_w) * bezel_f) * ca2 / ca)
|
||||
|
||||
# lower girdle angle
|
||||
if keep_lga:
|
||||
if pavi_f > 0 and pavi_f < 1:
|
||||
lga = (1 - pavi_f) * pavi_d * 2 / (ca2 - pavi_f * ca2 / ca)
|
||||
elif pavi_f == 1:
|
||||
lga = 0
|
||||
else:
|
||||
lga = 2 * pavi_d * ca
|
||||
else:
|
||||
lga = (1 - pavi_f) * pavi_d * 2 / (ca2 -
|
||||
(culet + (1 - culet) * pavi_f) * ca2 / ca)
|
||||
|
||||
# append girdle vertices
|
||||
va(1, 0, ang, 0, s)
|
||||
va(1, 2 * girdle_t, ang, 0, s)
|
||||
|
||||
# append real girdle vertices
|
||||
if girdle_real:
|
||||
dnu = uga * (1 - ca2)
|
||||
dfu = uga * (ta8 + ta4) * sa4
|
||||
dnl = lga * (1 - ca2)
|
||||
dfl = lga * (ta8 + ta4) * sa4
|
||||
if abs(dnu) + abs(dnl) > 2 * girdle_t or dnu < 0 or dnl < 0:
|
||||
girdle_real = False
|
||||
else:
|
||||
va(1, dnl, ang, ang / 2, s)
|
||||
va(1, 2 * girdle_t - dnu, ang, ang / 2, s)
|
||||
va(1, dfl, ang / 2, ang / 4, 2 * s)
|
||||
va(1, 2 * girdle_t - dfu, ang / 2, ang / 4, 2 * s)
|
||||
|
||||
# make girdle faces
|
||||
l1 = len(Verts) # 2*s / 8*s
|
||||
for i in range(l1):
|
||||
if girdle_real:
|
||||
if i < s:
|
||||
fa(i, i + s, 2 * i + 6 * s, 2 * i + 4 * s)
|
||||
if i == 0:
|
||||
fa(i, s, l1 - 1, 6 * s - 1)
|
||||
else:
|
||||
fa(i, i + s, 2 * i + 6 * s - 1, 2 * i + 4 * s - 1)
|
||||
elif i > 2 * s - 1 and i < 3 * s:
|
||||
fa(i, i + s, 2 * (i + s), 2 * i)
|
||||
fa(i, i + s, 2 * (i + s) + 1, 2 * i + 1)
|
||||
else:
|
||||
if i < s - 1:
|
||||
fa(i, i + s, i + s + 1, i + 1)
|
||||
elif i == s - 1:
|
||||
fa(i, i + s, s, 0)
|
||||
|
||||
# append upper girdle facet vertices
|
||||
va((table_w + (1 - table_w) * bezel_f) / ca, (1 - bezel_f) * 2 * crown_h +
|
||||
2 * girdle_t, 2 * ang, ang, int(s / 2))
|
||||
|
||||
# make upper girdle facet faces
|
||||
l2 = len(Verts) # 2.5*s / 8.5*s
|
||||
for i in range(l2):
|
||||
if i > s and i < 2 * s - 1 and i % 2 != 0:
|
||||
if girdle_real:
|
||||
fa(i, 2 * (i + 2 * s), i + 2 * s, 2 * (i + 2 * s) + 1, i + 1,
|
||||
int(7.5 * s) + int((i - 1) / 2))
|
||||
fa(i, 2 * (i + 2 * s) - 1, i + 2 * s - 1, 2 * (i + 2 * s - 1),
|
||||
i - 1, int(7.5 * s) + int((i - 1) / 2))
|
||||
else:
|
||||
fa(i, i + 1, int((i + 3 * s) / 2))
|
||||
fa(i, i - 1, int((i + 3 * s) / 2))
|
||||
elif i == s:
|
||||
if girdle_real:
|
||||
fa(i, l1 - 1, 4 * s - 1, l1 - 2, 2 * i - 1, l2 - 1)
|
||||
fa(2 * i - 2, l1 - 4, 4 * s - 2, l1 - 3, 2 * i - 1, l2 - 1)
|
||||
else:
|
||||
fa(i, 2 * i - 1, l2 - 1)
|
||||
fa(2 * i - 1, 2 * i - 2, l2 - 1)
|
||||
|
||||
# append table vertices
|
||||
va(table_w, (crown_h + girdle_t) * 2, 2 * ang, 0, int(s / 2))
|
||||
|
||||
# make bezel facet faces and star facet faces
|
||||
l3 = len(Verts) # 3*s / 9*s
|
||||
for i in range(l3):
|
||||
if i > l2 - 1 and i < l3 - 1:
|
||||
fa(i, i + 1, i - int(s / 2))
|
||||
fa(i + 1, i - int(s / 2), 2 * (i - l2) + 2 + s, i - int(s / 2) + 1)
|
||||
elif i == l3 - 1:
|
||||
fa(i, l2, l2 - 1)
|
||||
fa(s, l2 - 1, l2, l2 - int(s / 2))
|
||||
|
||||
# make table facet face
|
||||
tf = []
|
||||
for i in range(l3):
|
||||
if i > l2 - 1:
|
||||
tf.append(i)
|
||||
fa(*tf)
|
||||
|
||||
# append lower girdle facet vertices
|
||||
if keep_lga:
|
||||
va(pavi_f / ca, (pavi_f - 1) * pavi_d * 2, 2 * ang, ang, int(s / 2))
|
||||
else:
|
||||
va((pavi_f * (1 - culet) + culet) / ca, (pavi_f - 1) * pavi_d * 2, 2 * ang,
|
||||
ang, int(s / 2))
|
||||
|
||||
# make lower girdle facet faces
|
||||
l4 = len(Verts) # 3.5*s / 9.5*s
|
||||
for i in range(l4):
|
||||
if i > 0 and i < s - 1 and i % 2 == 0:
|
||||
if girdle_real:
|
||||
fa(i, 2 * (i + 2 * s), i + 2 * s, 2 * (i + 2 * s) + 1, i + 1,
|
||||
int(i / 2) + 9 * s)
|
||||
fa(i, 2 * (i + 2 * s) - 1, i + 2 * s - 1, 2 * (i + 2 * s - 1),
|
||||
i - 1, int(i / 2) + 9 * s - 1)
|
||||
else:
|
||||
fa(i, i + 1, int(i / 2) + l4 - int(s / 2))
|
||||
fa(i, i - 1, int(i / 2) + l4 - int(s / 2) - 1)
|
||||
elif i == 0:
|
||||
if girdle_real:
|
||||
fa(0, 4 * s, 2 * s, 4 * s + 1, 1, 9 * s)
|
||||
fa(0, 6 * s - 1, 3 * s - 1, 6 * s - 2, s - 1, l4 - 1)
|
||||
else:
|
||||
fa(0, 1, l4 - int(s / 2))
|
||||
fa(0, s - 1, l4 - 1)
|
||||
|
||||
# append culet vertice(s)
|
||||
if culet == 0:
|
||||
va(0, pavi_d * (-2), 0, 0, 1)
|
||||
else:
|
||||
if keep_lga:
|
||||
va(culet * pavi_f / ca, pavi_d * (-2) + culet * pavi_f * 2 * pavi_d,
|
||||
2 * ang, ang, int(s / 2))
|
||||
else:
|
||||
va(culet / ca, pavi_d * (-2), 2 * ang, ang, int(s / 2))
|
||||
|
||||
# make pavilion facet face
|
||||
l5 = len(Verts) # 4*s / 10*s //if !culet: 3.5*s+1 / 9.5*s+1
|
||||
for i in range(l5):
|
||||
if i > 0 and i < s - 1 and i % 2 == 0:
|
||||
if culet:
|
||||
fa(i, l3 + int(i / 2), l3 + int((s + i) / 2),
|
||||
l3 + int((s + i) / 2) - 1, l3 + int(i / 2) - 1)
|
||||
else:
|
||||
fa(i, l3 + int(i / 2), l5 - 1, l3 + int(i / 2) - 1)
|
||||
elif i == 0:
|
||||
if culet:
|
||||
fa(i, l3, l4, l5 - 1, l4 - 1)
|
||||
else:
|
||||
fa(i, l3, l5 - 1, l4 - 1)
|
||||
|
||||
# make culet facet face
|
||||
if culet:
|
||||
cf = []
|
||||
for i in range(l5):
|
||||
if i > l4 - 1:
|
||||
cf.append(i)
|
||||
fa(*cf)
|
||||
|
||||
# create actual mesh and object based on Verts and Faces given
|
||||
dmesh = bpy.data.meshes.new("dmesh")
|
||||
dmesh.from_pydata(Verts, [], Faces)
|
||||
dmesh.update()
|
||||
|
||||
return dmesh
|
||||
|
||||
# object generating function, returns final object
|
||||
def addBrilliant(context, self, s, table_w, crown_h, girdle_t, pavi_d, bezel_f,
|
||||
pavi_f, culet, girdle_real, keep_lga, g_real_smooth):
|
||||
|
||||
# deactivate possible active Objects
|
||||
bpy.context.view_layer.objects.active = None
|
||||
|
||||
# create actual mesh and object based on Verts and Faces given
|
||||
dmesh = add_mesh_Brilliant(context, s, table_w, crown_h, girdle_t, pavi_d, bezel_f,
|
||||
pavi_f, culet, girdle_real, keep_lga, g_real_smooth)
|
||||
|
||||
# Create object and link it into scene.
|
||||
dobj = object_utils.object_data_add(context, dmesh, operator=self, name="dobj")
|
||||
|
||||
# activate and select object
|
||||
bpy.context.view_layer.objects.active = dobj
|
||||
dobj.select_set(True)
|
||||
obj = bpy.context.active_object
|
||||
|
||||
# flip all face normals outside
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
sel_mode = bpy.context.tool_settings.mesh_select_mode
|
||||
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
|
||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
for i, face in enumerate(obj.data.polygons):
|
||||
face.select = True
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
bpy.ops.mesh.normals_make_consistent(inside=False)
|
||||
bpy.context.tool_settings.mesh_select_mode = sel_mode
|
||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
|
||||
# make girdle smooth for complex girdle
|
||||
if girdle_real and g_real_smooth:
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT') # deselect all mesh data
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
pls = []
|
||||
dp = obj.data.polygons[:4 * s] # only consider faces of girdle
|
||||
ov = obj.data.vertices
|
||||
|
||||
for i, p in enumerate(dp):
|
||||
pls.extend(p.vertices) # list all verts of girdle
|
||||
|
||||
for i, e in enumerate(obj.data.edges): # select edges to mark sharp
|
||||
if e.vertices[0] in pls and e.vertices[1] in pls and abs(
|
||||
ov[e.vertices[0]].co.x - ov[e.vertices[1]].co.x):
|
||||
obj.data.edges[i].select = True
|
||||
continue
|
||||
obj.data.edges[i].select = False
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
bpy.ops.mesh.mark_sharp()
|
||||
|
||||
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
|
||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
for i, face in enumerate(obj.data.polygons):
|
||||
if i < 4 * s:
|
||||
face.select = True
|
||||
continue
|
||||
face.select = False
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
bpy.ops.mesh.faces_shade_smooth()
|
||||
|
||||
edge_split_modifier = context.object.modifiers.new("", 'EDGE_SPLIT')
|
||||
|
||||
bpy.context.tool_settings.mesh_select_mode = sel_mode
|
||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
|
||||
bpy.ops.object.modifier_apply(modifier=edge_split_modifier.name)
|
||||
|
||||
return dobj
|
||||
|
||||
|
||||
# add new operator for object
|
||||
class MESH_OT_primitive_brilliant_add(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_brilliant_add"
|
||||
bl_label = "Brilliant"
|
||||
bl_description = "Construct a custom brilliant mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Brilliant : BoolProperty(name = "Brilliant",
|
||||
default = True,
|
||||
description = "Brilliant")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Brilliant")
|
||||
|
||||
s: IntProperty(
|
||||
name="Segments",
|
||||
description="Longitudial segmentation",
|
||||
step=1,
|
||||
min=6,
|
||||
max=128,
|
||||
default=16
|
||||
)
|
||||
table_w: FloatProperty(
|
||||
name="Table Width",
|
||||
description="Width of table",
|
||||
min=0.001,
|
||||
max=1.0,
|
||||
default=0.53,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
crown_h: FloatProperty(
|
||||
name="Crown Height",
|
||||
description="Height of crown",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
default=0.162,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
girdle_t: FloatProperty(
|
||||
name="Girdle Height",
|
||||
description="Height of girdle",
|
||||
min=0.0,
|
||||
max=0.5,
|
||||
default=0.017,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
girdle_real: BoolProperty(
|
||||
name="Real Girdle",
|
||||
description="More beautiful girdle; has more polygons",
|
||||
default=True
|
||||
)
|
||||
g_real_smooth: BoolProperty(
|
||||
name="Smooth Girdle",
|
||||
description="smooth shading for girdle, only available for real girdle",
|
||||
default=False
|
||||
)
|
||||
pavi_d: FloatProperty(
|
||||
name="Pavilion Depth",
|
||||
description="Height of pavilion",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
default=0.431,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
bezel_f: FloatProperty(
|
||||
name="Upper Facet Factor",
|
||||
description="Determines the form of bezel and upper girdle facets",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
default=0.250,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
pavi_f: FloatProperty(
|
||||
name="Lower Facet Factor",
|
||||
description="Determines the form of pavilion and lower girdle facets",
|
||||
min=0.001,
|
||||
max=1.0,
|
||||
default=0.400,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
culet: FloatProperty(
|
||||
name="Culet Size",
|
||||
description="0: no culet (default)",
|
||||
min=0.0,
|
||||
max=0.999,
|
||||
default=0.0,
|
||||
subtype='FACTOR'
|
||||
)
|
||||
keep_lga: BoolProperty(
|
||||
name="Retain Lower Angle",
|
||||
description="If culet > 0, retains angle of pavilion facets",
|
||||
default=False
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, "s")
|
||||
layout.prop(self, "table_w")
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "crown_h", text='Height Crown')
|
||||
col.prop(self, "girdle_t", text='Girdle')
|
||||
col.prop(self, "pavi_d", text='Pavilion')
|
||||
layout.prop(self, "girdle_real")
|
||||
layout.prop(self, "g_real_smooth")
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "bezel_f", text='Facet Upper')
|
||||
col.prop(self, "pavi_f", text='Lower')
|
||||
layout.prop(self, "culet")
|
||||
layout.prop(self, "keep_lga")
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
# call mesh/object generator function with user inputs
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Brilliant' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
mesh = add_mesh_Brilliant(context, self.s, self.table_w, self.crown_h,
|
||||
self.girdle_t, self.pavi_d, self.bezel_f,
|
||||
self.pavi_f, self.culet, self.girdle_real,
|
||||
self.keep_lga, self.g_real_smooth
|
||||
)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
obj = addBrilliant(context, self, self.s, self.table_w, self.crown_h,
|
||||
self.girdle_t, self.pavi_d, self.bezel_f,
|
||||
self.pavi_f, self.culet, self.girdle_real,
|
||||
self.keep_lga, self.g_real_smooth
|
||||
)
|
||||
|
||||
obj.data["Brilliant"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in BrilliantParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
obj = addBrilliant(context, self, self.s, self.table_w, self.crown_h,
|
||||
self.girdle_t, self.pavi_d, self.bezel_f,
|
||||
self.pavi_f, self.culet, self.girdle_real,
|
||||
self.keep_lga, self.g_real_smooth
|
||||
)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def BrilliantParameters():
|
||||
BrilliantParameters = [
|
||||
"s",
|
||||
"table_w",
|
||||
"crown_h",
|
||||
"girdle_t",
|
||||
"girdle_real",
|
||||
"g_real_smooth",
|
||||
"pavi_d",
|
||||
"bezel_f",
|
||||
"pavi_f",
|
||||
"culet",
|
||||
"keep_lga",
|
||||
]
|
||||
return BrilliantParameters
|
||||
@@ -0,0 +1,521 @@
|
||||
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: Alain Ducharme (phymec)
|
||||
|
||||
import bpy
|
||||
from bpy_extras import object_utils
|
||||
from itertools import permutations
|
||||
from math import (
|
||||
copysign, pi,
|
||||
sqrt,
|
||||
)
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
def round_cube(radius=1.0, arcdiv=4, lindiv=0., size=(0., 0., 0.),
|
||||
div_type='CORNERS', odd_axis_align=False, info_only=False):
|
||||
# subdiv bitmasks
|
||||
CORNERS, EDGES, ALL = 0, 1, 2
|
||||
try:
|
||||
subdiv = ('CORNERS', 'EDGES', 'ALL').index(div_type)
|
||||
except ValueError:
|
||||
subdiv = CORNERS # fallback
|
||||
|
||||
radius = max(radius, 0.)
|
||||
if not radius:
|
||||
# No sphere
|
||||
arcdiv = 1
|
||||
odd_axis_align = False
|
||||
|
||||
if arcdiv <= 0:
|
||||
arcdiv = max(round(pi * radius * lindiv * 0.5), 1)
|
||||
arcdiv = max(round(arcdiv), 1)
|
||||
if lindiv <= 0. and radius:
|
||||
lindiv = 1. / (pi / (arcdiv * 2.) * radius)
|
||||
lindiv = max(lindiv, 0.)
|
||||
if not lindiv:
|
||||
subdiv = CORNERS
|
||||
|
||||
odd = arcdiv % 2 # even = arcdiv % 2 ^ 1
|
||||
step_size = 2. / arcdiv
|
||||
|
||||
odd_aligned = 0
|
||||
vi = -1.
|
||||
steps = arcdiv + 1
|
||||
if odd_axis_align and odd:
|
||||
odd_aligned = 1
|
||||
vi += 0.5 * step_size
|
||||
steps = arcdiv
|
||||
axis_aligned = not odd or odd_aligned
|
||||
|
||||
if arcdiv == 1 and not odd_aligned and subdiv == EDGES:
|
||||
subdiv = CORNERS
|
||||
|
||||
half_chord = 0. # ~ spherical cap base radius
|
||||
sagitta = 0. # ~ spherical cap height
|
||||
if not axis_aligned:
|
||||
half_chord = sqrt(3.) * radius / (3. * arcdiv)
|
||||
id2 = 1. / (arcdiv * arcdiv)
|
||||
sagitta = radius - radius * sqrt(id2 * id2 / 3. - id2 + 1.)
|
||||
|
||||
# Extrusion per axis
|
||||
exyz = [0. if s < 2. * (radius - sagitta) else (s - 2. * (radius - sagitta)) * 0.5 for s in size]
|
||||
ex, ey, ez = exyz
|
||||
|
||||
dxyz = [0, 0, 0] # extrusion divisions per axis
|
||||
dssxyz = [0., 0., 0.] # extrusion division step sizes per axis
|
||||
|
||||
for i in range(3):
|
||||
sc = 2. * (exyz[i] + half_chord)
|
||||
dxyz[i] = round(sc * lindiv) if subdiv else 0
|
||||
if dxyz[i]:
|
||||
dssxyz[i] = sc / dxyz[i]
|
||||
dxyz[i] -= 1
|
||||
else:
|
||||
dssxyz[i] = sc
|
||||
|
||||
if info_only:
|
||||
ec = sum(1 for n in exyz if n)
|
||||
if subdiv:
|
||||
fxyz = [d + (e and axis_aligned) for d, e in zip(dxyz, exyz)]
|
||||
dvc = arcdiv * 4 * sum(fxyz)
|
||||
if subdiv == ALL:
|
||||
dvc += sum(p1 * p2 for p1, p2 in permutations(fxyz, 2))
|
||||
elif subdiv == EDGES and axis_aligned:
|
||||
# (0, 0, 2, 4) * sum(dxyz) + (0, 0, 2, 6)
|
||||
dvc += ec * ec // 2 * sum(dxyz) + ec * (ec - 1)
|
||||
else:
|
||||
dvc = (arcdiv * 4) * ec + ec * (ec - 1) if axis_aligned else 0
|
||||
vert_count = int(6 * arcdiv * arcdiv + (0 if odd_aligned else 2) + dvc)
|
||||
if not radius and not max(size) > 0:
|
||||
vert_count = 1
|
||||
return arcdiv, lindiv, vert_count
|
||||
|
||||
if not radius and not max(size) > 0:
|
||||
# Single vertex
|
||||
return [(0, 0, 0)], []
|
||||
|
||||
# uv lookup table
|
||||
uvlt = []
|
||||
v = vi
|
||||
for j in range(1, steps + 1):
|
||||
v2 = v * v
|
||||
uvlt.append((v, v2, radius * sqrt(18. - 6. * v2) / 6.))
|
||||
v = vi + j * step_size # v += step_size # instead of accumulating errors
|
||||
# clear fp errors / signs at axis
|
||||
if abs(v) < 1e-10:
|
||||
v = 0.0
|
||||
|
||||
# Sides built left to right bottom up
|
||||
# xp yp zp xd yd zd
|
||||
sides = ((0, 2, 1, (-1, 1, 1)), # Y+ Front
|
||||
(1, 2, 0, (-1, -1, 1)), # X- Left
|
||||
(0, 2, 1, (1, -1, 1)), # Y- Back
|
||||
(1, 2, 0, (1, 1, 1)), # X+ Right
|
||||
(0, 1, 2, (-1, 1, -1)), # Z- Bottom
|
||||
(0, 1, 2, (-1, -1, 1))) # Z+ Top
|
||||
|
||||
# side vertex index table (for sphere)
|
||||
svit = [[[] for i in range(steps)] for i in range(6)]
|
||||
# Extend svit rows for extrusion
|
||||
yer = zer = 0
|
||||
if ey:
|
||||
yer = axis_aligned + (dxyz[1] if subdiv else 0)
|
||||
svit[4].extend([[] for i in range(yer)])
|
||||
svit[5].extend([[] for i in range(yer)])
|
||||
if ez:
|
||||
zer = axis_aligned + (dxyz[2] if subdiv else 0)
|
||||
for side in range(4):
|
||||
svit[side].extend([[] for i in range(zer)])
|
||||
# Extend svit rows for odd_aligned
|
||||
if odd_aligned:
|
||||
for side in range(4):
|
||||
svit[side].append([])
|
||||
|
||||
hemi = steps // 2
|
||||
|
||||
# Create vertices and svit without dups
|
||||
vert = [0., 0., 0.]
|
||||
verts = []
|
||||
|
||||
if arcdiv == 1 and not odd_aligned and subdiv == ALL:
|
||||
# Special case: Grid Cuboid
|
||||
for side, (xp, yp, zp, dir) in enumerate(sides):
|
||||
svitc = svit[side]
|
||||
rows = len(svitc)
|
||||
if rows < dxyz[yp] + 2:
|
||||
svitc.extend([[] for i in range(dxyz[yp] + 2 - rows)])
|
||||
vert[zp] = (half_chord + exyz[zp]) * dir[zp]
|
||||
for j in range(dxyz[yp] + 2):
|
||||
vert[yp] = (j * dssxyz[yp] - half_chord - exyz[yp]) * dir[yp]
|
||||
for i in range(dxyz[xp] + 2):
|
||||
vert[xp] = (i * dssxyz[xp] - half_chord - exyz[xp]) * dir[xp]
|
||||
if (side == 5) or ((i < dxyz[xp] + 1 and j < dxyz[yp] + 1) and (side < 4 or (i and j))):
|
||||
svitc[j].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
else:
|
||||
for side, (xp, yp, zp, dir) in enumerate(sides):
|
||||
svitc = svit[side]
|
||||
exr = exyz[xp]
|
||||
eyr = exyz[yp]
|
||||
ri = 0 # row index
|
||||
rij = zer if side < 4 else yer
|
||||
|
||||
if side == 5:
|
||||
span = range(steps)
|
||||
elif side < 4 or odd_aligned:
|
||||
span = range(arcdiv)
|
||||
else:
|
||||
span = range(1, arcdiv)
|
||||
ri = 1
|
||||
|
||||
for j in span: # rows
|
||||
v, v2, mv2 = uvlt[j]
|
||||
tv2mh = 1. / 3. * v2 - 0.5
|
||||
hv2 = 0.5 * v2
|
||||
|
||||
if j == hemi and rij:
|
||||
# Jump over non-edge row indices
|
||||
ri += rij
|
||||
|
||||
for i in span: # columns
|
||||
u, u2, mu2 = uvlt[i]
|
||||
vert[xp] = u * mv2
|
||||
vert[yp] = v * mu2
|
||||
vert[zp] = radius * sqrt(u2 * tv2mh - hv2 + 1.)
|
||||
|
||||
vert[0] = (vert[0] + copysign(ex, vert[0])) * dir[0]
|
||||
vert[1] = (vert[1] + copysign(ey, vert[1])) * dir[1]
|
||||
vert[2] = (vert[2] + copysign(ez, vert[2])) * dir[2]
|
||||
rv = tuple(vert)
|
||||
|
||||
if exr and i == hemi:
|
||||
rx = vert[xp] # save rotated x
|
||||
vert[xp] = rxi = (-exr - half_chord) * dir[xp]
|
||||
if axis_aligned:
|
||||
svitc[ri].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
if subdiv:
|
||||
offsetx = dssxyz[xp] * dir[xp]
|
||||
for k in range(dxyz[xp]):
|
||||
vert[xp] += offsetx
|
||||
svitc[ri].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
if eyr and j == hemi and axis_aligned:
|
||||
vert[xp] = rxi
|
||||
vert[yp] = -eyr * dir[yp]
|
||||
svitc[hemi].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
if subdiv:
|
||||
offsety = dssxyz[yp] * dir[yp]
|
||||
ry = vert[yp]
|
||||
for k in range(dxyz[yp]):
|
||||
vert[yp] += offsety
|
||||
svitc[hemi + axis_aligned + k].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
vert[yp] = ry
|
||||
for k in range(dxyz[xp]):
|
||||
vert[xp] += offsetx
|
||||
svitc[hemi].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
if subdiv & ALL:
|
||||
for l in range(dxyz[yp]):
|
||||
vert[yp] += offsety
|
||||
svitc[hemi + axis_aligned + l].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
vert[yp] = ry
|
||||
vert[xp] = rx # restore
|
||||
|
||||
if eyr and j == hemi:
|
||||
vert[yp] = (-eyr - half_chord) * dir[yp]
|
||||
if axis_aligned:
|
||||
svitc[hemi].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
if subdiv:
|
||||
offsety = dssxyz[yp] * dir[yp]
|
||||
for k in range(dxyz[yp]):
|
||||
vert[yp] += offsety
|
||||
if exr and i == hemi and not axis_aligned and subdiv & ALL:
|
||||
vert[xp] = rxi
|
||||
for l in range(dxyz[xp]):
|
||||
vert[xp] += offsetx
|
||||
svitc[hemi + k].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
vert[xp] = rx
|
||||
svitc[hemi + axis_aligned + k].append(len(verts))
|
||||
verts.append(tuple(vert))
|
||||
|
||||
svitc[ri].append(len(verts))
|
||||
verts.append(rv)
|
||||
ri += 1
|
||||
|
||||
# Complete svit edges (shared vertices)
|
||||
# Sides' right edge
|
||||
for side, rows in enumerate(svit[:4]):
|
||||
for j, row in enumerate(rows[:-1]):
|
||||
svit[3 if not side else side - 1][j].append(row[0])
|
||||
# Sides' top edge
|
||||
svit[0][-1].extend(svit[5][0])
|
||||
svit[2][-1].extend(svit[5][-1][::-1])
|
||||
for row in svit[5]:
|
||||
svit[3][-1].insert(0, row[0])
|
||||
svit[1][-1].append(row[-1])
|
||||
if odd_aligned:
|
||||
for side in svit[:4]:
|
||||
side[-1].append(-1)
|
||||
# Bottom edges
|
||||
if odd_aligned:
|
||||
svit[4].insert(0, [-1] + svit[2][0][-2::-1] + [-1])
|
||||
for i, col in enumerate(svit[3][0][:-1]):
|
||||
svit[4][i + 1].insert(0, col)
|
||||
svit[4][i + 1].append(svit[1][0][-i - 2])
|
||||
svit[4].append([-1] + svit[0][0][:-1] + [-1])
|
||||
else:
|
||||
svit[4][0].extend(svit[2][0][::-1])
|
||||
for i, col in enumerate(svit[3][0][1:-1]):
|
||||
svit[4][i + 1].insert(0, col)
|
||||
svit[4][i + 1].append(svit[1][0][-i - 2])
|
||||
svit[4][-1].extend(svit[0][0])
|
||||
|
||||
# Build faces
|
||||
faces = []
|
||||
if not axis_aligned:
|
||||
hemi -= 1
|
||||
for side, rows in enumerate(svit):
|
||||
xp, yp = sides[side][:2]
|
||||
oa4 = odd_aligned and side == 4
|
||||
if oa4: # special case
|
||||
hemi += 1
|
||||
for j, row in enumerate(rows[:-1]):
|
||||
tri = odd_aligned and (oa4 and not j or rows[j + 1][-1] < 0)
|
||||
for i, vi in enumerate(row[:-1]):
|
||||
# odd_aligned triangle corners
|
||||
if vi < 0:
|
||||
if not j and not i:
|
||||
faces.append((row[i + 1], rows[j + 1][i + 1], rows[j + 1][i]))
|
||||
elif oa4 and not i and j == len(rows) - 2:
|
||||
faces.append((vi, row[i + 1], rows[j + 1][i + 1]))
|
||||
elif tri and i == len(row) - 2:
|
||||
if j:
|
||||
faces.append((vi, row[i + 1], rows[j + 1][i]))
|
||||
else:
|
||||
if oa4 or arcdiv > 1:
|
||||
faces.append((vi, rows[j + 1][i + 1], rows[j + 1][i]))
|
||||
else:
|
||||
faces.append((vi, row[i + 1], rows[j + 1][i]))
|
||||
# subdiv = EDGES (not ALL)
|
||||
elif subdiv and len(rows[j + 1]) < len(row) and (i >= hemi):
|
||||
if (i == hemi):
|
||||
faces.append((vi, row[i + 1 + dxyz[xp]], rows[j + 1 + dxyz[yp]][i + 1 + dxyz[xp]],
|
||||
rows[j + 1 + dxyz[yp]][i]))
|
||||
elif i > hemi + dxyz[xp]:
|
||||
faces.append((vi, row[i + 1], rows[j + 1][i + 1 - dxyz[xp]], rows[j + 1][i - dxyz[xp]]))
|
||||
elif subdiv and len(rows[j + 1]) > len(row) and (i >= hemi):
|
||||
if (i > hemi):
|
||||
faces.append((vi, row[i + 1], rows[j + 1][i + 1 + dxyz[xp]], rows[j + 1][i + dxyz[xp]]))
|
||||
elif subdiv and len(row) < len(rows[0]) and i == hemi:
|
||||
pass
|
||||
else:
|
||||
# Most faces...
|
||||
faces.append((vi, row[i + 1], rows[j + 1][i + 1], rows[j + 1][i]))
|
||||
if oa4:
|
||||
hemi -= 1
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
class AddRoundCube(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_round_cube_add"
|
||||
bl_label = "Add Round Cube"
|
||||
bl_description = ("Create mesh primitives: Quadspheres, "
|
||||
"Capsules, Rounded Cuboids, 3D Grids etc")
|
||||
bl_options = {"REGISTER", "UNDO", "PRESET"}
|
||||
|
||||
sanity_check_verts = 200000
|
||||
vert_count = 0
|
||||
|
||||
Roundcube : BoolProperty(name = "Roundcube",
|
||||
default = True,
|
||||
description = "Roundcube")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Roundcube")
|
||||
|
||||
radius: FloatProperty(
|
||||
name="Radius",
|
||||
description="Radius of vertices for sphere, capsule or cuboid bevel",
|
||||
default=1, min=0.0, soft_min=0.01, step=10
|
||||
)
|
||||
size: FloatVectorProperty(
|
||||
name="Size",
|
||||
description="Size",
|
||||
subtype='XYZ',
|
||||
default=(0.0, 0.0, 0.0),
|
||||
)
|
||||
arc_div: IntProperty(
|
||||
name="Arc Divisions",
|
||||
description="Arc curve divisions, per quadrant, 0=derive from Linear",
|
||||
default=8, min=1
|
||||
)
|
||||
lin_div: FloatProperty(
|
||||
name="Linear Divisions",
|
||||
description="Linear unit divisions (Edges/Faces), 0=derive from Arc",
|
||||
default=0.0, min=0.0, step=100, precision=1
|
||||
)
|
||||
no_limit: BoolProperty(
|
||||
name='No Vertex Limit',
|
||||
description='Do not limit to ' + str(sanity_check_verts) + ' vertices (sanity check)',
|
||||
options={'HIDDEN'},
|
||||
default=False
|
||||
)
|
||||
div_type: EnumProperty(
|
||||
name='Type',
|
||||
description='Division type',
|
||||
items=(
|
||||
('CORNERS', 'Corners', 'Sphere / Corners'),
|
||||
('EDGES', 'Edges', 'Sphere / Corners and extruded edges (size)'),
|
||||
('ALL', 'All', 'Sphere / Corners, extruded edges and faces (size)')),
|
||||
default='CORNERS',
|
||||
)
|
||||
odd_axis_align: BoolProperty(
|
||||
name='Odd Axis Align',
|
||||
description='Align odd arc divisions with axes (Note: triangle corners!)',
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if self.arc_div <= 0 and self.lin_div <= 0:
|
||||
self.report({'ERROR'},
|
||||
"Either Arc Divisions or Linear Divisions must be greater than zero")
|
||||
return {'CANCELLED'}
|
||||
|
||||
if not self.no_limit and self.vert_count > self.sanity_check_verts:
|
||||
self.report({'ERROR'}, 'More than ' + str(self.sanity_check_verts) +
|
||||
' vertices! Check "No Limit" to proceed')
|
||||
return {'CANCELLED'}
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Roundcube' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
verts, faces = round_cube(self.radius, self.arc_div, self.lin_div,
|
||||
self.size, self.div_type, self.odd_axis_align)
|
||||
mesh = bpy.data.meshes.new('Roundcube')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = round_cube(self.radius, self.arc_div, self.lin_div,
|
||||
self.size, self.div_type, self.odd_axis_align)
|
||||
mesh = bpy.data.meshes.new('Roundcube')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["Roundcube"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in RoundCubeParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = round_cube(self.radius, self.arc_div, self.lin_div,
|
||||
self.size, self.div_type, self.odd_axis_align)
|
||||
mesh = bpy.data.meshes.new('Roundcube')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def check(self, context):
|
||||
self.arcdiv, self.lindiv, self.vert_count = round_cube(
|
||||
self.radius, self.arc_div, self.lin_div,
|
||||
self.size, self.div_type, self.odd_axis_align,
|
||||
True
|
||||
)
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.check(context)
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
self.check(context)
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'radius')
|
||||
layout.column().prop(self, 'size', expand=True)
|
||||
layout.separator()
|
||||
|
||||
layout.prop(self, 'div_type', text='Division Method')
|
||||
layout.prop(self, 'arc_div', text='Arc')
|
||||
row = layout.row()
|
||||
row.enabled = (
|
||||
self.div_type != 'CORNERS' and
|
||||
(
|
||||
self.size[0] > self.radius*2 or
|
||||
self.size[1] > self.radius*2 or
|
||||
self.size[2] > self.radius*2
|
||||
)
|
||||
)
|
||||
row.prop(self, 'lin_div', text='Linear')
|
||||
|
||||
row = layout.row()
|
||||
row.alert = self.vert_count > self.sanity_check_verts
|
||||
row.prop(self, 'no_limit')
|
||||
|
||||
row = layout.row()
|
||||
row.active = self.arcdiv % 2
|
||||
row.prop(self, 'odd_axis_align', text='Triangle Corners')
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def RoundCubeParameters():
|
||||
RoundCubeParameters = [
|
||||
"radius",
|
||||
"size",
|
||||
"arc_div",
|
||||
"lin_div",
|
||||
"div_type",
|
||||
"odd_axis_align",
|
||||
"no_limit",
|
||||
]
|
||||
return RoundCubeParameters
|
||||
@@ -0,0 +1,405 @@
|
||||
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: DreamPainter
|
||||
|
||||
import bpy
|
||||
from math import sqrt
|
||||
from mathutils import Vector
|
||||
from functools import reduce
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
)
|
||||
from bpy_extras.object_utils import object_data_add
|
||||
|
||||
|
||||
# function to make the reduce function work as a workaround to sum a list of vectors
|
||||
|
||||
def vSum(list):
|
||||
return reduce(lambda a, b: a + b, list)
|
||||
|
||||
|
||||
# Get a copy of the input faces, but with the normals flipped by reversing the order of the vertex indices of each face.
|
||||
def flippedFaceNormals(faces):
|
||||
return [list(reversed(vertexIndices)) for vertexIndices in faces]
|
||||
|
||||
|
||||
# creates the 5 platonic solids as a base for the rest
|
||||
# plato: should be one of {"4","6","8","12","20"}. decides what solid the
|
||||
# outcome will be.
|
||||
# returns a list of vertices and faces
|
||||
|
||||
def source(plato):
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
# Tetrahedron
|
||||
if plato == "4":
|
||||
# Calculate the necessary constants
|
||||
s = sqrt(2) / 3.0
|
||||
t = -1 / 3
|
||||
u = sqrt(6) / 3
|
||||
|
||||
# create the vertices and faces
|
||||
v = [(0, 0, 1), (2 * s, 0, t), (-s, u, t), (-s, -u, t)]
|
||||
faces = [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]]
|
||||
|
||||
# Hexahedron (cube)
|
||||
elif plato == "6":
|
||||
# Calculate the necessary constants
|
||||
s = 1 / sqrt(3)
|
||||
|
||||
# create the vertices and faces
|
||||
v = [(-s, -s, -s), (s, -s, -s), (s, s, -s), (-s, s, -s), (-s, -s, s), (s, -s, s), (s, s, s), (-s, s, s)]
|
||||
faces = [[0, 3, 2, 1], [0, 1, 5, 4], [0, 4, 7, 3], [6, 5, 1, 2], [6, 2, 3, 7], [6, 7, 4, 5]]
|
||||
|
||||
# Octahedron
|
||||
elif plato == "8":
|
||||
# create the vertices and faces
|
||||
v = [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]
|
||||
faces = [[4, 0, 2], [4, 2, 1], [4, 1, 3], [4, 3, 0], [5, 2, 0], [5, 1, 2], [5, 3, 1], [5, 0, 3]]
|
||||
|
||||
# Dodecahedron
|
||||
elif plato == "12":
|
||||
# Calculate the necessary constants
|
||||
s = 1 / sqrt(3)
|
||||
t = sqrt((3 - sqrt(5)) / 6)
|
||||
u = sqrt((3 + sqrt(5)) / 6)
|
||||
|
||||
# create the vertices and faces
|
||||
v = [(s, s, s), (s, s, -s), (s, -s, s), (s, -s, -s), (-s, s, s), (-s, s, -s), (-s, -s, s), (-s, -s, -s),
|
||||
(t, u, 0), (-t, u, 0), (t, -u, 0), (-t, -u, 0), (u, 0, t), (u, 0, -t), (-u, 0, t), (-u, 0, -t), (0, t, u),
|
||||
(0, -t, u), (0, t, -u), (0, -t, -u)]
|
||||
faces = [[0, 8, 9, 4, 16], [0, 12, 13, 1, 8], [0, 16, 17, 2, 12], [8, 1, 18, 5, 9], [12, 2, 10, 3, 13],
|
||||
[16, 4, 14, 6, 17], [9, 5, 15, 14, 4], [6, 11, 10, 2, 17], [3, 19, 18, 1, 13], [7, 15, 5, 18, 19],
|
||||
[7, 11, 6, 14, 15], [7, 19, 3, 10, 11]]
|
||||
|
||||
# Icosahedron
|
||||
elif plato == "20":
|
||||
# Calculate the necessary constants
|
||||
s = (1 + sqrt(5)) / 2
|
||||
t = sqrt(1 + s * s)
|
||||
s = s / t
|
||||
t = 1 / t
|
||||
|
||||
# create the vertices and faces
|
||||
v = [(s, t, 0), (-s, t, 0), (s, -t, 0), (-s, -t, 0), (t, 0, s), (t, 0, -s), (-t, 0, s), (-t, 0, -s),
|
||||
(0, s, t), (0, -s, t), (0, s, -t), (0, -s, -t)]
|
||||
faces = [[0, 8, 4], [0, 5, 10], [2, 4, 9], [2, 11, 5], [1, 6, 8], [1, 10, 7], [3, 9, 6], [3, 7, 11],
|
||||
[0, 10, 8], [1, 8, 10], [2, 9, 11], [3, 11, 9], [4, 2, 0], [5, 0, 2], [6, 1, 3], [7, 3, 1],
|
||||
[8, 6, 4], [9, 4, 6], [10, 5, 7], [11, 7, 5]]
|
||||
|
||||
# convert the tuples to Vectors
|
||||
verts = [Vector(i) for i in v]
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
# processes the raw data from source
|
||||
|
||||
def createSolid(plato, vtrunc, etrunc, dual, snub):
|
||||
# the duals from each platonic solid
|
||||
dualSource = {"4": "4",
|
||||
"6": "8",
|
||||
"8": "6",
|
||||
"12": "20",
|
||||
"20": "12"}
|
||||
|
||||
# constants saving space and readability
|
||||
vtrunc *= 0.5
|
||||
etrunc *= 0.5
|
||||
supposedSize = 0
|
||||
noSnub = (snub == "None") or (etrunc == 0.5) or (etrunc == 0)
|
||||
lSnub = (snub == "Left") and (0 < etrunc < 0.5)
|
||||
rSnub = (snub == "Right") and (0 < etrunc < 0.5)
|
||||
|
||||
# no truncation
|
||||
if vtrunc == 0:
|
||||
if dual: # dual is as simple as another, but mirrored platonic solid
|
||||
vInput, fInput = source(dualSource[plato])
|
||||
supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0])
|
||||
vInput = [-i * supposedSize for i in vInput] # mirror it
|
||||
# Inverting vInput turns the mesh inside-out, so normals need to be flipped.
|
||||
return vInput, flippedFaceNormals(fInput)
|
||||
return source(plato)
|
||||
elif 0 < vtrunc <= 0.5: # simple truncation of the source
|
||||
vInput, fInput = source(plato)
|
||||
else:
|
||||
# truncation is now equal to simple truncation of the dual of the source
|
||||
vInput, fInput = source(dualSource[plato])
|
||||
supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0])
|
||||
vtrunc = 1 - vtrunc # account for the source being a dual
|
||||
if vtrunc == 0: # no truncation needed
|
||||
if dual:
|
||||
vInput, fInput = source(plato)
|
||||
vInput = [-i * supposedSize for i in vInput]
|
||||
# Inverting vInput turns the mesh inside-out, so normals need to be flipped.
|
||||
return vInput, flippedFaceNormals(fInput)
|
||||
|
||||
# generate connection database
|
||||
vDict = [{} for i in vInput]
|
||||
# for every face, store what vertex comes after and before the current vertex
|
||||
for x in range(len(fInput)):
|
||||
i = fInput[x]
|
||||
for j in range(len(i)):
|
||||
vDict[i[j - 1]][i[j]] = [i[j - 2], x]
|
||||
if len(vDict[i[j - 1]]) == 1:
|
||||
vDict[i[j - 1]][-1] = i[j]
|
||||
|
||||
# the actual connection database: exists out of:
|
||||
# [vtrunc pos, etrunc pos, connected vert IDs, connected face IDs]
|
||||
vData = [[[], [], [], []] for i in vInput]
|
||||
fvOutput = [] # faces created from truncated vertices
|
||||
feOutput = [] # faces created from truncated edges
|
||||
vOutput = [] # newly created vertices
|
||||
for x in range(len(vInput)):
|
||||
i = vDict[x] # lookup the current vertex
|
||||
current = i[-1]
|
||||
while True: # follow the chain to get a ccw order of connected verts and faces
|
||||
vData[x][2].append(i[current][0])
|
||||
vData[x][3].append(i[current][1])
|
||||
# create truncated vertices
|
||||
vData[x][0].append((1 - vtrunc) * vInput[x] + vtrunc * vInput[vData[x][2][-1]])
|
||||
current = i[current][0]
|
||||
if current == i[-1]:
|
||||
break # if we're back at the first: stop the loop
|
||||
fvOutput.append([]) # new face from truncated vert
|
||||
fOffset = x * (len(i) - 1) # where to start off counting faceVerts
|
||||
# only create one vert where one is needed (v1 todo: done)
|
||||
if etrunc == 0.5:
|
||||
for j in range(len(i) - 1):
|
||||
vOutput.append((vData[x][0][j] + vData[x][0][j - 1]) * etrunc) # create vert
|
||||
fvOutput[x].append(fOffset + j) # add to face
|
||||
fvOutput[x] = fvOutput[x][1:] + [fvOutput[x][0]] # rotate face for ease later on
|
||||
# create faces from truncated edges.
|
||||
for j in range(len(i) - 1):
|
||||
if x > vData[x][2][j]: # only create when other vertex has been added
|
||||
index = vData[vData[x][2][j]][2].index(x)
|
||||
feOutput.append([fvOutput[x][j], fvOutput[x][j - 1],
|
||||
fvOutput[vData[x][2][j]][index],
|
||||
fvOutput[vData[x][2][j]][index - 1]])
|
||||
# edge truncation between none and full
|
||||
elif etrunc > 0:
|
||||
for j in range(len(i) - 1):
|
||||
# create snubs from selecting verts from rectified meshes
|
||||
if rSnub:
|
||||
vOutput.append(etrunc * vData[x][0][j] + (1 - etrunc) * vData[x][0][j - 1])
|
||||
fvOutput[x].append(fOffset + j)
|
||||
elif lSnub:
|
||||
vOutput.append((1 - etrunc) * vData[x][0][j] + etrunc * vData[x][0][j - 1])
|
||||
fvOutput[x].append(fOffset + j)
|
||||
else: # noSnub, select both verts from rectified mesh
|
||||
vOutput.append(etrunc * vData[x][0][j] + (1 - etrunc) * vData[x][0][j - 1])
|
||||
vOutput.append((1 - etrunc) * vData[x][0][j] + etrunc * vData[x][0][j - 1])
|
||||
fvOutput[x].append(2 * fOffset + 2 * j)
|
||||
fvOutput[x].append(2 * fOffset + 2 * j + 1)
|
||||
# rotate face for ease later on
|
||||
if noSnub:
|
||||
fvOutput[x] = fvOutput[x][2:] + fvOutput[x][:2]
|
||||
else:
|
||||
fvOutput[x] = fvOutput[x][1:] + [fvOutput[x][0]]
|
||||
# create single face for each edge
|
||||
if noSnub:
|
||||
for j in range(len(i) - 1):
|
||||
if x > vData[x][2][j]:
|
||||
index = vData[vData[x][2][j]][2].index(x)
|
||||
feOutput.append([fvOutput[x][j * 2], fvOutput[x][2 * j - 1],
|
||||
fvOutput[vData[x][2][j]][2 * index],
|
||||
fvOutput[vData[x][2][j]][2 * index - 1]])
|
||||
# create 2 tri's for each edge for the snubs
|
||||
elif rSnub:
|
||||
for j in range(len(i) - 1):
|
||||
if x > vData[x][2][j]:
|
||||
index = vData[vData[x][2][j]][2].index(x)
|
||||
feOutput.append([fvOutput[x][j], fvOutput[x][j - 1],
|
||||
fvOutput[vData[x][2][j]][index]])
|
||||
feOutput.append([fvOutput[x][j], fvOutput[vData[x][2][j]][index],
|
||||
fvOutput[vData[x][2][j]][index - 1]])
|
||||
elif lSnub:
|
||||
for j in range(len(i) - 1):
|
||||
if x > vData[x][2][j]:
|
||||
index = vData[vData[x][2][j]][2].index(x)
|
||||
feOutput.append([fvOutput[x][j], fvOutput[x][j - 1],
|
||||
fvOutput[vData[x][2][j]][index - 1]])
|
||||
feOutput.append([fvOutput[x][j - 1], fvOutput[vData[x][2][j]][index],
|
||||
fvOutput[vData[x][2][j]][index - 1]])
|
||||
# special rules for birectified mesh (v1 todo: done)
|
||||
elif vtrunc == 0.5:
|
||||
for j in range(len(i) - 1):
|
||||
if x < vData[x][2][j]: # use current vert, since other one has not passed yet
|
||||
vOutput.append(vData[x][0][j])
|
||||
fvOutput[x].append(len(vOutput) - 1)
|
||||
else:
|
||||
# search for other edge to avoid duplicity
|
||||
connectee = vData[x][2][j]
|
||||
fvOutput[x].append(fvOutput[connectee][vData[connectee][2].index(x)])
|
||||
else: # vert truncation only
|
||||
vOutput.extend(vData[x][0]) # use generated verts from way above
|
||||
for j in range(len(i) - 1): # create face from them
|
||||
fvOutput[x].append(fOffset + j)
|
||||
|
||||
# calculate supposed vertex length to ensure continuity
|
||||
if supposedSize and not dual: # this to make the vtrunc > 1 work
|
||||
supposedSize *= len(fvOutput[0]) / vSum(vOutput[i] for i in fvOutput[0]).length
|
||||
vOutput = [-i * supposedSize for i in vOutput]
|
||||
# Inverting vOutput turns the mesh inside-out, so normals need to be flipped.
|
||||
flipNormals = True
|
||||
else:
|
||||
flipNormals = False
|
||||
|
||||
# create new faces by replacing old vert IDs by newly generated verts
|
||||
ffOutput = [[] for i in fInput]
|
||||
for x in range(len(fInput)):
|
||||
# only one generated vert per vertex, so choose accordingly
|
||||
if etrunc == 0.5 or (etrunc == 0 and vtrunc == 0.5) or lSnub or rSnub:
|
||||
ffOutput[x] = [fvOutput[i][vData[i][3].index(x) - 1] for i in fInput[x]]
|
||||
# two generated verts per vertex
|
||||
elif etrunc > 0:
|
||||
for i in fInput[x]:
|
||||
ffOutput[x].append(fvOutput[i][2 * vData[i][3].index(x) - 1])
|
||||
ffOutput[x].append(fvOutput[i][2 * vData[i][3].index(x) - 2])
|
||||
else: # cutting off corners also makes 2 verts
|
||||
for i in fInput[x]:
|
||||
ffOutput[x].append(fvOutput[i][vData[i][3].index(x)])
|
||||
ffOutput[x].append(fvOutput[i][vData[i][3].index(x) - 1])
|
||||
|
||||
if not dual:
|
||||
fOutput = fvOutput + feOutput + ffOutput
|
||||
if flipNormals:
|
||||
fOutput = flippedFaceNormals(fOutput)
|
||||
return vOutput, fOutput
|
||||
else:
|
||||
# do the same procedure as above, only now on the generated mesh
|
||||
# generate connection database
|
||||
vDict = [{} for i in vOutput]
|
||||
dvOutput = [0 for i in fvOutput + feOutput + ffOutput]
|
||||
dfOutput = []
|
||||
|
||||
for x in range(len(dvOutput)): # for every face
|
||||
i = (fvOutput + feOutput + ffOutput)[x] # choose face to work with
|
||||
# find vertex from face
|
||||
normal = (vOutput[i[0]] - vOutput[i[1]]).cross(vOutput[i[2]] - vOutput[i[1]]).normalized()
|
||||
dvOutput[x] = normal / (normal.dot(vOutput[i[0]]))
|
||||
for j in range(len(i)): # create vert chain
|
||||
vDict[i[j - 1]][i[j]] = [i[j - 2], x]
|
||||
if len(vDict[i[j - 1]]) == 1:
|
||||
vDict[i[j - 1]][-1] = i[j]
|
||||
|
||||
# calculate supposed size for continuity
|
||||
supposedSize = vSum([vInput[i] for i in fInput[0]]).length / len(fInput[0])
|
||||
supposedSize /= dvOutput[-1].length
|
||||
dvOutput = [i * supposedSize for i in dvOutput]
|
||||
|
||||
# use chains to create faces
|
||||
for x in range(len(vOutput)):
|
||||
i = vDict[x]
|
||||
current = i[-1]
|
||||
face = []
|
||||
while True:
|
||||
face.append(i[current][1])
|
||||
current = i[current][0]
|
||||
if current == i[-1]:
|
||||
break
|
||||
dfOutput.append(face)
|
||||
|
||||
return dvOutput, dfOutput
|
||||
|
||||
|
||||
class Solids(bpy.types.Operator):
|
||||
"""Add one of the (regular) solids (mesh)"""
|
||||
bl_idname = "mesh.primitive_solid_add"
|
||||
bl_label = "Add Regular Solid"
|
||||
bl_description = "Add one of the Platonic, Archimedean or Catalan solids"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
source: EnumProperty(
|
||||
items=(("4", "Tetrahedron", ""),
|
||||
("6", "Hexahedron", ""),
|
||||
("8", "Octahedron", ""),
|
||||
("12", "Dodecahedron", ""),
|
||||
("20", "Icosahedron", "")),
|
||||
name="Source",
|
||||
description="Starting point of your solid"
|
||||
)
|
||||
size: FloatProperty(
|
||||
name="Size",
|
||||
description="Radius of the sphere through the vertices",
|
||||
min=0.01,
|
||||
soft_min=0.01,
|
||||
max=100,
|
||||
soft_max=100,
|
||||
default=1.0
|
||||
)
|
||||
vTrunc: FloatProperty(
|
||||
name="Vertex Truncation",
|
||||
description="Amount of vertex truncation",
|
||||
min=0.0,
|
||||
soft_min=0.0,
|
||||
max=2.0,
|
||||
soft_max=2.0,
|
||||
default=0.0,
|
||||
precision=3,
|
||||
step=0.5
|
||||
)
|
||||
eTrunc: FloatProperty(
|
||||
name="Edge Truncation",
|
||||
description="Amount of edge truncation",
|
||||
min=0.0,
|
||||
soft_min=0.0,
|
||||
max=1.0,
|
||||
soft_max=1.0,
|
||||
default=0.0,
|
||||
precision=3,
|
||||
step=0.2
|
||||
)
|
||||
snub: EnumProperty(
|
||||
items=(("None", "No Snub", ""),
|
||||
("Left", "Left Snub", ""),
|
||||
("Right", "Right Snub", "")),
|
||||
name="Snub",
|
||||
description="Create the snub version"
|
||||
)
|
||||
dual: BoolProperty(
|
||||
name="Dual",
|
||||
description="Create the dual of the current solid",
|
||||
default=False
|
||||
)
|
||||
keepSize: BoolProperty(
|
||||
name="Keep Size",
|
||||
description="Keep the whole solid at a constant size",
|
||||
default=False
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
# generate mesh
|
||||
verts, faces = createSolid(self.source,
|
||||
self.vTrunc,
|
||||
self.eTrunc,
|
||||
self.dual,
|
||||
self.snub
|
||||
)
|
||||
|
||||
# resize to normal size, or if keepSize, make sure all verts are of length 'size'
|
||||
if self.keepSize:
|
||||
rad = self.size / verts[-1 if self.dual else 0].length
|
||||
else:
|
||||
rad = self.size
|
||||
verts = [i * rad for i in verts]
|
||||
|
||||
# generate object
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new("Solid")
|
||||
|
||||
# Make a mesh from a list of verts/edges/faces.
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
# Update mesh geometry after adding stuff.
|
||||
mesh.update()
|
||||
|
||||
object_data_add(context, mesh, operator=None)
|
||||
# object generation done
|
||||
|
||||
return {'FINISHED'}
|
||||
@@ -0,0 +1,285 @@
|
||||
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Original by Fourmadmen
|
||||
|
||||
import bpy
|
||||
from mathutils import (
|
||||
Vector,
|
||||
Quaternion,
|
||||
)
|
||||
from math import pi
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
# Create a new mesh (object) from verts/edges/faces.
|
||||
# verts/edges/faces ... List of vertices/edges/faces for the
|
||||
# new mesh (as used in from_pydata)
|
||||
# name ... Name of the new mesh (& object)
|
||||
|
||||
def create_mesh_object(context, verts, edges, faces, name):
|
||||
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
|
||||
# Make a mesh from a list of verts/edges/faces.
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Update mesh geometry after adding stuff.
|
||||
mesh.update()
|
||||
|
||||
from bpy_extras import object_utils
|
||||
return object_utils.object_data_add(context, mesh, operator=None)
|
||||
|
||||
|
||||
# A very simple "bridge" tool.
|
||||
|
||||
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
||||
faces = []
|
||||
|
||||
if not vertIdx1 or not vertIdx2:
|
||||
return None
|
||||
|
||||
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
||||
return None
|
||||
|
||||
fan = False
|
||||
if (len(vertIdx1) != len(vertIdx2)):
|
||||
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
||||
fan = True
|
||||
else:
|
||||
return None
|
||||
|
||||
total = len(vertIdx2)
|
||||
|
||||
if closed:
|
||||
# Bridge the start with the end.
|
||||
if flipped:
|
||||
face = [
|
||||
vertIdx1[0],
|
||||
vertIdx2[0],
|
||||
vertIdx2[total - 1]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
else:
|
||||
face = [vertIdx2[0], vertIdx1[0]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
face.append(vertIdx2[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
# Bridge the rest of the faces.
|
||||
for num in range(total - 1):
|
||||
if flipped:
|
||||
if fan:
|
||||
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx2[num], vertIdx1[num],
|
||||
vertIdx1[num + 1], vertIdx2[num + 1]]
|
||||
faces.append(face)
|
||||
else:
|
||||
if fan:
|
||||
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx1[num], vertIdx2[num],
|
||||
vertIdx2[num + 1], vertIdx1[num + 1]]
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
# @todo Clean up vertex&face creation process a bit.
|
||||
|
||||
def add_star(points, outer_radius, inner_radius, height):
|
||||
PI_2 = pi * 2
|
||||
z_axis = (0, 0, 1)
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
segments = points * 2
|
||||
|
||||
half_height = height / 2.0
|
||||
|
||||
vert_idx_top = len(verts)
|
||||
verts.append(Vector((0.0, 0.0, half_height)))
|
||||
|
||||
vert_idx_bottom = len(verts)
|
||||
verts.append(Vector((0.0, 0.0, -half_height)))
|
||||
|
||||
edgeloop_top = []
|
||||
edgeloop_bottom = []
|
||||
|
||||
for index in range(segments):
|
||||
quat = Quaternion(z_axis, (index / segments) * PI_2)
|
||||
|
||||
if index % 2:
|
||||
# Uneven
|
||||
radius = outer_radius
|
||||
else:
|
||||
# Even
|
||||
radius = inner_radius
|
||||
|
||||
edgeloop_top.append(len(verts))
|
||||
vec = quat @ Vector((radius, 0, half_height))
|
||||
verts.append(vec)
|
||||
|
||||
edgeloop_bottom.append(len(verts))
|
||||
vec = quat @ Vector((radius, 0, -half_height))
|
||||
verts.append(vec)
|
||||
|
||||
faces_top = createFaces([vert_idx_top], edgeloop_top, closed=True)
|
||||
faces_outside = createFaces(edgeloop_top, edgeloop_bottom, closed=True)
|
||||
faces_bottom = createFaces([vert_idx_bottom], edgeloop_bottom,
|
||||
flipped=True, closed=True)
|
||||
|
||||
faces.extend(faces_top)
|
||||
faces.extend(faces_outside)
|
||||
faces.extend(faces_bottom)
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
class AddStar(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_star_add"
|
||||
bl_label = "Simple Star"
|
||||
bl_description = "Construct a star mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
Star : BoolProperty(name = "Star",
|
||||
default = True,
|
||||
description = "Star")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change Star")
|
||||
|
||||
points: IntProperty(
|
||||
name="Points",
|
||||
description="Number of points for the star",
|
||||
min=2,
|
||||
max=256,
|
||||
default=5
|
||||
)
|
||||
outer_radius: FloatProperty(
|
||||
name="Outer Radius",
|
||||
description="Outer radius of the star",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=1.0
|
||||
)
|
||||
innter_radius: FloatProperty(
|
||||
name="Inner Radius",
|
||||
description="Inner radius of the star",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.5
|
||||
)
|
||||
height: FloatProperty(name="Height",
|
||||
description="Height of the star",
|
||||
min=0.01,
|
||||
max=9999.0,
|
||||
default=0.5
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'points')
|
||||
layout.prop(self, 'height')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'outer_radius', text='Radius Outer')
|
||||
col.prop(self, 'innter_radius', text='Inner')
|
||||
|
||||
if self.change == False:
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('Star' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
verts, faces = add_star(
|
||||
self.points,
|
||||
self.outer_radius,
|
||||
self.innter_radius,
|
||||
self.height
|
||||
)
|
||||
mesh = bpy.data.meshes.new('Star')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = add_star(
|
||||
self.points,
|
||||
self.outer_radius,
|
||||
self.innter_radius,
|
||||
self.height
|
||||
)
|
||||
mesh = bpy.data.meshes.new('Star')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["Star"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in StarParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = add_star(
|
||||
self.points,
|
||||
self.outer_radius,
|
||||
self.innter_radius,
|
||||
self.height
|
||||
)
|
||||
mesh = bpy.data.meshes.new('Star')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def StarParameters():
|
||||
StarParameters = [
|
||||
"points",
|
||||
"outer_radius",
|
||||
"innter_radius",
|
||||
"height",
|
||||
]
|
||||
return StarParameters
|
||||
@@ -0,0 +1,331 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: DreamPainter
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
from math import pi, cos, sin
|
||||
from mathutils import Vector
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
# A very simple "bridge" tool
|
||||
|
||||
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
||||
faces = []
|
||||
|
||||
if not vertIdx1 or not vertIdx2:
|
||||
return None
|
||||
|
||||
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
||||
return None
|
||||
|
||||
fan = False
|
||||
if (len(vertIdx1) != len(vertIdx2)):
|
||||
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
||||
fan = True
|
||||
else:
|
||||
return None
|
||||
|
||||
total = len(vertIdx2)
|
||||
|
||||
if closed:
|
||||
# Bridge the start with the end.
|
||||
if flipped:
|
||||
face = [
|
||||
vertIdx1[0],
|
||||
vertIdx2[0],
|
||||
vertIdx2[total - 1]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
else:
|
||||
face = [vertIdx2[0], vertIdx1[0]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
face.append(vertIdx2[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
# Bridge the rest of the faces.
|
||||
for num in range(total - 1):
|
||||
if flipped:
|
||||
if fan:
|
||||
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx2[num], vertIdx1[num],
|
||||
vertIdx1[num + 1], vertIdx2[num + 1]]
|
||||
faces.append(face)
|
||||
else:
|
||||
if fan:
|
||||
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx1[num], vertIdx2[num],
|
||||
vertIdx2[num + 1], vertIdx1[num + 1]]
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
def power(a, b):
|
||||
if a < 0:
|
||||
return -((-a) ** b)
|
||||
return a ** b
|
||||
|
||||
|
||||
def supertoroid(R, r, u, v, n1, n2):
|
||||
"""
|
||||
R = big radius
|
||||
r = small radius
|
||||
u = lateral segmentation
|
||||
v = radial segmentation
|
||||
n1 = value determines the shape of the torus
|
||||
n2 = value determines the shape of the cross-section
|
||||
"""
|
||||
# create the necessary constants
|
||||
a = 2 * pi / u
|
||||
b = 2 * pi / v
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
# create each cross-section by calculating each vector on the
|
||||
# the wannabe circle
|
||||
# x = (cos(theta) ** n1)*(R + r * (cos(phi) ** n2))
|
||||
# y = (sin(theta) ** n1)*(R + r * (cos(phi) ** n2))
|
||||
# z = (r * sin(phi) ** n2)
|
||||
# with theta and phi ranging from 0 to 2pi
|
||||
|
||||
for i in range(u):
|
||||
s = power(sin(i * a), n1)
|
||||
c = power(cos(i * a), n1)
|
||||
for j in range(v):
|
||||
c2 = R + r * power(cos(j * b), n2)
|
||||
s2 = r * power(sin(j * b), n2)
|
||||
verts.append(Vector((c * c2, s * c2, s2)))
|
||||
|
||||
# bridge the last circle with the previous circle
|
||||
if i > 0: # but not for the first circle, 'cus there's no previous before the first
|
||||
f = createFaces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True)
|
||||
faces.extend(f)
|
||||
# bridge the last circle with the first
|
||||
f = createFaces(range((u - 1) * v, u * v), range(v), closed=True)
|
||||
faces.extend(f)
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
class add_supertoroid(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_supertoroid_add"
|
||||
bl_label = "Add SuperToroid"
|
||||
bl_description = "Construct a supertoroid mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
SuperToroid : BoolProperty(name = "SuperToroid",
|
||||
default = True,
|
||||
description = "SuperToroid")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change SuperToroid")
|
||||
|
||||
method: EnumProperty(
|
||||
name='Method',
|
||||
description='Method for determining the size and thickness of the torus',
|
||||
items=(
|
||||
('MAJOR-MINOR', 'Major / Minor', 'Uses the major radius for the overall size and the minor for the thickness'),
|
||||
('INT-EXT', 'Interior / Exterior', 'Uses the absolute size of the inner and outer circles to determine the size and thickness'),
|
||||
),
|
||||
default='MAJOR-MINOR',
|
||||
)
|
||||
R: FloatProperty(
|
||||
name="Big radius",
|
||||
description="The radius inside the tube",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
r: FloatProperty(
|
||||
name="Small radius",
|
||||
description="The radius of the tube",
|
||||
default=0.3,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
outer_r: FloatProperty(
|
||||
name="Exterior Radius",
|
||||
description="Total Exterior Radius of the torus",
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
default=1.3
|
||||
)
|
||||
inner_r: FloatProperty(
|
||||
name="Inside Radius",
|
||||
description="Total Interior Radius of the torus",
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
default=0.7
|
||||
)
|
||||
u: IntProperty(
|
||||
name="U-segments",
|
||||
description="Radial segmentation",
|
||||
default=16,
|
||||
min=3, max=265
|
||||
)
|
||||
v: IntProperty(
|
||||
name="V-segments",
|
||||
description="Lateral segmentation",
|
||||
default=8,
|
||||
min=3, max=265
|
||||
)
|
||||
n1: FloatProperty(
|
||||
name="Ring manipulator",
|
||||
description="Manipulates the shape of the Ring",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
n2: FloatProperty(
|
||||
name="Cross manipulator",
|
||||
description="Manipulates the shape of the cross-section",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0
|
||||
)
|
||||
edit: BoolProperty(
|
||||
name="",
|
||||
description="",
|
||||
default=False,
|
||||
options={'HIDDEN'}
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'method', text='Dimensions Mode')
|
||||
col = layout.column(align=True)
|
||||
if self.method == 'MAJOR-MINOR':
|
||||
col.prop(self, 'R', text='Radius Major')
|
||||
col.prop(self, 'r', text='Minor')
|
||||
else:
|
||||
col.prop(self, 'outer_r', text='Radius Exterior')
|
||||
col.prop(self, 'inner_r', text='Interior')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'u', text='Segments Major')
|
||||
col.prop(self, 'v', text='Minor')
|
||||
layout.prop(self, 'n1', text='Ring')
|
||||
layout.prop(self, 'n2', text='Cross')
|
||||
|
||||
if self.change == False:
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
props = self.properties
|
||||
|
||||
# check how the radii properties must be used
|
||||
if props.method == 'INT-EXT':
|
||||
rad1 = (props.outer_r + props.inner_r) / 2
|
||||
rad2 = (props.outer_r - props.inner_r) / 2
|
||||
# for consistency in the mesh, ie no crossing faces, make the largest of the two
|
||||
# the outer radius
|
||||
if rad2 > rad1:
|
||||
[rad1, rad2] = [rad2, rad1]
|
||||
else:
|
||||
rad1 = props.R
|
||||
rad2 = props.r
|
||||
# again for consistency, make the radius in the tube,
|
||||
# at least as big as the radius of the tube
|
||||
if rad2 > rad1:
|
||||
rad1 = rad2
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('SuperToroid' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
verts, faces = supertoroid(rad1,
|
||||
rad2,
|
||||
props.u,
|
||||
props.v,
|
||||
props.n1,
|
||||
props.n2
|
||||
)
|
||||
mesh = bpy.data.meshes.new('SuperToroid')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = supertoroid(rad1,
|
||||
rad2,
|
||||
props.u,
|
||||
props.v,
|
||||
props.n1,
|
||||
props.n2
|
||||
)
|
||||
mesh = bpy.data.meshes.new('SuperToroid')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["SuperToroid"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in SuperToroidParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = supertoroid(rad1,
|
||||
rad2,
|
||||
props.u,
|
||||
props.v,
|
||||
props.n1,
|
||||
props.n2
|
||||
)
|
||||
mesh = bpy.data.meshes.new('SuperToroid')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def SuperToroidParameters():
|
||||
SuperToroidParameters = [
|
||||
"R",
|
||||
"r",
|
||||
"u",
|
||||
"v",
|
||||
"n1",
|
||||
"n2",
|
||||
"method",
|
||||
"edit",
|
||||
"inner_r",
|
||||
"outer_r",
|
||||
]
|
||||
return SuperToroidParameters
|
||||
@@ -0,0 +1,865 @@
|
||||
# SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author, Anthony D'Agostino
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
import mathutils
|
||||
import io
|
||||
import operator
|
||||
import functools
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
class AddTeapot(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_teapot_add"
|
||||
bl_label = "Add Teapot"
|
||||
bl_description = "Construct a teapot or teaspoon mesh"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
resolution: IntProperty(
|
||||
name="Resolution",
|
||||
description="Resolution of the Teapot",
|
||||
default=5,
|
||||
min=2, max=15,
|
||||
)
|
||||
objecttype: EnumProperty(
|
||||
name="Type",
|
||||
description="Type of Bezier Object",
|
||||
items=(('1', "Teapot", "Construct a teapot mesh"),
|
||||
('2', "Tea Spoon", "Construct a teaspoon mesh")),
|
||||
default='1',
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.prop(self, 'objecttype')
|
||||
layout.prop(self, 'resolution')
|
||||
layout.separator()
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
cmode = bpy.context.mode
|
||||
verts, faces = make_teapot(self.objecttype,
|
||||
self.resolution)
|
||||
# Actually create the mesh object from this geometry data.
|
||||
obj = create_mesh_object(self, context, verts, [], faces, "Teapot")
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.remove_doubles()
|
||||
if cmode != "EDIT_MESH":
|
||||
bpy.ops.object.mode_set(mode=cmode)
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def create_mesh_face_hack(faces):
|
||||
# FIXME, faces with duplicate vertices shouldn't be created in the first place.
|
||||
faces_copy = []
|
||||
for f in faces:
|
||||
f_copy = []
|
||||
for i in f:
|
||||
if i not in f_copy:
|
||||
f_copy.append(i)
|
||||
faces_copy.append(f_copy)
|
||||
faces[:] = faces_copy
|
||||
|
||||
|
||||
def create_mesh_object(self, context, verts, edges, faces, name):
|
||||
|
||||
create_mesh_face_hack(faces)
|
||||
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
# Make a mesh from a list of verts/edges/faces.
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
# Update mesh geometry after adding stuff.
|
||||
mesh.update()
|
||||
|
||||
return object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
|
||||
# ==========================
|
||||
# === Bezier patch Block ===
|
||||
# ==========================
|
||||
|
||||
def read_indexed_patch_file(filename):
|
||||
file = io.StringIO(filename)
|
||||
rawpatches = []
|
||||
patches = []
|
||||
numpatches = int(file.readline())
|
||||
for i in range(numpatches):
|
||||
line = file.readline()
|
||||
(a, b, c, d,
|
||||
e, f, g, h,
|
||||
i, j, k, l,
|
||||
m, n, o, p,
|
||||
) = map(int, line.split(","))
|
||||
patches.append([[a, b, c, d], [e, f, g, h], [i, j, k, l], [m, n, o, p]])
|
||||
rawpatches.append([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
|
||||
verts = []
|
||||
numverts = int(file.readline())
|
||||
for i in range(numverts):
|
||||
line = file.readline()
|
||||
v1, v2, v3 = map(float, line.split(","))
|
||||
verts.append((v1, v2, v3))
|
||||
for i in range(len(patches)):
|
||||
for j in range(4): # len(patches[i])):
|
||||
for k in range(4): # len(patches[i][j])):
|
||||
index = patches[i][j][k] - 1
|
||||
rawpatches[i][j][k] = verts[index]
|
||||
return rawpatches
|
||||
|
||||
|
||||
def patches_to_raw(patches, resolution):
|
||||
raw = []
|
||||
for patch in patches:
|
||||
verts = make_verts(patch, resolution)
|
||||
faces = make_faces(resolution)
|
||||
rawquads = indexed_to_rawquads(verts, faces)
|
||||
raw.append(rawquads)
|
||||
raw = functools.reduce(operator.add, raw) # flatten the list
|
||||
return raw
|
||||
|
||||
|
||||
def make_bezier(ctrlpnts, resolution):
|
||||
|
||||
def b1(t):
|
||||
return t * t * t
|
||||
|
||||
def b2(t):
|
||||
return 3.0 * t * t * (1.0 - t)
|
||||
|
||||
def b3(t):
|
||||
return 3.0 * t * (1.0 - t) * (1.0 - t)
|
||||
|
||||
def b4(t):
|
||||
return (1.0 - t) * (1.0 - t) * (1.0 - t)
|
||||
|
||||
p1, p2, p3, p4 = map(mathutils.Vector, ctrlpnts)
|
||||
|
||||
def makevert(t):
|
||||
x, y, z = b1(t) * p1 + b2(t) * p2 + b3(t) * p3 + b4(t) * p4
|
||||
return (x, y, z)
|
||||
curveverts = [makevert(i / resolution) for i in range(resolution + 1)]
|
||||
return curveverts
|
||||
|
||||
|
||||
def make_verts(a, resolution):
|
||||
s = []
|
||||
for i in a:
|
||||
c = make_bezier(i, resolution)
|
||||
s.append(c)
|
||||
b = transpose(s)
|
||||
s = []
|
||||
for i in b:
|
||||
c = make_bezier(i, resolution)
|
||||
s.append(c)
|
||||
verts = s
|
||||
verts = functools.reduce(operator.add, verts) # flatten the list
|
||||
return verts
|
||||
|
||||
|
||||
def make_faces(resolution):
|
||||
n = resolution + 1
|
||||
faces = []
|
||||
for i in range(resolution):
|
||||
for j in range(resolution):
|
||||
v1 = (i + 1) * n + j
|
||||
v2 = (i + 1) * n + j + 1
|
||||
v3 = i * n + j + 1
|
||||
v4 = i * n + j
|
||||
faces.append([v1, v2, v3, v4])
|
||||
return faces
|
||||
|
||||
|
||||
def indexed_to_rawquads(verts, faces):
|
||||
rows = len(faces)
|
||||
cols = len(faces[0]) # or 4
|
||||
rawquads = [[None] * cols for i in range(rows)]
|
||||
for i in range(rows):
|
||||
for j in range(cols):
|
||||
index = faces[i][j]
|
||||
rawquads[i][j] = verts[index]
|
||||
return rawquads
|
||||
|
||||
|
||||
def raw_to_indexed(rawfaces):
|
||||
# Generate verts and faces lists, without dups
|
||||
verts = []
|
||||
coords = {}
|
||||
index = 0
|
||||
for i in range(len(rawfaces)):
|
||||
for j in range(len(rawfaces[i])):
|
||||
vertex = rawfaces[i][j]
|
||||
if vertex not in coords:
|
||||
coords[vertex] = index
|
||||
index += 1
|
||||
verts.append(vertex)
|
||||
rawfaces[i][j] = coords[vertex]
|
||||
return verts, rawfaces
|
||||
|
||||
|
||||
def transpose(rowsbycols):
|
||||
rows = len(rowsbycols)
|
||||
cols = len(rowsbycols[0])
|
||||
colsbyrows = [[None] * rows for i in range(cols)]
|
||||
for i in range(cols):
|
||||
for j in range(rows):
|
||||
colsbyrows[i][j] = rowsbycols[j][i]
|
||||
return colsbyrows
|
||||
|
||||
|
||||
def make_teapot(enumname, resolution):
|
||||
filenames = [None, teapot, teaspoon]
|
||||
try:
|
||||
indexes = int(enumname)
|
||||
filename = filenames[indexes]
|
||||
except:
|
||||
print("Add Teapot Error: EnumProperty could not be set")
|
||||
filename = filenames[1]
|
||||
|
||||
patches = read_indexed_patch_file(filename)
|
||||
raw = patches_to_raw(patches, resolution)
|
||||
verts, faces = raw_to_indexed(raw)
|
||||
return (verts, faces)
|
||||
|
||||
|
||||
# =================================
|
||||
# === Indexed Bezier Data Block ===
|
||||
# =================================
|
||||
teapot = """32
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
||||
4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28
|
||||
19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40
|
||||
31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13
|
||||
13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60
|
||||
16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69
|
||||
28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78
|
||||
40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57
|
||||
57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96
|
||||
60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105
|
||||
69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114
|
||||
78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93
|
||||
121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136
|
||||
124,137,138,121,128,139,140,125,132,141,142,129,136,143,144,133
|
||||
133,134,135,136,145,146,147,148,149,150,151,152,69,153,154,155
|
||||
136,143,144,133,148,156,157,145,152,158,159,149,155,160,161,69
|
||||
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177
|
||||
165,178,179,162,169,180,181,166,173,182,183,170,177,184,185,174
|
||||
174,175,176,177,186,187,188,189,190,191,192,193,194,195,196,197
|
||||
177,184,185,174,189,198,199,186,193,200,201,190,197,202,203,194
|
||||
204,204,204,204,207,208,209,210,211,211,211,211,212,213,214,215
|
||||
204,204,204,204,210,217,218,219,211,211,211,211,215,220,221,222
|
||||
204,204,204,204,219,224,225,226,211,211,211,211,222,227,228,229
|
||||
204,204,204,204,226,230,231,207,211,211,211,211,229,232,233,212
|
||||
212,213,214,215,234,235,236,237,238,239,240,241,242,243,244,245
|
||||
215,220,221,222,237,246,247,248,241,249,250,251,245,252,253,254
|
||||
222,227,228,229,248,255,256,257,251,258,259,260,254,261,262,263
|
||||
229,232,233,212,257,264,265,234,260,266,267,238,263,268,269,242
|
||||
270,270,270,270,279,280,281,282,275,276,277,278,271,272,273,274
|
||||
270,270,270,270,282,289,290,291,278,286,287,288,274,283,284,285
|
||||
270,270,270,270,291,298,299,300,288,295,296,297,285,292,293,294
|
||||
270,270,270,270,300,305,306,279,297,303,304,275,294,301,302,271
|
||||
306
|
||||
1.4,0.0,2.4
|
||||
1.4,-0.784,2.4
|
||||
0.784,-1.4,2.4
|
||||
0.0,-1.4,2.4
|
||||
1.3375,0.0,2.53125
|
||||
1.3375,-0.749,2.53125
|
||||
0.749,-1.3375,2.53125
|
||||
0.0,-1.3375,2.53125
|
||||
1.4375,0.0,2.53125
|
||||
1.4375,-0.805,2.53125
|
||||
0.805,-1.4375,2.53125
|
||||
0.0,-1.4375,2.53125
|
||||
1.5,0.0,2.4
|
||||
1.5,-0.84,2.4
|
||||
0.84,-1.5,2.4
|
||||
0.0,-1.5,2.4
|
||||
-0.784,-1.4,2.4
|
||||
-1.4,-0.784,2.4
|
||||
-1.4,0.0,2.4
|
||||
-0.749,-1.3375,2.53125
|
||||
-1.3375,-0.749,2.53125
|
||||
-1.3375,0.0,2.53125
|
||||
-0.805,-1.4375,2.53125
|
||||
-1.4375,-0.805,2.53125
|
||||
-1.4375,0.0,2.53125
|
||||
-0.84,-1.5,2.4
|
||||
-1.5,-0.84,2.4
|
||||
-1.5,0.0,2.4
|
||||
-1.4,0.784,2.4
|
||||
-0.784,1.4,2.4
|
||||
0.0,1.4,2.4
|
||||
-1.3375,0.749,2.53125
|
||||
-0.749,1.3375,2.53125
|
||||
0.0,1.3375,2.53125
|
||||
-1.4375,0.805,2.53125
|
||||
-0.805,1.4375,2.53125
|
||||
0.0,1.4375,2.53125
|
||||
-1.5,0.84,2.4
|
||||
-0.84,1.5,2.4
|
||||
0.0,1.5,2.4
|
||||
0.784,1.4,2.4
|
||||
1.4,0.784,2.4
|
||||
0.749,1.3375,2.53125
|
||||
1.3375,0.749,2.53125
|
||||
0.805,1.4375,2.53125
|
||||
1.4375,0.805,2.53125
|
||||
0.84,1.5,2.4
|
||||
1.5,0.84,2.4
|
||||
1.75,0.0,1.875
|
||||
1.75,-0.98,1.875
|
||||
0.98,-1.75,1.875
|
||||
0.0,-1.75,1.875
|
||||
2.0,0.0,1.35
|
||||
2.0,-1.12,1.35
|
||||
1.12,-2.0,1.35
|
||||
0.0,-2.0,1.35
|
||||
2.0,0.0,0.9
|
||||
2.0,-1.12,0.9
|
||||
1.12,-2.0,0.9
|
||||
0.0,-2.0,0.9
|
||||
-0.98,-1.75,1.875
|
||||
-1.75,-0.98,1.875
|
||||
-1.75,0.0,1.875
|
||||
-1.12,-2.0,1.35
|
||||
-2.0,-1.12,1.35
|
||||
-2.0,0.0,1.35
|
||||
-1.12,-2.0,0.9
|
||||
-2.0,-1.12,0.9
|
||||
-2.0,0.0,0.9
|
||||
-1.75,0.98,1.875
|
||||
-0.98,1.75,1.875
|
||||
0.0,1.75,1.875
|
||||
-2.0,1.12,1.35
|
||||
-1.12,2.0,1.35
|
||||
0.0,2.0,1.35
|
||||
-2.0,1.12,0.9
|
||||
-1.12,2.0,0.9
|
||||
0.0,2.0,0.9
|
||||
0.98,1.75,1.875
|
||||
1.75,0.98,1.875
|
||||
1.12,2.0,1.35
|
||||
2.0,1.12,1.35
|
||||
1.12,2.0,0.9
|
||||
2.0,1.12,0.9
|
||||
2.0,0.0,0.45
|
||||
2.0,-1.12,0.45
|
||||
1.12,-2.0,0.45
|
||||
0.0,-2.0,0.45
|
||||
1.5,0.0,0.225
|
||||
1.5,-0.84,0.225
|
||||
0.84,-1.5,0.225
|
||||
0.0,-1.5,0.225
|
||||
1.5,0.0,0.15
|
||||
1.5,-0.84,0.15
|
||||
0.84,-1.5,0.15
|
||||
0.0,-1.5,0.15
|
||||
-1.12,-2.0,0.45
|
||||
-2.0,-1.12,0.45
|
||||
-2.0,0.0,0.45
|
||||
-0.84,-1.5,0.225
|
||||
-1.5,-0.84,0.225
|
||||
-1.5,0.0,0.225
|
||||
-0.84,-1.5,0.15
|
||||
-1.5,-0.84,0.15
|
||||
-1.5,0.0,0.15
|
||||
-2.0,1.12,0.45
|
||||
-1.12,2.0,0.45
|
||||
0.0,2.0,0.45
|
||||
-1.5,0.84,0.225
|
||||
-0.84,1.5,0.225
|
||||
0.0,1.5,0.225
|
||||
-1.5,0.84,0.15
|
||||
-0.84,1.5,0.15
|
||||
0.0,1.5,0.15
|
||||
1.12,2.0,0.45
|
||||
2.0,1.12,0.45
|
||||
0.84,1.5,0.225
|
||||
1.5,0.84,0.225
|
||||
0.84,1.5,0.15
|
||||
1.5,0.84,0.15
|
||||
-1.6,0.0,2.025
|
||||
-1.6,-0.3,2.025
|
||||
-1.5,-0.3,2.25
|
||||
-1.5,0.0,2.25
|
||||
-2.3,0.0,2.025
|
||||
-2.3,-0.3,2.025
|
||||
-2.5,-0.3,2.25
|
||||
-2.5,0.0,2.25
|
||||
-2.7,0.0,2.025
|
||||
-2.7,-0.3,2.025
|
||||
-3.0,-0.3,2.25
|
||||
-3.0,0.0,2.25
|
||||
-2.7,0.0,1.8
|
||||
-2.7,-0.3,1.8
|
||||
-3.0,-0.3,1.8
|
||||
-3.0,0.0,1.8
|
||||
-1.5,0.3,2.25
|
||||
-1.6,0.3,2.025
|
||||
-2.5,0.3,2.25
|
||||
-2.3,0.3,2.025
|
||||
-3.0,0.3,2.25
|
||||
-2.7,0.3,2.025
|
||||
-3.0,0.3,1.8
|
||||
-2.7,0.3,1.8
|
||||
-2.7,0.0,1.575
|
||||
-2.7,-0.3,1.575
|
||||
-3.0,-0.3,1.35
|
||||
-3.0,0.0,1.35
|
||||
-2.5,0.0,1.125
|
||||
-2.5,-0.3,1.125
|
||||
-2.65,-0.3,0.9375
|
||||
-2.65,0.0,0.9375
|
||||
-2.0,-0.3,0.9
|
||||
-1.9,-0.3,0.6
|
||||
-1.9,0.0,0.6
|
||||
-3.0,0.3,1.35
|
||||
-2.7,0.3,1.575
|
||||
-2.65,0.3,0.9375
|
||||
-2.5,0.3,1.125
|
||||
-1.9,0.3,0.6
|
||||
-2.0,0.3,0.9
|
||||
1.7,0.0,1.425
|
||||
1.7,-0.66,1.425
|
||||
1.7,-0.66,0.6
|
||||
1.7,0.0,0.6
|
||||
2.6,0.0,1.425
|
||||
2.6,-0.66,1.425
|
||||
3.1,-0.66,0.825
|
||||
3.1,0.0,0.825
|
||||
2.3,0.0,2.1
|
||||
2.3,-0.25,2.1
|
||||
2.4,-0.25,2.025
|
||||
2.4,0.0,2.025
|
||||
2.7,0.0,2.4
|
||||
2.7,-0.25,2.4
|
||||
3.3,-0.25,2.4
|
||||
3.3,0.0,2.4
|
||||
1.7,0.66,0.6
|
||||
1.7,0.66,1.425
|
||||
3.1,0.66,0.825
|
||||
2.6,0.66,1.425
|
||||
2.4,0.25,2.025
|
||||
2.3,0.25,2.1
|
||||
3.3,0.25,2.4
|
||||
2.7,0.25,2.4
|
||||
2.8,0.0,2.475
|
||||
2.8,-0.25,2.475
|
||||
3.525,-0.25,2.49375
|
||||
3.525,0.0,2.49375
|
||||
2.9,0.0,2.475
|
||||
2.9,-0.15,2.475
|
||||
3.45,-0.15,2.5125
|
||||
3.45,0.0,2.5125
|
||||
2.8,0.0,2.4
|
||||
2.8,-0.15,2.4
|
||||
3.2,-0.15,2.4
|
||||
3.2,0.0,2.4
|
||||
3.525,0.25,2.49375
|
||||
2.8,0.25,2.475
|
||||
3.45,0.15,2.5125
|
||||
2.9,0.15,2.475
|
||||
3.2,0.15,2.4
|
||||
2.8,0.15,2.4
|
||||
0.0,0.0,3.15
|
||||
0.0,-0.002,3.15
|
||||
0.002,0.0,3.15
|
||||
0.8,0.0,3.15
|
||||
0.8,-0.45,3.15
|
||||
0.45,-0.8,3.15
|
||||
0.0,-0.8,3.15
|
||||
0.0,0.0,2.85
|
||||
0.2,0.0,2.7
|
||||
0.2,-0.112,2.7
|
||||
0.112,-0.2,2.7
|
||||
0.0,-0.2,2.7
|
||||
-0.002,0.0,3.15
|
||||
-0.45,-0.8,3.15
|
||||
-0.8,-0.45,3.15
|
||||
-0.8,0.0,3.15
|
||||
-0.112,-0.2,2.7
|
||||
-0.2,-0.112,2.7
|
||||
-0.2,0.0,2.7
|
||||
0.0,0.002,3.15
|
||||
-0.8,0.45,3.15
|
||||
-0.45,0.8,3.15
|
||||
0.0,0.8,3.15
|
||||
-0.2,0.112,2.7
|
||||
-0.112,0.2,2.7
|
||||
0.0,0.2,2.7
|
||||
0.45,0.8,3.15
|
||||
0.8,0.45,3.15
|
||||
0.112,0.2,2.7
|
||||
0.2,0.112,2.7
|
||||
0.4,0.0,2.55
|
||||
0.4,-0.224,2.55
|
||||
0.224,-0.4,2.55
|
||||
0.0,-0.4,2.55
|
||||
1.3,0.0,2.55
|
||||
1.3,-0.728,2.55
|
||||
0.728,-1.3,2.55
|
||||
0.0,-1.3,2.55
|
||||
1.3,0.0,2.4
|
||||
1.3,-0.728,2.4
|
||||
0.728,-1.3,2.4
|
||||
0.0,-1.3,2.4
|
||||
-0.224,-0.4,2.55
|
||||
-0.4,-0.224,2.55
|
||||
-0.4,0.0,2.55
|
||||
-0.728,-1.3,2.55
|
||||
-1.3,-0.728,2.55
|
||||
-1.3,0.0,2.55
|
||||
-0.728,-1.3,2.4
|
||||
-1.3,-0.728,2.4
|
||||
-1.3,0.0,2.4
|
||||
-0.4,0.224,2.55
|
||||
-0.224,0.4,2.55
|
||||
0.0,0.4,2.55
|
||||
-1.3,0.728,2.55
|
||||
-0.728,1.3,2.55
|
||||
0.0,1.3,2.55
|
||||
-1.3,0.728,2.4
|
||||
-0.728,1.3,2.4
|
||||
0.0,1.3,2.4
|
||||
0.224,0.4,2.55
|
||||
0.4,0.224,2.55
|
||||
0.728,1.3,2.55
|
||||
1.3,0.728,2.55
|
||||
0.728,1.3,2.4
|
||||
1.3,0.728,2.4
|
||||
0.0,0.0,0.0
|
||||
1.5,0.0,0.15
|
||||
1.5,0.84,0.15
|
||||
0.84,1.5,0.15
|
||||
0.0,1.5,0.15
|
||||
1.5,0.0,0.075
|
||||
1.5,0.84,0.075
|
||||
0.84,1.5,0.075
|
||||
0.0,1.5,0.075
|
||||
1.425,0.0,0.0
|
||||
1.425,0.798,0.0
|
||||
0.798,1.425,0.0
|
||||
0.0,1.425,0.0
|
||||
-0.84,1.5,0.15
|
||||
-1.5,0.84,0.15
|
||||
-1.5,0.0,0.15
|
||||
-0.84,1.5,0.075
|
||||
-1.5,0.84,0.075
|
||||
-1.5,0.0,0.075
|
||||
-0.798,1.425,0.0
|
||||
-1.425,0.798,0.0
|
||||
-1.425,0.0,0.0
|
||||
-1.5,-0.84,0.15
|
||||
-0.84,-1.5,0.15
|
||||
0.0,-1.5,0.15
|
||||
-1.5,-0.84,0.075
|
||||
-0.84,-1.5,0.075
|
||||
0.0,-1.5,0.075
|
||||
-1.425,-0.798,0.0
|
||||
-0.798,-1.425,0.0
|
||||
0.0,-1.425,0.0
|
||||
0.84,-1.5,0.15
|
||||
1.5,-0.84,0.15
|
||||
0.84,-1.5,0.075
|
||||
1.5,-0.84,0.075
|
||||
0.798,-1.425,0.0
|
||||
1.425,-0.798,0.0
|
||||
"""
|
||||
|
||||
teaspoon = """16
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
||||
17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32
|
||||
33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48
|
||||
49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64
|
||||
65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80
|
||||
81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96
|
||||
97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112
|
||||
113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128
|
||||
129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144
|
||||
145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160
|
||||
161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176
|
||||
177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192
|
||||
193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208
|
||||
209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224
|
||||
225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240
|
||||
241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256
|
||||
256
|
||||
-0.000107143,0.205357,0.0
|
||||
0.0,0.196429,-0.0178571
|
||||
0.0,0.196429,-0.0178571
|
||||
0.000107143,0.205357,0.0
|
||||
-0.0535714,0.205357,0.0
|
||||
-0.0222714,0.178571,-0.0534286
|
||||
0.0222714,0.178571,-0.0534286
|
||||
0.0535714,0.205357,0.0
|
||||
-0.107143,0.0952429,-0.0178571
|
||||
-0.0446429,0.0952429,-0.0892857
|
||||
0.0446429,0.0952429,-0.0892857
|
||||
0.107143,0.0952429,-0.0178571
|
||||
-0.107143,0.0,-0.0178571
|
||||
-0.0446429,0.0,-0.0892857
|
||||
0.0446429,0.0,-0.0892857
|
||||
0.107143,0.0,-0.0178571
|
||||
0.000107143,0.205357,0.0
|
||||
0.000135714,0.207589,0.00446429
|
||||
0.000157143,0.216518,0.00446429
|
||||
0.000125,0.214286,0.0
|
||||
0.0535714,0.205357,0.0
|
||||
0.0613964,0.212054,0.0133571
|
||||
0.0714286,0.220982,0.015625
|
||||
0.0625,0.214286,0.0
|
||||
0.107143,0.0952429,-0.0178571
|
||||
0.122768,0.0952429,0.0
|
||||
0.142857,0.0952429,0.00446429
|
||||
0.125,0.0952429,-0.0178571
|
||||
0.107143,0.0,-0.0178571
|
||||
0.122768,0.0,0.0
|
||||
0.142857,0.0,0.00446429
|
||||
0.125,0.0,-0.0178571
|
||||
0.000125,0.214286,0.0
|
||||
0.0,0.205357,-0.0178571
|
||||
0.0,0.205357,-0.0178571
|
||||
-0.000125,0.214286,0.0
|
||||
0.0625,0.214286,0.0
|
||||
0.0267857,0.1875,-0.0625
|
||||
-0.0267857,0.1875,-0.0625
|
||||
-0.0625,0.214286,0.0
|
||||
0.125,0.0952429,-0.0178571
|
||||
0.0535714,0.0952429,-0.107143
|
||||
-0.0535714,0.0952429,-0.107143
|
||||
-0.125,0.0952429,-0.0178571
|
||||
0.125,0.0,-0.0178571
|
||||
0.0535714,0.0,-0.107143
|
||||
-0.0535714,0.0,-0.107143
|
||||
-0.125,0.0,-0.0178571
|
||||
-0.000125,0.214286,0.0
|
||||
-0.000157143,0.216518,0.00446429
|
||||
-0.000135714,0.207589,0.00446429
|
||||
-0.000107143,0.205357,0.0
|
||||
-0.0625,0.214286,0.0
|
||||
-0.0714286,0.220982,0.015625
|
||||
-0.0613964,0.212054,0.0133571
|
||||
-0.0535714,0.205357,0.0
|
||||
-0.125,0.0952429,-0.0178571
|
||||
-0.142857,0.0952429,0.00446429
|
||||
-0.122768,0.0952429,0.0
|
||||
-0.107143,0.0952429,-0.0178571
|
||||
-0.125,0.0,-0.0178571
|
||||
-0.142857,0.0,0.00446429
|
||||
-0.122768,0.0,0.0
|
||||
-0.107143,0.0,-0.0178571
|
||||
-0.107143,0.0,-0.0178571
|
||||
-0.0446429,0.0,-0.0892857
|
||||
0.0446429,0.0,-0.0892857
|
||||
0.107143,0.0,-0.0178571
|
||||
-0.107143,-0.142857,-0.0178571
|
||||
-0.0446429,-0.142857,-0.0892857
|
||||
0.0446429,-0.142857,-0.0892857
|
||||
0.107143,-0.142857,-0.0178571
|
||||
-0.0133929,-0.160714,0.0386893
|
||||
-0.00557857,-0.160714,0.0386893
|
||||
0.00557857,-0.160714,0.0386893
|
||||
0.0133929,-0.160714,0.0386893
|
||||
-0.0133929,-0.25,0.0535714
|
||||
-0.00557857,-0.25,0.0535714
|
||||
0.00557857,-0.25,0.0535714
|
||||
0.0133929,-0.25,0.0535714
|
||||
0.107143,0.0,-0.0178571
|
||||
0.122768,0.0,0.0
|
||||
0.142857,0.0,0.00446429
|
||||
0.125,0.0,-0.0178571
|
||||
0.107143,-0.142857,-0.0178571
|
||||
0.122768,-0.142857,0.0
|
||||
0.142857,-0.142857,0.00446429
|
||||
0.125,-0.142857,-0.0178571
|
||||
0.0133929,-0.160714,0.0386893
|
||||
0.0153464,-0.160714,0.0386893
|
||||
0.0178571,-0.160714,0.0314357
|
||||
0.015625,-0.160714,0.0297607
|
||||
0.0133929,-0.25,0.0535714
|
||||
0.0153464,-0.25,0.0535714
|
||||
0.0178571,-0.25,0.0463179
|
||||
0.015625,-0.25,0.0446429
|
||||
0.125,0.0,-0.0178571
|
||||
0.0535714,0.0,-0.107143
|
||||
-0.0535714,0.0,-0.107143
|
||||
-0.125,0.0,-0.0178571
|
||||
0.125,-0.142857,-0.0178571
|
||||
0.0535714,-0.142857,-0.107143
|
||||
-0.0535714,-0.142857,-0.107143
|
||||
-0.125,-0.142857,-0.0178571
|
||||
0.015625,-0.160714,0.0297607
|
||||
0.00669643,-0.160714,0.0230643
|
||||
-0.00781071,-0.160714,0.0208321
|
||||
-0.015625,-0.160714,0.0297607
|
||||
0.015625,-0.25,0.0446429
|
||||
0.00669643,-0.25,0.0379464
|
||||
-0.00781071,-0.25,0.0357143
|
||||
-0.015625,-0.25,0.0446429
|
||||
-0.125,0.0,-0.0178571
|
||||
-0.142857,0.0,0.00446429
|
||||
-0.122768,0.0,0.0
|
||||
-0.107143,0.0,-0.0178571
|
||||
-0.125,-0.142857,-0.0178571
|
||||
-0.142857,-0.142857,0.00446429
|
||||
-0.122768,-0.142857,0.0
|
||||
-0.107143,-0.142857,-0.0178571
|
||||
-0.015625,-0.160714,0.0297607
|
||||
-0.0175786,-0.160714,0.0319929
|
||||
-0.0153464,-0.160714,0.0386893
|
||||
-0.0133929,-0.160714,0.0386893
|
||||
-0.015625,-0.25,0.0446429
|
||||
-0.0175786,-0.25,0.046875
|
||||
-0.0153464,-0.25,0.0535714
|
||||
-0.0133929,-0.25,0.0535714
|
||||
-0.0133929,-0.25,0.0535714
|
||||
-0.00557857,-0.25,0.0535714
|
||||
0.00557857,-0.25,0.0535714
|
||||
0.0133929,-0.25,0.0535714
|
||||
-0.0133929,-0.46425,0.0892857
|
||||
-0.00557857,-0.46425,0.0892857
|
||||
0.00557857,-0.46425,0.0892857
|
||||
0.0133929,-0.46425,0.0892857
|
||||
-0.0446429,-0.678571,0.0535714
|
||||
-0.00892857,-0.678571,0.0625
|
||||
0.00892857,-0.678571,0.0625
|
||||
0.0446429,-0.678571,0.0535714
|
||||
-0.0446429,-0.857143,0.0357143
|
||||
-0.00892857,-0.857143,0.0446429
|
||||
0.00892857,-0.857143,0.0446429
|
||||
0.0446429,-0.857143,0.0357143
|
||||
0.0133929,-0.25,0.0535714
|
||||
0.0153464,-0.25,0.0535714
|
||||
0.0178571,-0.25,0.0463179
|
||||
0.015625,-0.25,0.0446429
|
||||
0.0133929,-0.46425,0.0892857
|
||||
0.0153464,-0.464286,0.0892857
|
||||
0.0178571,-0.46425,0.0820321
|
||||
0.015625,-0.46425,0.0803571
|
||||
0.0446429,-0.678571,0.0535714
|
||||
0.0535714,-0.678571,0.0513393
|
||||
0.0535714,-0.678571,0.0334821
|
||||
0.0446429,-0.678571,0.0357143
|
||||
0.0446429,-0.857143,0.0357143
|
||||
0.0535714,-0.857143,0.0334821
|
||||
0.0535714,-0.857143,0.015625
|
||||
0.0446429,-0.857143,0.0178571
|
||||
0.015625,-0.25,0.0446429
|
||||
0.00669643,-0.25,0.0379464
|
||||
-0.00781071,-0.25,0.0357143
|
||||
-0.015625,-0.25,0.0446429
|
||||
0.015625,-0.46425,0.0803571
|
||||
0.00669643,-0.464286,0.0736607
|
||||
-0.00781071,-0.46425,0.0714286
|
||||
-0.015625,-0.46425,0.0803571
|
||||
0.0446429,-0.678571,0.0357143
|
||||
0.00892857,-0.678571,0.0446429
|
||||
-0.00892857,-0.678571,0.0446429
|
||||
-0.0446429,-0.678571,0.0357143
|
||||
0.0446429,-0.857143,0.0178571
|
||||
0.00892857,-0.857143,0.0267857
|
||||
-0.00892857,-0.857143,0.0267857
|
||||
-0.0446429,-0.857143,0.0178571
|
||||
-0.015625,-0.25,0.0446429
|
||||
-0.0175786,-0.25,0.046875
|
||||
-0.0153464,-0.25,0.0535714
|
||||
-0.0133929,-0.25,0.0535714
|
||||
-0.015625,-0.46425,0.0803571
|
||||
-0.0175786,-0.464286,0.0825893
|
||||
-0.0153464,-0.464286,0.0892857
|
||||
-0.0133929,-0.46425,0.0892857
|
||||
-0.0446429,-0.678571,0.0357143
|
||||
-0.0535714,-0.678571,0.0334821
|
||||
-0.0535714,-0.678571,0.0513393
|
||||
-0.0446429,-0.678571,0.0535714
|
||||
-0.0446429,-0.857143,0.0178571
|
||||
-0.0535714,-0.857143,0.015625
|
||||
-0.0535714,-0.857143,0.0334821
|
||||
-0.0446429,-0.857143,0.0357143
|
||||
-0.0446429,-0.857143,0.0357143
|
||||
-0.00892857,-0.857143,0.0446429
|
||||
0.00892857,-0.857143,0.0446429
|
||||
0.0446429,-0.857143,0.0357143
|
||||
-0.0446429,-0.928571,0.0285714
|
||||
-0.00892857,-0.928571,0.0375
|
||||
0.00892857,-0.928571,0.0375
|
||||
0.0446429,-0.928571,0.0285714
|
||||
-0.0539286,-0.999643,0.0178571
|
||||
0.000357143,-0.999643,0.0178571
|
||||
0.0,-0.999643,0.0178571
|
||||
0.0535714,-0.999643,0.0178571
|
||||
-0.000357143,-1,0.0178571
|
||||
0.000357143,-1,0.0178571
|
||||
0.0,-1,0.0178571
|
||||
0.0,-1,0.0178571
|
||||
0.0446429,-0.857143,0.0357143
|
||||
0.0535714,-0.857143,0.0334821
|
||||
0.0535714,-0.857143,0.015625
|
||||
0.0446429,-0.857143,0.0178571
|
||||
0.0446429,-0.928571,0.0285714
|
||||
0.0535714,-0.928571,0.0263393
|
||||
0.0535714,-0.928571,0.00848214
|
||||
0.0446429,-0.928571,0.0107143
|
||||
0.0535714,-0.999643,0.0178571
|
||||
0.0669643,-0.999643,0.0178571
|
||||
0.0673214,-0.999643,0.0
|
||||
0.0539286,-0.999643,0.0
|
||||
0.0,-1,0.0178571
|
||||
0.0,-1,0.0178571
|
||||
0.000357143,-1,0.0
|
||||
0.000357143,-1,0.0
|
||||
0.0446429,-0.857143,0.0178571
|
||||
0.00892857,-0.857143,0.0267857
|
||||
-0.00892857,-0.857143,0.0267857
|
||||
-0.0446429,-0.857143,0.0178571
|
||||
0.0446429,-0.928571,0.0107143
|
||||
0.00892857,-0.928571,0.0196429
|
||||
-0.00892857,-0.928571,0.0196429
|
||||
-0.0446429,-0.928571,0.0107143
|
||||
0.0539286,-0.999643,0.0
|
||||
0.000357143,-0.999643,0.0
|
||||
-0.000357143,-0.999643,0.0
|
||||
-0.0539286,-0.999643,0.0
|
||||
0.000357143,-1,0.0
|
||||
0.000357143,-1,0.0
|
||||
-0.000357143,-1,0.0
|
||||
-0.000357143,-1,0.0
|
||||
-0.0446429,-0.857143,0.0178571
|
||||
-0.0535714,-0.857143,0.015625
|
||||
-0.0535714,-0.857143,0.0334821
|
||||
-0.0446429,-0.857143,0.0357143
|
||||
-0.0446429,-0.928571,0.0107143
|
||||
-0.0535714,-0.928571,0.00848214
|
||||
-0.0535714,-0.928571,0.0263393
|
||||
-0.0446429,-0.928571,0.0285714
|
||||
-0.0539286,-0.999643,0.0
|
||||
-0.0673214,-0.999643,0.0
|
||||
-0.0675,-0.999643,0.0178571
|
||||
-0.0539286,-0.999643,0.0178571
|
||||
-0.000357143,-1,0.0
|
||||
-0.000357143,-1,0.0
|
||||
-0.000535714,-1,0.0178571
|
||||
-0.000357143,-1,0.0178571
|
||||
"""
|
||||
@@ -0,0 +1,201 @@
|
||||
# SPDX-FileCopyrightText: 2012-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: Anthony D'Agostino
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from math import sin, cos, pi
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
def create_mesh_object(context, verts, edges, faces, name):
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
# Make a mesh from a list of verts/edges/faces.
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
# Update mesh geometry after adding stuff.
|
||||
mesh.update()
|
||||
from bpy_extras import object_utils
|
||||
return object_utils.object_data_add(context, mesh, operator=None)
|
||||
|
||||
|
||||
# ========================
|
||||
# === Torus Knot Block ===
|
||||
# ========================
|
||||
|
||||
def k1(t):
|
||||
x = cos(t) - 2 * cos(2 * t)
|
||||
y = sin(t) + 2 * sin(2 * t)
|
||||
z = sin(3 * t)
|
||||
return Vector([x, y, z])
|
||||
|
||||
|
||||
def k2(t):
|
||||
x = 10 * (cos(t) + cos(3 * t)) + cos(2 * t) + cos(4 * t)
|
||||
y = 6 * sin(t) + 10 * sin(3 * t)
|
||||
z = 4 * sin(3 * t) * sin(5 * t / 2) + 4 * sin(4 * t) - 2 * sin(6 * t)
|
||||
return Vector([x, y, z]) * 0.2
|
||||
|
||||
|
||||
def k3(t):
|
||||
x = 2.5 * cos(t + pi) / 3 + 2 * cos(3 * t)
|
||||
y = 2.5 * sin(t) / 3 + 2 * sin(3 * t)
|
||||
z = 1.5 * sin(4 * t) + sin(2 * t) / 3
|
||||
return Vector([x, y, z])
|
||||
|
||||
|
||||
def make_verts(ures, vres, r2, knotfunc):
|
||||
verts = []
|
||||
for i in range(ures):
|
||||
t1 = (i + 0) * 2 * pi / ures
|
||||
t2 = (i + 1) * 2 * pi / ures
|
||||
a = knotfunc(t1) # curr point
|
||||
b = knotfunc(t2) # next point
|
||||
a, b = map(Vector, (a, b))
|
||||
e = a - b
|
||||
f = a + b
|
||||
g = e.cross(f)
|
||||
h = e.cross(g)
|
||||
g.normalize()
|
||||
h.normalize()
|
||||
for j in range(vres):
|
||||
k = j * 2 * pi / vres
|
||||
l = (cos(k), 0.0, sin(k))
|
||||
l = Vector(l)
|
||||
m = l * r2
|
||||
x, y, z = m
|
||||
n = h * x
|
||||
o = g * z
|
||||
p = n + o
|
||||
q = a + p
|
||||
verts.append(q)
|
||||
return verts
|
||||
|
||||
|
||||
def make_faces(ures, vres):
|
||||
faces = []
|
||||
for u in range(0, ures):
|
||||
for v in range(0, vres):
|
||||
p1 = v + u * vres
|
||||
p2 = v + ((u + 1) % ures) * vres
|
||||
p4 = (v + 1) % vres + u * vres
|
||||
p3 = (v + 1) % vres + ((u + 1) % ures) * vres
|
||||
faces.append([p4, p3, p2, p1])
|
||||
return faces
|
||||
|
||||
|
||||
def make_knot(knotidx, ures):
|
||||
knots = [k1, k2, k3]
|
||||
knotfunc = knots[knotidx - 1]
|
||||
vres = ures // 10
|
||||
r2 = 0.5
|
||||
verts = make_verts(ures, vres, r2, knotfunc)
|
||||
faces = make_faces(ures, vres)
|
||||
return (verts, faces)
|
||||
|
||||
|
||||
class AddTorusKnot(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_torusknot_add"
|
||||
bl_label = "Add Torus Knot"
|
||||
bl_description = "Construct a torus knot mesh"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
TorusKnot : BoolProperty(name = "TorusKnot",
|
||||
default = True,
|
||||
description = "TorusKnot")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change TorusKnot")
|
||||
|
||||
resolution: IntProperty(
|
||||
name="Resolution",
|
||||
description="Resolution of the Torus Knot",
|
||||
default=80,
|
||||
min=30, max=256
|
||||
)
|
||||
objecttype: IntProperty(
|
||||
name="Knot Type",
|
||||
description="Type of Knot",
|
||||
default=1,
|
||||
min=1, max=3
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.prop(self, 'resolution', expand=True)
|
||||
layout.prop(self, 'objecttype', expand=True)
|
||||
|
||||
if self.change == False:
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('TorusKnot' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
verts, faces = make_knot(self.objecttype, self.resolution)
|
||||
mesh = bpy.data.meshes.new('TorusKnot')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = make_knot(self.objecttype, self.resolution)
|
||||
mesh = bpy.data.meshes.new('TorusKnot')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["TorusKnot"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in TorusKnotParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = make_knot(self.objecttype, self.resolution)
|
||||
mesh = bpy.data.meshes.new('TorusKnot')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def TorusKnotParameters():
|
||||
TorusKnotParameters = [
|
||||
"resolution",
|
||||
"objecttype",
|
||||
]
|
||||
return TorusKnotParameters
|
||||
@@ -0,0 +1,313 @@
|
||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
"""
|
||||
This script provides a triangle mesh primitive
|
||||
and a toolbar menu to further specify settings
|
||||
"""
|
||||
|
||||
import math
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
def checkEditMode():
|
||||
# Check if we are in edit mode
|
||||
# Returns: 1 if True
|
||||
# 0 if False
|
||||
if (bpy.context.active_object.mode == 'EDIT'):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def exitEditMode():
|
||||
# Check if we are in edit mode (cuz we don't want this when creating a new Mesh)
|
||||
# If we are then toggle back to object mode
|
||||
# Check if there are active objects
|
||||
if bpy.context.active_object is not None:
|
||||
# Only the active object should be in edit mode
|
||||
if (bpy.context.active_object.mode == 'EDIT'):
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
|
||||
class MakeTriangle(Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.make_triangle"
|
||||
bl_label = "Add Triangle"
|
||||
bl_description = "Construct different types of Triangle Meshes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
nothing = 0
|
||||
Ya = 0.0
|
||||
Xb = 0.0
|
||||
Xc = 0.0
|
||||
Vertices = []
|
||||
Faces = []
|
||||
|
||||
triangleTypeList = [
|
||||
('ISOSCELES', "Isosceles", "Two equal sides", 0),
|
||||
('EQUILATERAL', "Equilateral", "Three equal sides and angles (60°)", 1),
|
||||
('ISOSCELESRIGHTANGLE', "Isosceles right angled", "90° angle and two equal sides", 2),
|
||||
('SCALENERIGHTANGLE', "Scalene right angled", "90° angle, no equal sides", 3)
|
||||
]
|
||||
triangleFaceList = [
|
||||
('DEFAULT', "Triangle", "1 Triangle face", 0),
|
||||
('TRIANGLES', "3 Triangles", "4 Vertices & 3 Triangle faces", 1),
|
||||
('QUADS', "3 Quads", "7 Vertices & 3 Quad faces", 2),
|
||||
('SAFEQUADS', "6 Quads", "12 Vertices & 6 Quad faces", 3)
|
||||
]
|
||||
|
||||
# add definitions for some manipulation buttons
|
||||
flipX: BoolProperty(
|
||||
name="Flip X sign",
|
||||
description="Draw on the other side of the X axis (Mirror on Y axis)",
|
||||
default=False
|
||||
)
|
||||
flipY: BoolProperty(
|
||||
name="Flip Y sign",
|
||||
description="Draw on the other side of the Y axis (Mirror on X axis)",
|
||||
default=False
|
||||
)
|
||||
scale: FloatProperty(
|
||||
name="Scale",
|
||||
description="Triangle scale",
|
||||
default=1.0,
|
||||
min=1.0
|
||||
)
|
||||
triangleType: EnumProperty(
|
||||
items=triangleTypeList,
|
||||
name="Type",
|
||||
description="Triangle Type"
|
||||
)
|
||||
triangleFace: EnumProperty(
|
||||
items=triangleFaceList,
|
||||
name="Face Types",
|
||||
description="Triangle Face Types"
|
||||
)
|
||||
at_3Dcursor: BoolProperty(
|
||||
name="Use 3D Cursor",
|
||||
description="Draw the triangle where the 3D cursor is",
|
||||
default=True
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column()
|
||||
col.prop(self, "triangleType", text="Type")
|
||||
col.prop(self, "triangleFace", text="Fill Type")
|
||||
col.prop(self, "scale")
|
||||
col.separator()
|
||||
row = col.row(heading='At')
|
||||
row.prop(self, "at_3Dcursor", text="3D Cursor")
|
||||
col.separator()
|
||||
row = col.row(heading='Flip')
|
||||
row.prop(self, "flipX", text='X')
|
||||
col.prop(self, "flipY", text='Y')
|
||||
col.separator()
|
||||
draw_transform_props(self, col)
|
||||
|
||||
def drawBasicTriangleShape(self):
|
||||
# set everything to 0
|
||||
Xb = Xc = 0.0
|
||||
Ya = 0.0
|
||||
|
||||
scale = self.scale
|
||||
Xsign = -1 if self.flipX else 1
|
||||
Ysign = -1 if self.flipY else 1
|
||||
|
||||
# Isosceles (2 equal sides)
|
||||
if (self.triangleType == 'ISOSCELES'):
|
||||
# below a simple triangle containing 2 triangles with 1:2 side ratio
|
||||
Ya = (1 * Ysign * scale)
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
Xb = (0.5 * Xsign * scale)
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
Xc = (-0.5 * Xsign * scale)
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
|
||||
self.Ya = Ya
|
||||
self.Xb = Xb
|
||||
self.Xc = Xc
|
||||
self.Vertices = [A, B, C, ]
|
||||
|
||||
return True
|
||||
|
||||
# Equilateral (all sides equal)
|
||||
if (self.triangleType == 'EQUILATERAL'):
|
||||
Ya = (math.sqrt(0.75) * Ysign * scale)
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
Xb = (0.5 * Xsign * scale)
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
Xc = (-0.5 * Xsign * scale)
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
|
||||
self.Ya = Ya
|
||||
self.Xb = Xb
|
||||
self.Xc = Xc
|
||||
self.Vertices = [A, B, C, ]
|
||||
|
||||
return True
|
||||
|
||||
# Isosceles right angled (1, 1, sqrt(2))
|
||||
if (self.triangleType == 'ISOSCELESRIGHTANGLE'):
|
||||
Ya = (1 * Ysign * scale)
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
Xb = 0.0
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
Xc = (1 * Xsign * scale)
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
|
||||
self.Ya = Ya
|
||||
self.Xb = Xb
|
||||
self.Xc = Xc
|
||||
self.Vertices = [A, B, C, ]
|
||||
return True
|
||||
|
||||
# Scalene right angled (3, 4, 5)
|
||||
if (self.triangleType == 'SCALENERIGHTANGLE'):
|
||||
Ya = (1 * Ysign * scale)
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
Xb = 0
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
Xc = (0.75 * Xsign * scale)
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
|
||||
self.Ya = Ya
|
||||
self.Xb = Xb
|
||||
self.Xc = Xc
|
||||
self.Vertices = [A, B, C, ]
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def addFaces(self, fType=None):
|
||||
Ya = self.Ya
|
||||
Xb = self.Xb
|
||||
Xc = self.Xc
|
||||
|
||||
if (self.triangleFace == 'DEFAULT'):
|
||||
self.Faces = [[0, 1, 2]]
|
||||
return True
|
||||
|
||||
if (self.triangleFace == 'TRIANGLES'):
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
|
||||
|
||||
self.Vertices = [A, B, C, D, ]
|
||||
self.Faces = [[0, 1, 3], [1, 2, 3], [2, 0, 3]]
|
||||
return True
|
||||
|
||||
if (self.triangleFace == 'QUADS'):
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
|
||||
AB = A.lerp(B, 0.5)
|
||||
AC = A.lerp(C, 0.5)
|
||||
BC = B.lerp(C, 0.5)
|
||||
|
||||
self.Vertices = [A, AB, B, BC, C, AC, D, ]
|
||||
self.Faces = [[0, 1, 6, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
|
||||
return True
|
||||
|
||||
if (self.triangleFace == 'SAFEQUADS'):
|
||||
A = Vector([0.0, Ya, 0.0])
|
||||
B = Vector([Xb, 0.0, 0.0])
|
||||
C = Vector([Xc, 0.0, 0.0])
|
||||
D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
|
||||
E = A.lerp(D, 0.5)
|
||||
AB = A.lerp(B, 0.5)
|
||||
AC = A.lerp(C, 0.5)
|
||||
BC = B.lerp(C, 0.5)
|
||||
AAB = AB.lerp(A, 0.5)
|
||||
AAC = AC.lerp(A, 0.5)
|
||||
BBA = AB.lerp(B, 0.5)
|
||||
BBC = BC.lerp(B, 0.5)
|
||||
BCC = BC.lerp(C, 0.5)
|
||||
CCA = AC.lerp(C, 0.5)
|
||||
|
||||
self.Vertices = [A, AAB, BBA, B, BBC, BC, BCC, C, CCA, AAC, D, E, ]
|
||||
self.Faces = [[0, 1, 11, 9], [1, 2, 10, 11], [2, 3, 4, 10],
|
||||
[4, 5, 6, 10], [6, 7, 8, 10], [8, 9, 11, 10]]
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def action_common(self, context):
|
||||
# definitions:
|
||||
# a triangle consists of 3 points: A, B, C
|
||||
# a 'safer' subdividable triangle consists of 4 points: A, B, C, D
|
||||
# a subdivide friendly triangle consists of 7 points: A, B, C, D, AB, AC, BC
|
||||
# a truly subdivide friendly triangle consists of (3 x 4 = )12 points:
|
||||
# A, B, C, D, E, BC, AAB, AAC, BBA, BBC, BCC, CCA
|
||||
|
||||
BasicShapeCreated = False
|
||||
ShapeFacesCreated = False
|
||||
go = 0
|
||||
|
||||
#
|
||||
# call the functions for creating the triangles and test if successful
|
||||
#
|
||||
BasicShapeCreated = self.drawBasicTriangleShape()
|
||||
if (BasicShapeCreated):
|
||||
ShapeFacesCreated = self.addFaces()
|
||||
if ShapeFacesCreated:
|
||||
go = 1
|
||||
|
||||
if (go == 1):
|
||||
NewMesh = bpy.data.meshes.new("Triangle")
|
||||
NewMesh.from_pydata(self.Vertices, [], self.Faces)
|
||||
|
||||
NewMesh.update()
|
||||
NewObj = bpy.data.objects.new("Triangle", NewMesh)
|
||||
context.collection.objects.link(NewObj)
|
||||
|
||||
# before doing the deselect make sure edit mode isn't active
|
||||
exitEditMode()
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
NewObj.select_set(True)
|
||||
context.view_layer.objects.active = NewObj
|
||||
|
||||
if self.at_3Dcursor is True:
|
||||
# we'll need to be sure there is actually an object selected
|
||||
if NewObj.select_get() is True:
|
||||
# we also have to check if we're considered to be in 3D View (view3d)
|
||||
if bpy.ops.view3d.snap_selected_to_cursor.poll() is True:
|
||||
bpy.ops.view3d.snap_selected_to_cursor()
|
||||
else:
|
||||
# as we weren't considered to be in 3D View
|
||||
# the object couldn't be moved to the 3D cursor
|
||||
# so to avoid confusion we change the at_3Dcursor boolean to false
|
||||
self.at_3Dcursor = False
|
||||
|
||||
else:
|
||||
self.report({'WARNING'},
|
||||
"Triangle could not be completed. (See Console for more Info)")
|
||||
|
||||
print("\n[Add Mesh Extra Objects]\n\nModule: add_mesh_triangle")
|
||||
print("Triangle type: %s\n" % self.triangleType,
|
||||
"Face type: %s\n" % self.triangleFace,
|
||||
"Ya: %s, Xb: %s, Xc: %s\n" % (self.Ya, self.Xb, self.Xc),
|
||||
"Vertices: %s\n" % self.Vertices,
|
||||
"Faces: %s\n" % self.Faces)
|
||||
|
||||
def execute(self, context):
|
||||
self.action_common(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.action_common(context)
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,327 @@
|
||||
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Author: Paulo_Gomes
|
||||
|
||||
import bpy
|
||||
from mathutils import Quaternion, Vector
|
||||
from math import cos, sin, pi
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
from bpy_extras import object_utils
|
||||
from .interface import draw_transform_props
|
||||
|
||||
|
||||
# Create a new mesh (object) from verts/edges/faces
|
||||
# verts/edges/faces ... List of vertices/edges/faces for the
|
||||
# new mesh (as used in from_pydata)
|
||||
# name ... Name of the new mesh (& object)
|
||||
|
||||
def create_mesh_object(context, verts, edges, faces, name):
|
||||
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
|
||||
# Make a mesh from a list of verts/edges/faces
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Update mesh geometry after adding stuff
|
||||
mesh.update()
|
||||
|
||||
from bpy_extras import object_utils
|
||||
return object_utils.object_data_add(context, mesh, operator=None)
|
||||
|
||||
|
||||
# A very simple "bridge" tool
|
||||
|
||||
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
||||
faces = []
|
||||
|
||||
if not vertIdx1 or not vertIdx2:
|
||||
return None
|
||||
|
||||
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
||||
return None
|
||||
|
||||
fan = False
|
||||
if (len(vertIdx1) != len(vertIdx2)):
|
||||
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
|
||||
fan = True
|
||||
else:
|
||||
return None
|
||||
|
||||
total = len(vertIdx2)
|
||||
|
||||
if closed:
|
||||
# Bridge the start with the end
|
||||
if flipped:
|
||||
face = [
|
||||
vertIdx1[0],
|
||||
vertIdx2[0],
|
||||
vertIdx2[total - 1]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
else:
|
||||
face = [vertIdx2[0], vertIdx1[0]]
|
||||
if not fan:
|
||||
face.append(vertIdx1[total - 1])
|
||||
face.append(vertIdx2[total - 1])
|
||||
faces.append(face)
|
||||
|
||||
# Bridge the rest of the faces
|
||||
for num in range(total - 1):
|
||||
if flipped:
|
||||
if fan:
|
||||
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx2[num], vertIdx1[num],
|
||||
vertIdx1[num + 1], vertIdx2[num + 1]]
|
||||
faces.append(face)
|
||||
else:
|
||||
if fan:
|
||||
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
||||
else:
|
||||
face = [vertIdx1[num], vertIdx2[num],
|
||||
vertIdx2[num + 1], vertIdx1[num + 1]]
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
def add_twisted_torus(major_rad, minor_rad, major_seg, minor_seg, twists):
|
||||
PI_2 = pi * 2.0
|
||||
z_axis = (0.0, 0.0, 1.0)
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
|
||||
edgeloop_prev = []
|
||||
for major_index in range(major_seg):
|
||||
quat = Quaternion(z_axis, (major_index / major_seg) * PI_2)
|
||||
rot_twists = PI_2 * major_index / major_seg * twists
|
||||
|
||||
edgeloop = []
|
||||
|
||||
# Create section ring
|
||||
for minor_index in range(minor_seg):
|
||||
angle = (PI_2 * minor_index / minor_seg) + rot_twists
|
||||
|
||||
vec = Vector((
|
||||
major_rad + (cos(angle) * minor_rad),
|
||||
0.0,
|
||||
sin(angle) * minor_rad))
|
||||
vec = quat @ vec
|
||||
|
||||
edgeloop.append(len(verts))
|
||||
verts.append(vec)
|
||||
|
||||
# Remember very first edgeloop
|
||||
if major_index == 0:
|
||||
edgeloop_first = edgeloop
|
||||
|
||||
# Bridge last with current ring
|
||||
if edgeloop_prev:
|
||||
f = createFaces(edgeloop_prev, edgeloop, closed=True)
|
||||
faces.extend(f)
|
||||
|
||||
edgeloop_prev = edgeloop
|
||||
|
||||
# Bridge first and last ring
|
||||
f = createFaces(edgeloop_prev, edgeloop_first, closed=True)
|
||||
faces.extend(f)
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
class AddTwistedTorus(bpy.types.Operator, object_utils.AddObjectHelper):
|
||||
bl_idname = "mesh.primitive_twisted_torus_add"
|
||||
bl_label = "Add Twisted Torus"
|
||||
bl_description = "Construct a twisted torus mesh"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
TwistedTorus : BoolProperty(name = "TwistedTorus",
|
||||
default = True,
|
||||
description = "TwistedTorus")
|
||||
change : BoolProperty(name = "Change",
|
||||
default = False,
|
||||
description = "change TwistedTorus")
|
||||
|
||||
|
||||
method: EnumProperty(
|
||||
name='Method',
|
||||
description='Method for determining the size and thickness of the torus',
|
||||
items=(
|
||||
('MAJOR-MINOR', 'Major / Minor', 'Uses the major radius for the overall size and the minor for the thickness'),
|
||||
('INT-EXT', 'Interior / Exterior', 'Uses the absolute size of the inner and outer circles to determine the size and thickness'),
|
||||
),
|
||||
default='MAJOR-MINOR',
|
||||
)
|
||||
major_radius: FloatProperty(
|
||||
name="Major Radius",
|
||||
description="Radius from the origin to the"
|
||||
" center of the cross section",
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
default=1.0
|
||||
)
|
||||
minor_radius: FloatProperty(
|
||||
name="Minor Radius",
|
||||
description="Radius of the torus' cross section",
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
default=0.25
|
||||
)
|
||||
major_segments: IntProperty(
|
||||
name="Major Segments",
|
||||
description="Number of segments for the main ring of the torus",
|
||||
min=3,
|
||||
max=256,
|
||||
default=48
|
||||
)
|
||||
minor_segments: IntProperty(
|
||||
name="Minor Segments",
|
||||
description="Number of segments for the minor ring of the torus",
|
||||
min=3,
|
||||
max=256,
|
||||
default=12
|
||||
)
|
||||
twists: IntProperty(
|
||||
name="Twists",
|
||||
description="Number of twists of the torus",
|
||||
min=0,
|
||||
max=256,
|
||||
default=1
|
||||
)
|
||||
abso_major_rad: FloatProperty(
|
||||
name="Exterior Radius",
|
||||
description="Total Exterior Radius of the torus",
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
default=1.0
|
||||
)
|
||||
abso_minor_rad: FloatProperty(
|
||||
name="Inside Radius",
|
||||
description="Total Interior Radius of the torus",
|
||||
min=0.01,
|
||||
max=100.0,
|
||||
default=0.5
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'method', text='Dimensions Mode')
|
||||
col = layout.column(align=True)
|
||||
if self.method == 'MAJOR-MINOR':
|
||||
col.prop(self, 'major_radius', text='Radius Major')
|
||||
col.prop(self, 'minor_radius', text='Minor')
|
||||
else:
|
||||
col.prop(self, 'abso_major_rad', text='Radius Exterior')
|
||||
col.prop(self, 'abso_minor_rad', text='Interior')
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, 'major_segments', text='Segments Major')
|
||||
col.prop(self, 'minor_segments', text='Minor')
|
||||
layout.prop(self, 'twists', expand=True)
|
||||
|
||||
if self.change == False:
|
||||
draw_transform_props(self, layout)
|
||||
|
||||
def execute(self, context):
|
||||
# turn off 'Enter Edit Mode'
|
||||
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = False
|
||||
|
||||
if self.method == 'INT-EXT':
|
||||
extra_helper = (self.abso_major_rad - self.abso_minor_rad) * 0.5
|
||||
self.major_radius = self.abso_minor_rad + extra_helper
|
||||
self.minor_radius = extra_helper
|
||||
|
||||
if bpy.context.mode == "OBJECT":
|
||||
if context.selected_objects != [] and context.active_object and \
|
||||
(context.active_object.data is not None) and ('TwistedTorus' in context.active_object.data.keys()) and \
|
||||
(self.change == True):
|
||||
obj = context.active_object
|
||||
oldmesh = obj.data
|
||||
oldmeshname = obj.data.name
|
||||
verts, faces = add_twisted_torus(
|
||||
self.major_radius,
|
||||
self.minor_radius,
|
||||
self.major_segments,
|
||||
self.minor_segments,
|
||||
self.twists
|
||||
)
|
||||
mesh = bpy.data.meshes.new('TwistedTorus')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj.data = mesh
|
||||
for material in oldmesh.materials:
|
||||
obj.data.materials.append(material)
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
obj.data.name = oldmeshname
|
||||
else:
|
||||
verts, faces = add_twisted_torus(
|
||||
self.major_radius,
|
||||
self.minor_radius,
|
||||
self.major_segments,
|
||||
self.minor_segments,
|
||||
self.twists
|
||||
)
|
||||
mesh = bpy.data.meshes.new('TwistedTorus')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
|
||||
obj.data["TwistedTorus"] = True
|
||||
obj.data["change"] = False
|
||||
for prm in TwistedTorusParameters():
|
||||
obj.data[prm] = getattr(self, prm)
|
||||
|
||||
if bpy.context.mode == "EDIT_MESH":
|
||||
active_object = context.active_object
|
||||
name_active_object = active_object.name
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
verts, faces = add_twisted_torus(
|
||||
self.major_radius,
|
||||
self.minor_radius,
|
||||
self.major_segments,
|
||||
self.minor_segments,
|
||||
self.twists
|
||||
)
|
||||
mesh = bpy.data.meshes.new('TwistedTorus')
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
obj = object_utils.object_data_add(context, mesh, operator=self)
|
||||
obj.select_set(True)
|
||||
active_object.select_set(True)
|
||||
bpy.context.view_layer.objects.active = active_object
|
||||
bpy.ops.object.join()
|
||||
context.active_object.name = name_active_object
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
if use_enter_edit_mode:
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# restore pre operator state
|
||||
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def TwistedTorusParameters():
|
||||
TwistedTorusParameters = [
|
||||
"major_radius",
|
||||
"minor_radius",
|
||||
"major_segments",
|
||||
"minor_segments",
|
||||
"twists",
|
||||
"method",
|
||||
"abso_major_rad",
|
||||
"abso_minor_rad",
|
||||
]
|
||||
return TwistedTorusParameters
|
||||
@@ -0,0 +1,144 @@
|
||||
# SPDX-FileCopyrightText: 2015-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Originals by meta-androcto, Pablo Vazquez, Liero, Richard Wilks
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy_extras import object_utils
|
||||
|
||||
|
||||
def object_origin(width, height, depth):
|
||||
"""
|
||||
This function takes inputs and returns vertex and face arrays.
|
||||
no actual mesh data creation is done here.
|
||||
"""
|
||||
verts = [(+0.0, +0.0, +0.0)]
|
||||
faces = []
|
||||
|
||||
# apply size
|
||||
for i, v in enumerate(verts):
|
||||
verts[i] = v[0] * width, v[1] * depth, v[2] * height
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
class AddVert(Operator):
|
||||
bl_idname = "mesh.primitive_vert_add"
|
||||
bl_label = "Single Vert"
|
||||
bl_description = "Add a Single Vertice to Edit Mode"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
mesh = bpy.data.meshes.new("Vert")
|
||||
mesh.vertices.add(1)
|
||||
|
||||
object_utils.object_data_add(context, mesh, operator=None)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class AddEmptyVert(Operator):
|
||||
bl_idname = "mesh.primitive_emptyvert_add"
|
||||
bl_label = "Empty Object Origin"
|
||||
bl_description = "Add an Object Origin to Edit Mode"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
mesh = bpy.data.meshes.new("Vert")
|
||||
mesh.vertices.add(1)
|
||||
|
||||
object_utils.object_data_add(context, mesh, operator=None)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.delete(type='VERT')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def Add_Symmetrical_Empty():
|
||||
|
||||
bpy.ops.mesh.primitive_plane_add(enter_editmode=True)
|
||||
|
||||
sempty = bpy.context.object
|
||||
sempty.name = "SymmEmpty"
|
||||
|
||||
# check if we have a mirror modifier, otherwise add
|
||||
if not any(mod.type == 'MIRROR' for mod in sempty.modifiers):
|
||||
bpy.ops.object.modifier_add(type='MIRROR')
|
||||
|
||||
# Delete all!
|
||||
bpy.ops.mesh.select_all(action='TOGGLE')
|
||||
bpy.ops.mesh.select_all(action='TOGGLE')
|
||||
bpy.ops.mesh.delete(type='VERT')
|
||||
|
||||
|
||||
def Add_Symmetrical_Vert():
|
||||
|
||||
bpy.ops.mesh.primitive_plane_add(enter_editmode=True)
|
||||
|
||||
sempty = bpy.context.object
|
||||
sempty.name = "SymmVert"
|
||||
|
||||
# check if we have a mirror modifier, otherwise add
|
||||
if not any(mod.type == 'MIRROR' for mod in sempty.modifiers):
|
||||
bpy.ops.object.modifier_add(type='MIRROR')
|
||||
|
||||
# Delete all!
|
||||
bpy.ops.mesh.select_all(action='TOGGLE')
|
||||
bpy.ops.mesh.select_all(action='TOGGLE')
|
||||
bpy.ops.mesh.merge(type='CENTER')
|
||||
|
||||
|
||||
class AddSymmetricalEmpty(Operator):
|
||||
bl_idname = "mesh.primitive_symmetrical_empty_add"
|
||||
bl_label = "Add Symmetrical Object Origin"
|
||||
bl_description = "Object Origin with a Mirror Modifier for symmetrical modeling"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
mirror = next(mod for mod in bpy.context.object.modifiers
|
||||
if mod.type == 'MIRROR')
|
||||
|
||||
layout.prop(mirror, "use_clip", text="Use Clipping")
|
||||
|
||||
layout.label(text="Mirror Axis")
|
||||
col = layout.column(align=True)
|
||||
col.prop(mirror, "use_axis")
|
||||
col.prop(mirror, "use_axis")
|
||||
col.prop(mirror, "use_axis")
|
||||
|
||||
def execute(self, context):
|
||||
Add_Symmetrical_Empty()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class AddSymmetricalVert(Operator):
|
||||
bl_idname = "mesh.primitive_symmetrical_vert_add"
|
||||
bl_label = "Add Symmetrical Origin & Vert"
|
||||
bl_description = "Object Origin with a Mirror Modifier for symmetrical modeling"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
mirror = next(mod for mod in bpy.context.object.modifiers
|
||||
if mod.type == 'MIRROR')
|
||||
|
||||
layout.prop(mirror, "use_clip", text="Use Clipping")
|
||||
|
||||
layout.label(text="Mirror Axis")
|
||||
col = layout.column(align=True)
|
||||
col.prop(mirror, "use_axis")
|
||||
col.prop(mirror, "use_axis")
|
||||
col.prop(mirror, "use_axis")
|
||||
|
||||
def execute(self, context):
|
||||
Add_Symmetrical_Vert()
|
||||
|
||||
return {'FINISHED'}
|
||||
@@ -0,0 +1,12 @@
|
||||
schema_version = "1.0.0"
|
||||
id = "extra_mesh_objects"
|
||||
name = "Extra Mesh Objects"
|
||||
version = "0.4.0"
|
||||
tagline = "Add extra mesh object types"
|
||||
maintainer = "Community"
|
||||
type = "add-on"
|
||||
tags = ["Add Mesh"]
|
||||
blender_version_min = "4.2.0"
|
||||
license = ["SPDX:GPL-3.0-or-later"]
|
||||
website = "https://projects.blender.org/extensions/add_mesh_extra_objects"
|
||||
copyright = ["2024 Multiple Authors"]
|
||||
@@ -0,0 +1,9 @@
|
||||
import bpy
|
||||
|
||||
def draw_transform_props(self, layout):
|
||||
if hasattr(self, 'align'):
|
||||
layout.prop(self, 'align', expand=False)
|
||||
if hasattr(self, 'location'):
|
||||
layout.prop(self, 'location', expand=True)
|
||||
if hasattr(self, 'rotation'):
|
||||
layout.prop(self, 'rotation', expand=True)
|
||||
@@ -0,0 +1,58 @@
|
||||
import bpy
|
||||
|
||||
|
||||
class AddMeshExtraObjectsPreferences(bpy.types.AddonPreferences):
|
||||
bl_idname = __package__
|
||||
|
||||
show_round_cube: bpy.props.BoolProperty(
|
||||
name = "Round Cube",
|
||||
default = True,
|
||||
)
|
||||
show_single_vert: bpy.props.BoolProperty(
|
||||
name = "Single Vert Menu",
|
||||
default = True,
|
||||
)
|
||||
show_torus_objects: bpy.props.BoolProperty(
|
||||
name = "Torus Objects Menu",
|
||||
default = True,
|
||||
)
|
||||
show_math_functions: bpy.props.BoolProperty(
|
||||
name = "Math Functions Menu",
|
||||
default = True,
|
||||
)
|
||||
show_gears: bpy.props.BoolProperty(
|
||||
name = "Gears Menu",
|
||||
default = True,
|
||||
)
|
||||
show_pipe_joints: bpy.props.BoolProperty(
|
||||
name = "Pipe Joints Menu",
|
||||
default = True,
|
||||
)
|
||||
show_gemstones: bpy.props.BoolProperty(
|
||||
name = "Gemstones Menu",
|
||||
default = True,
|
||||
)
|
||||
show_extras: bpy.props.BoolProperty(
|
||||
name = "Extras Menu",
|
||||
default = True,
|
||||
)
|
||||
show_parent_to_empty: bpy.props.BoolProperty(
|
||||
name = "Parent to Empty",
|
||||
default = True,
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column(heading="Filter Add Menu Items")
|
||||
col.prop(self, "show_round_cube")
|
||||
col.prop(self, "show_single_vert")
|
||||
col.prop(self, "show_torus_objects")
|
||||
col.prop(self, "show_math_functions")
|
||||
col.prop(self, "show_gears")
|
||||
col.prop(self, "show_pipe_joints")
|
||||
col.prop(self, "show_gemstones")
|
||||
col.prop(self, "show_extras")
|
||||
col.prop(self, "show_parent_to_empty")
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 0.5
|
||||
op.arc_div = 8
|
||||
op.lin_div = 0
|
||||
op.size = (0.0, 0.0, 3.0)
|
||||
op.div_type = 'CORNERS'
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 0.4
|
||||
op.arc_div = 8
|
||||
op.lin_div = 0
|
||||
op.size = (1.5, 3.0, 1.0)
|
||||
op.div_type = 'ALL'
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 0.0
|
||||
op.arc_div = 1
|
||||
op.lin_div = 0
|
||||
op.size = (2.0, 2.0, 2.0)
|
||||
op.div_type = 'CORNERS'
|
||||
op.odd_axis_align = False
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 0
|
||||
op.size = (2, 2, 2)
|
||||
op.lin_div = 5
|
||||
op.div_type = 'ALL'
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 1.0
|
||||
op.arc_div = 1
|
||||
op.lin_div = 0
|
||||
op.size = (0.0, 0.0, 0.0)
|
||||
op.div_type = 'CORNERS'
|
||||
op.odd_axis_align = True
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 1.0
|
||||
op.arc_div = 8
|
||||
op.lin_div = 0
|
||||
op.size = (0.0, 0.0, 0.0)
|
||||
op.div_type = 'CORNERS'
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.radius = 0.25
|
||||
op.arc_div = 8
|
||||
op.lin_div = 0
|
||||
op.size = (2.0, 2.0, 2.0)
|
||||
op.div_type = 'CORNERS'
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "4"
|
||||
op.vTrunc = 1
|
||||
op.eTrunc = 1
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 1.1338
|
||||
op.eTrunc = 1
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 1.0938
|
||||
op.eTrunc = 1
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 1.0572
|
||||
op.eTrunc = 0.585786
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "20"
|
||||
op.vTrunc = 0.921
|
||||
op.eTrunc = 0.553
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 1
|
||||
op.eTrunc = 0
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 1.1235
|
||||
op.eTrunc = 0.68
|
||||
op.dual = 1
|
||||
op.snub = "Left"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 1.0875
|
||||
op.eTrunc = 0.704
|
||||
op.dual = 1
|
||||
op.snub = "Left"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "20"
|
||||
op.vTrunc = 2 / 3
|
||||
op.eTrunc = 0
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "4"
|
||||
op.vTrunc = 1
|
||||
op.eTrunc = 1
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 1
|
||||
op.eTrunc = 0
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 1.1338
|
||||
op.eTrunc = 1
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 1.0938
|
||||
op.eTrunc = 1
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 1.0875
|
||||
op.eTrunc = 0.704
|
||||
op.dual = 0
|
||||
op.snub = "Left"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 1.1235
|
||||
op.eTrunc = 0.68
|
||||
op.dual = 0
|
||||
op.snub = "Left"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "8"
|
||||
op.vTrunc = 2 / 3
|
||||
op.eTrunc = 0
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 2 / 3
|
||||
op.eTrunc = 0
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 2/3
|
||||
op.eTrunc = 0
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "4"
|
||||
op.vTrunc = 2 / 3
|
||||
op.eTrunc = 0
|
||||
op.dual = 1
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 2/3
|
||||
op.eTrunc = 0
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "6"
|
||||
op.vTrunc = 1.0572
|
||||
op.eTrunc = 0.585786
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "12"
|
||||
op.vTrunc = 2 / 3
|
||||
op.eTrunc = 0
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "20"
|
||||
op.vTrunc = 2 / 3
|
||||
op.eTrunc = 0
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "20"
|
||||
op.vTrunc = 0.921
|
||||
op.eTrunc = 0.553
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "8"
|
||||
op.vTrunc = 2/3
|
||||
op.eTrunc = 0
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.source = "4"
|
||||
op.vTrunc = 2/3
|
||||
op.eTrunc = 0
|
||||
op.dual = 0
|
||||
op.snub = "None"
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'u'
|
||||
op.y_eq = 'cos(u)*sin(v)'
|
||||
op.z_eq = 'cos(u)*cos(v)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '2/3* (cos(u)* cos(2*v) + sqrt(2)* sin(u)* cos(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v))'
|
||||
op.y_eq = 'sqrt(2)* cos(u)* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v))'
|
||||
op.z_eq = '2/3* (cos(u)* sin(2*v) - sqrt(2)* sin(u)* sin(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v))'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 3.1415927410125732
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 3.1415927410125732
|
||||
op.range_v_step = 64
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'u-sin(u)*cosh(v)'
|
||||
op.y_eq = '4*sin(1/2*u)*sinh(v/2)'
|
||||
op.z_eq = '1-cos(u)*cosh(v)'
|
||||
op.range_u_min = -3.1415927410125732
|
||||
op.range_u_max = 9.42477798461914
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -2.0
|
||||
op.range_v_max = 2.0
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '2*cosh(v/2)*cos(u)'
|
||||
op.y_eq = 'v'
|
||||
op.z_eq = '2*cosh(v/2)*sin(u)'
|
||||
op.range_u_min = -3.1415927410125732
|
||||
op.range_u_max = 3.1415927410125732
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = True
|
||||
op.range_v_min = -3.1415927410125732
|
||||
op.range_v_max = 3.1415927410125732
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'cos(u+v)/(sqrt(2.)+cos(v-u))'
|
||||
op.y_eq = 'sin(v-u)/(sqrt(2.)+cos(v-u))'
|
||||
op.z_eq = 'sin(u+v)/(sqrt(2.)+cos(v-u))'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 3.140000104904175
|
||||
op.range_u_step = 8
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'v*cos(u)'
|
||||
op.y_eq = 'v*sin(u)'
|
||||
op.z_eq = '0.4*u'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 12.566370964050293
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 2.0
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'u'
|
||||
op.y_eq = 'sin(pi*((u)**2+(v)**2))/2'
|
||||
op.z_eq = 'v'
|
||||
op.range_u_min = -1.0
|
||||
op.range_u_max = 1.0
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -1.0
|
||||
op.range_v_max = 1.0
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'a*cos(u)*sin(v)'
|
||||
op.y_eq = 'a*sin(u)*sin(v)'
|
||||
op.z_eq = '(cos(v)+log(tan(v/2)+1e-2)) + b*u'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 12.566370964050293
|
||||
op.range_u_step = 128
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 2.0
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '1'
|
||||
op.b_eq = '0.2'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'u -u**3/3 + u*v**2'
|
||||
op.y_eq = 'u**2 - v**2'
|
||||
op.z_eq = 'v -v**3/3 + v*u**2'
|
||||
op.range_u_min = -2.0
|
||||
op.range_u_max = 2.0
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -2.0
|
||||
op.range_v_max = 2.0
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'sinh(v)*sin(u)'
|
||||
op.y_eq = '3*u'
|
||||
op.z_eq = '-sinh(v)*cos(u)'
|
||||
op.range_u_min = -3.1415927410125732
|
||||
op.range_u_max = 3.1415927410125732
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -3.1415927410125732
|
||||
op.range_v_max = 3.1415927410125732
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '(1-0.1*cos(v))*cos(u)'
|
||||
op.y_eq = '0.1*(sin(v) + u/1.7 -10)'
|
||||
op.z_eq = '(1-0.1*cos(v))*sin(u)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 12.566370964050293
|
||||
op.range_u_step = 128
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'cos(v)**3*cos(u)**3'
|
||||
op.y_eq = 'sin(u)**3'
|
||||
op.z_eq = 'sin(v)**3*cos(u)**3'
|
||||
op.range_u_min = -1.2999999523162842
|
||||
op.range_u_max = 1.2999999523162842
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '(sinh(v)*cos(3*u))/(1+cosh(u)*cosh(v))'
|
||||
op.y_eq = '(cosh(v)*sinh(u))/(1+cosh(u)*cosh(v))'
|
||||
op.z_eq = '(sinh(v)*sin(3*u))/(1+cosh(u)*cosh(v))'
|
||||
op.range_u_min = -3.1415927410125732
|
||||
op.range_u_max = 3.1415927410125732
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -3.1415927410125732
|
||||
op.range_v_max = 3.1415927410125732
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '(3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u))*cos(v)'
|
||||
op.y_eq = '(4+2*(1-cos(v)/2)*cos(u))*sin(v)'
|
||||
op.z_eq = '-2*(1-cos(v)/2)*sin(u)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = True
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'cos(v)+u*cos(v/2)*cos(v)'
|
||||
op.y_eq = 'u*sin(v/2)'
|
||||
op.z_eq = 'sin(v)+u*cos(v/2)*sin(v)'
|
||||
op.range_u_min = -0.4000000059604645
|
||||
op.range_u_max = 0.4000000059604645
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '2.2*(2*cosh(v/2)*cos(u)) '
|
||||
op.y_eq = '1.51166 * (2*cosh(v/2)*sin(u) * sin((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513) + 1.8*(v) * cos((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513)) '
|
||||
op.z_eq = '1.51166 * (2*cosh(v/2)*sin(u) * cos((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513) - 1.8*(v) * sin((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513)) '
|
||||
op.range_u_min = -3.1415927410125732
|
||||
op.range_u_max = 3.1415927410125732
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -3.1415927410125732
|
||||
op.range_v_max = 3.1415927410125732
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'cos(u)*cos(v)+sin((sin(u)+1)*2*pi) '
|
||||
op.y_eq = '4*sin(u) '
|
||||
op.z_eq = 'cos(u)*sin(v)+cos((sin(u)+1)*2*pi) '
|
||||
op.range_u_min = -1.5707963705062866
|
||||
op.range_u_max = 1.5707963705062866
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'a*cos(u)+(b*sin(f*u)+c)*cos(u)*cos(v)'
|
||||
op.y_eq = 'a*sin(u)+(b*sin(f*u)+c)*sin(u)*cos(v)'
|
||||
op.z_eq = '(b*sin(f*u)+c)*sin(v)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 128
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '5'
|
||||
op.b_eq = '0.6'
|
||||
op.c_eq = '2'
|
||||
op.f_eq = '10'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'cos(v)*(1+cos(u))*sin(v/8)'
|
||||
op.y_eq = 'sin(u)*sin(v/8)+cos(v/8)*1.5'
|
||||
op.z_eq = 'sin(v)*(1+cos(u))*sin(v/8)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = True
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 12.566370964050293
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'sin(u)'
|
||||
op.y_eq = 'sin(v)'
|
||||
op.z_eq = 'sin(u+v)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 128
|
||||
op.wrap_u = True
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = True
|
||||
op.close_v = True
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '1.2*(1 -v/(2*pi))*cos(3*v)*(1 + cos(u)) + 3*cos(3*v)'
|
||||
op.y_eq = '9*v/(2*pi) + 1.2*(1 - v/(2*pi))*sin(u)'
|
||||
op.z_eq = '1.2*(1 -v/(2*pi))*sin(3*v)*(1 + cos(u)) + 3*sin(3*v)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 64
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '2.*u/(u*u+v*v+1.)'
|
||||
op.y_eq = '(u*u+v*v-1.)/(u*u+v*v+1.)'
|
||||
op.z_eq = '2.*v/(u*u+v*v+1.)'
|
||||
op.range_u_min = -2.0
|
||||
op.range_u_max = 2.0
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = -2.0
|
||||
op.range_v_max = 2.0
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = '(1+0.5*cos(u))*cos(v)'
|
||||
op.y_eq = '0.5*sin(u)'
|
||||
op.z_eq = '(1+0.5*cos(u))*sin(v)'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 32
|
||||
op.wrap_u = False
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 128
|
||||
op.wrap_v = False
|
||||
op.close_v = False
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
op = bpy.context.active_operator
|
||||
|
||||
op.x_eq = 'cos(u)*(6-(5./4. + sin(3*v))*sin(v-3*u))'
|
||||
op.y_eq = '(6-(5./4. + sin(3*v))*sin(v-3*u))*sin(u)'
|
||||
op.z_eq = '-cos(v-3*u)*(5./4.+sin(3*v))'
|
||||
op.range_u_min = 0.0
|
||||
op.range_u_max = 6.2831854820251465
|
||||
op.range_u_step = 128
|
||||
op.wrap_u = True
|
||||
op.range_v_min = 0.0
|
||||
op.range_v_max = 6.2831854820251465
|
||||
op.range_v_step = 32
|
||||
op.wrap_v = True
|
||||
op.close_v = True
|
||||
op.n_eq = 1
|
||||
op.a_eq = '0'
|
||||
op.b_eq = '0'
|
||||
op.c_eq = '0'
|
||||
op.f_eq = '0'
|
||||
op.g_eq = '0'
|
||||
op.h_eq = '0'
|
||||
Reference in New Issue
Block a user