basically finish rail adder
This commit is contained in:
parent
b5565d796a
commit
f9502fe2d4
@ -10,163 +10,360 @@ from . import UTIL_functions, UTIL_naming_convension
|
|||||||
# BallRadius is the radius of player ball. It always is 2.
|
# BallRadius is the radius of player ball. It always is 2.
|
||||||
# Ref: https://tieba.baidu.com/p/6557180791
|
# Ref: https://tieba.baidu.com/p/6557180791
|
||||||
|
|
||||||
#region Operators
|
#region Operator Helpers
|
||||||
|
|
||||||
|
class SharedRailSectionInputProperty():
|
||||||
|
"""
|
||||||
|
This class is served for user to pick the transition type of rail.
|
||||||
|
And order rail radius and span accoridng to user picked rail type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rail_type: bpy.props.EnumProperty(
|
||||||
|
name = "Type",
|
||||||
|
description = "Rail type",
|
||||||
|
items = [
|
||||||
|
('MONORAIL', "Monorail", ""),
|
||||||
|
('RAIL', "Rail", ""),
|
||||||
|
],
|
||||||
|
default = 'RAIL',
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
class SharedRailInputProperty():
|
|
||||||
rail_radius: bpy.props.FloatProperty(
|
rail_radius: bpy.props.FloatProperty(
|
||||||
name = "Rail Radius",
|
name = "Radius",
|
||||||
description = "Define rail section radius",
|
description = "Define rail section radius",
|
||||||
default = 0.35,
|
default = 0.35,
|
||||||
min = 0,
|
min = 0,
|
||||||
|
unit = 'LENGTH'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
rail_span: bpy.props.FloatProperty(
|
rail_span: bpy.props.FloatProperty(
|
||||||
name = "Rail Span",
|
name = "Span",
|
||||||
description = "The length between 2 single rails.",
|
description = "The length between 2 single rails.",
|
||||||
default = 3.75,
|
default = 3.75,
|
||||||
min = 0,
|
min = 0,
|
||||||
|
unit = 'LENGTH'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
rail_length: bpy.props.FloatProperty(
|
def draw_rail_section_input(self, layout: bpy.types.UILayout, force_monorail: bool | None) -> None:
|
||||||
name = "Rail Length",
|
"""
|
||||||
description = "The length of this rail.",
|
Draw rail section properties
|
||||||
default = 5.0,
|
|
||||||
min = 0,
|
|
||||||
step = 50, # same unit as BME Struct
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
rail_cap: bpy.props.BoolProperty(
|
@param force_monorail[in] Force this draw method for monorail if True, or for rail if False. Accept None if you want user to choose it.
|
||||||
name = 'Rail Cap',
|
"""
|
||||||
description = 'Whether this rail should have terminal cap.',
|
if force_monorail is None:
|
||||||
default = False
|
# show picker to allow user pick
|
||||||
) # type: ignore
|
layout.prop(self, 'rail_type', expand = True)
|
||||||
|
# show radius
|
||||||
def draw_rail_radius_input(self, layout: bpy.types.UILayout) -> None:
|
layout.prop(self, "rail_radius")
|
||||||
layout.prop(self, "rail_radius")
|
# show span for rail
|
||||||
def draw_rail_span_input(self, layout: bpy.types.UILayout) -> None:
|
if self.rail_type == 'RAIL':
|
||||||
layout.prop(self, "rail_span")
|
layout.prop(self, "rail_span")
|
||||||
def draw_rail_length_input(self, layout: bpy.types.UILayout) -> None:
|
else:
|
||||||
layout.prop(self, "rail_length")
|
# according to force type to show
|
||||||
def draw_rail_cap_input(self, layout: bpy.types.UILayout) -> None:
|
# always show radius
|
||||||
layout.prop(self, "rail_cap")
|
layout.prop(self, "rail_radius")
|
||||||
|
# show span in condition
|
||||||
|
if not force_monorail:
|
||||||
|
layout.prop(self, "rail_span")
|
||||||
|
|
||||||
|
def general_get_is_monorail(self) -> bool:
|
||||||
|
return self.rail_type == 'MONORAIL'
|
||||||
def general_get_rail_radius(self) -> float:
|
def general_get_rail_radius(self) -> float:
|
||||||
return self.rail_radius
|
return self.rail_radius
|
||||||
def general_get_rail_span(self) -> float:
|
def general_get_rail_span(self) -> float:
|
||||||
return self.rail_span
|
return self.rail_span
|
||||||
|
|
||||||
|
class SharedRailCapInputProperty():
|
||||||
|
"""
|
||||||
|
This class provide properties for cap switch.
|
||||||
|
Support head cap and tail cap. Both straight and screw rail can use this.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rail_start_cap: bpy.props.BoolProperty(
|
||||||
|
name = 'Start Cap',
|
||||||
|
description = 'Whether this rail should have cap at start terminal.',
|
||||||
|
default = False
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
rail_end_cap: bpy.props.BoolProperty(
|
||||||
|
name = 'End Cap',
|
||||||
|
description = 'Whether this rail should have cap at end terminal.',
|
||||||
|
default = False
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
def draw_rail_cap_input(self, layout: bpy.types.UILayout) -> None:
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(self, "rail_start_cap", toggle = 1)
|
||||||
|
row.prop(self, "rail_end_cap", toggle = 1)
|
||||||
|
|
||||||
|
def general_get_rail_start_cap(self) -> bool:
|
||||||
|
return self.rail_start_cap
|
||||||
|
def general_get_rail_end_cap(self) -> bool:
|
||||||
|
return self.rail_end_cap
|
||||||
|
|
||||||
|
class SharedStraightRailInputProperty():
|
||||||
|
"""
|
||||||
|
The properties for straight rail.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rail_length: bpy.props.FloatProperty(
|
||||||
|
name = "Length",
|
||||||
|
description = "The length of this rail.",
|
||||||
|
default = 5.0,
|
||||||
|
min = 0,
|
||||||
|
step = 50, # same unit as BME Struct
|
||||||
|
unit = 'LENGTH'
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
def draw_straight_rail_input(self, layout: bpy.types.UILayout) -> None:
|
||||||
|
layout.prop(self, "rail_length")
|
||||||
|
|
||||||
def general_get_rail_length(self) -> float:
|
def general_get_rail_length(self) -> float:
|
||||||
return self.rail_length
|
return self.rail_length
|
||||||
def general_get_rail_cap(self) -> bool:
|
|
||||||
return self.rail_cap
|
|
||||||
|
|
||||||
class BBP_OT_add_monorail_section(SharedRailInputProperty, bpy.types.Operator):
|
class SharedScrewRailInputProperty():
|
||||||
"""Add Monorail Section"""
|
"""
|
||||||
bl_idname = "bbp.add_monorail_section"
|
The properties for straight rail.
|
||||||
bl_label = "Monorail Section"
|
"""
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
def execute(self, context):
|
rail_screw_angle: bpy.props.FloatProperty(
|
||||||
_create_monorail_section(self.general_get_rail_radius())
|
name = "Angle",
|
||||||
return {'FINISHED'}
|
description = "The angle of this screw rail rotated in one interation.",
|
||||||
|
default = 90,
|
||||||
|
subtype = 'ANGLE',
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
def draw(self, context):
|
rail_screw_screw: bpy.props.FloatProperty(
|
||||||
layout = self.layout
|
name = "Screw",
|
||||||
self.draw_rail_radius_input(layout)
|
description = "The increased height in each iteration. Minus height also is accepted.",
|
||||||
|
default = 6,
|
||||||
|
unit = 'LENGTH'
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
class BBP_OT_add_rail_section(SharedRailInputProperty, bpy.types.Operator):
|
rail_screw_iterations: bpy.props.IntProperty(
|
||||||
|
name = "Iterations",
|
||||||
|
description = "The angle of this screw rail rotated in one interation.",
|
||||||
|
default = 1,
|
||||||
|
min = 1,
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
rail_screw_steps: bpy.props.IntProperty(
|
||||||
|
name = "Steps",
|
||||||
|
description = "The segment count per iteration.",
|
||||||
|
default = 20,
|
||||||
|
min = 1,
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
rail_screw_radius: bpy.props.FloatProperty(
|
||||||
|
name = "Radius",
|
||||||
|
description = "The screw radius. Minus radius will flip the built screw.",
|
||||||
|
default = 10,
|
||||||
|
unit = 'LENGTH'
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
|
def draw_screw_rail_input(self, layout: bpy.types.UILayout, show_for_screw: bool) -> None:
|
||||||
|
if show_for_screw:
|
||||||
|
# screw do not need angle property
|
||||||
|
layout.prop(self, "rail_screw_screw")
|
||||||
|
layout.prop(self, "rail_screw_iterations")
|
||||||
|
layout.prop(self, "rail_screw_radius")
|
||||||
|
layout.prop(self, "rail_screw_steps")
|
||||||
|
else:
|
||||||
|
# curve do not need iterations (always is 1)
|
||||||
|
# and do not need screw (always is 0)
|
||||||
|
layout.prop(self, "rail_screw_angle")
|
||||||
|
layout.prop(self, "rail_screw_radius")
|
||||||
|
layout.prop(self, "rail_screw_steps")
|
||||||
|
|
||||||
|
# Getter should return default value if corresponding field
|
||||||
|
# is not existing in that mode.
|
||||||
|
|
||||||
|
def general_get_rail_screw_angle(self, is_for_screw: bool) -> float:
|
||||||
|
"""This function return angle in degree unit."""
|
||||||
|
return 360 if is_for_screw else self.rail_screw_angle
|
||||||
|
def general_get_rail_screw_screw(self, is_for_screw: bool) -> float:
|
||||||
|
return self.rail_screw_screw if is_for_screw else 0
|
||||||
|
def general_get_rail_screw_iterations(self, is_for_screw: bool) -> int:
|
||||||
|
return self.rail_screw_iterations if is_for_screw else 1
|
||||||
|
def general_get_rail_screw_radius(self) -> float:
|
||||||
|
return self.rail_screw_radius
|
||||||
|
def general_get_rail_screw_steps(self) -> int:
|
||||||
|
return self.rail_screw_steps
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Operators
|
||||||
|
|
||||||
|
class BBP_OT_add_rail_section(SharedRailSectionInputProperty, bpy.types.Operator):
|
||||||
"""Add Rail Section"""
|
"""Add Rail Section"""
|
||||||
bl_idname = "bbp.add_rail_section"
|
bl_idname = "bbp.add_rail_section"
|
||||||
bl_label = "Rail Section"
|
bl_label = "Rail Section"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
_create_rail_section(self.general_get_rail_radius(), self.general_get_rail_span())
|
_rail_creator_wrapper(
|
||||||
|
lambda bm: _create_rail_section(
|
||||||
|
bm,
|
||||||
|
self.general_get_is_monorail(), self.general_get_rail_radius(), self.general_get_rail_span()
|
||||||
|
)
|
||||||
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
self.draw_rail_radius_input(layout)
|
self.draw_rail_section_input(layout, None)
|
||||||
self.draw_rail_span_input(layout)
|
|
||||||
|
|
||||||
class BBP_OT_add_transition_section(SharedRailInputProperty, bpy.types.Operator):
|
class BBP_OT_add_transition_section(SharedRailSectionInputProperty, bpy.types.Operator):
|
||||||
"""Add Transition Section"""
|
"""Add Transition Section"""
|
||||||
bl_idname = "bbp.add_transition_section"
|
bl_idname = "bbp.add_transition_section"
|
||||||
bl_label = "Transition Section"
|
bl_label = "Transition Section"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# calc sink
|
_rail_creator_wrapper(
|
||||||
radius_: float = self.general_get_rail_radius()
|
lambda bm: _create_transition_section(
|
||||||
span_: float = self.general_get_rail_span()
|
bm,
|
||||||
sink_: float
|
self.general_get_rail_radius(), self.general_get_rail_span()
|
||||||
try:
|
)
|
||||||
sink_ = math.sqrt((radius_ + 2) ** 2 - (span_ / 2) ** 2) - 2 - radius_
|
|
||||||
except:
|
|
||||||
sink_ = -2 # if sqrt(minus number) happended, it mean no triangle relation. the depth should always be -2.
|
|
||||||
|
|
||||||
# create section
|
|
||||||
_create_transition_section(radius_, span_, sink_)
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
self.draw_rail_radius_input(layout)
|
|
||||||
self.draw_rail_span_input(layout)
|
|
||||||
|
|
||||||
class BBP_OT_add_straight_monorail(SharedRailInputProperty, bpy.types.Operator):
|
|
||||||
"""Add Straight Monorail"""
|
|
||||||
bl_idname = "bbp.add_straight_monorail"
|
|
||||||
bl_label = "Straight Monorail"
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
_create_straight_monorail(
|
|
||||||
self.general_get_rail_radius(),
|
|
||||||
self.general_get_rail_length(),
|
|
||||||
self.general_get_rail_cap()
|
|
||||||
)
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
self.draw_rail_radius_input(layout)
|
# force show double rail params
|
||||||
self.draw_rail_length_input(layout)
|
self.draw_rail_section_input(layout, False)
|
||||||
self.draw_rail_cap_input(layout)
|
|
||||||
|
|
||||||
class BBP_OT_add_straight_rail(SharedRailInputProperty, bpy.types.Operator):
|
class BBP_OT_add_straight_rail(SharedRailSectionInputProperty, SharedRailCapInputProperty, SharedStraightRailInputProperty, bpy.types.Operator):
|
||||||
"""Add Straight Rail"""
|
"""Add Straight Rail"""
|
||||||
bl_idname = "bbp.add_straight_rail"
|
bl_idname = "bbp.add_straight_rail"
|
||||||
bl_label = "Straight Rail"
|
bl_label = "Straight Rail"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
_create_straight_rail(
|
_rail_creator_wrapper(
|
||||||
self.general_get_rail_radius(),
|
lambda bm: _create_straight_rail(
|
||||||
self.general_get_rail_span(),
|
bm,
|
||||||
self.general_get_rail_length(),
|
self.general_get_is_monorail(), self.general_get_rail_radius(), self.general_get_rail_span(),
|
||||||
self.general_get_rail_cap()
|
self.general_get_rail_length(),
|
||||||
|
self.general_get_rail_start_cap(), self.general_get_rail_end_cap()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
self.draw_rail_radius_input(layout)
|
self.draw_rail_section_input(layout, None)
|
||||||
self.draw_rail_span_input(layout)
|
layout.separator()
|
||||||
self.draw_rail_length_input(layout)
|
|
||||||
self.draw_rail_cap_input(layout)
|
self.draw_rail_cap_input(layout)
|
||||||
|
layout.separator()
|
||||||
|
self.draw_straight_rail_input(layout)
|
||||||
|
|
||||||
|
class BBP_OT_add_screw_rail(SharedRailSectionInputProperty, SharedRailCapInputProperty, SharedScrewRailInputProperty, bpy.types.Operator):
|
||||||
|
"""Add Screw Rail"""
|
||||||
|
bl_idname = "bbp.add_screw_rail"
|
||||||
|
bl_label = "Screw Rail"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
_rail_creator_wrapper(
|
||||||
|
lambda bm: _create_screw_rail(
|
||||||
|
bm,
|
||||||
|
self.general_get_is_monorail(), self.general_get_rail_radius(), self.general_get_rail_span(),
|
||||||
|
self.general_get_rail_start_cap(), self.general_get_rail_end_cap(),
|
||||||
|
self.general_get_rail_screw_angle(True), self.general_get_rail_screw_screw(True), self.general_get_rail_screw_iterations(True),
|
||||||
|
self.general_get_rail_screw_steps(), self.general_get_rail_screw_radius()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
self.draw_rail_section_input(layout, None)
|
||||||
|
layout.separator()
|
||||||
|
self.draw_rail_cap_input(layout)
|
||||||
|
layout.separator()
|
||||||
|
self.draw_screw_rail_input(layout, True)
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Modifier Adder
|
#region BMesh Operations Helper
|
||||||
|
|
||||||
def _set_screw_modifier(obj: bpy.types.Object) -> None:
|
def _bmesh_extrude(bm: bmesh.types.BMesh, start_edges: list[bmesh.types.BMEdge], direction: mathutils.Vector) -> list[bmesh.types.BMEdge]:
|
||||||
pass
|
# extrude
|
||||||
|
ret: dict[str, typing.Any] = bmesh.ops.extrude_edge_only(
|
||||||
|
bm,
|
||||||
|
edges = start_edges,
|
||||||
|
use_normal_flip = False, use_select_history = False
|
||||||
|
)
|
||||||
|
|
||||||
|
# get end edges
|
||||||
|
ret_geom = ret['geom']
|
||||||
|
del ret
|
||||||
|
end_verts: list[bmesh.types.BMVert] = list(filter(lambda x: isinstance(x, bmesh.types.BMVert), ret_geom))
|
||||||
|
end_edges: list[bmesh.types.BMEdge] = list(filter(lambda x: isinstance(x, bmesh.types.BMEdge) and x.is_boundary, ret_geom))
|
||||||
|
# and move it
|
||||||
|
bmesh.ops.translate(
|
||||||
|
bm,
|
||||||
|
vec = direction, space = mathutils.Matrix.Identity(4),
|
||||||
|
verts = end_verts,
|
||||||
|
use_shapekey = False
|
||||||
|
)
|
||||||
|
|
||||||
|
# return value
|
||||||
|
return end_edges
|
||||||
|
|
||||||
|
def _bmesh_screw(
|
||||||
|
bm: bmesh.types.BMesh,
|
||||||
|
start_verts: list[bmesh.types.BMVert], start_edges: list[bmesh.types.BMEdge],
|
||||||
|
angle: float, steps: int, iterations: int,
|
||||||
|
center: mathutils.Vector, screw_per_iteration: float) -> list[bmesh.types.BMEdge]:
|
||||||
|
# screw
|
||||||
|
ret: dict[str, typing.Any] = bmesh.ops.spin(
|
||||||
|
bm,
|
||||||
|
geom = start_edges,
|
||||||
|
cent = center,
|
||||||
|
axis = mathutils.Vector((0, 0, 1)), # default to +Z
|
||||||
|
dvec = mathutils.Vector((0, 0, screw_per_iteration / steps)), # conv to step delta
|
||||||
|
angle = angle * iterations,
|
||||||
|
space = mathutils.Matrix.Identity(4),
|
||||||
|
steps = steps * iterations,
|
||||||
|
use_merge = False,
|
||||||
|
use_normal_flip = True, # NOTE: flip nml according to real test result
|
||||||
|
use_duplicate = False
|
||||||
|
)
|
||||||
|
|
||||||
|
# return last segment
|
||||||
|
geom_last = ret['geom_last']
|
||||||
|
del ret
|
||||||
|
return list(filter(lambda x: isinstance(x, bmesh.types.BMEdge), geom_last))
|
||||||
|
|
||||||
|
def _bmesh_cap(bm: bmesh.types.BMesh, edges: list[bmesh.types.BMEdge]) -> None:
|
||||||
|
# fill holes
|
||||||
|
bmesh.ops.triangle_fill(
|
||||||
|
bm,
|
||||||
|
use_beauty = False, use_dissolve = False,
|
||||||
|
edges = edges
|
||||||
|
# no pass to normal.
|
||||||
|
)
|
||||||
|
|
||||||
|
def _bmesh_mark_sharp(bm: bmesh.types.BMesh, edges: typing.Iterable[list[bmesh.types.BMEdge]]) -> None:
|
||||||
|
# Ref: https://blender.stackexchange.com/questions/41351/is-there-a-way-to-select-edges-marked-as-sharp-via-python/41352#41352
|
||||||
|
|
||||||
|
# reset all edges to smooth
|
||||||
|
edge: bmesh.types.BMEdge
|
||||||
|
for edge in bm.edges:
|
||||||
|
edge.smooth = True
|
||||||
|
|
||||||
|
# and only set sharp for specified edges
|
||||||
|
for subedges in edges:
|
||||||
|
for edge in subedges:
|
||||||
|
edge.smooth = False
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Polygon Adders
|
#region Real Rail Creators
|
||||||
|
|
||||||
def _polygon_adder_wrapper(fct_poly_cret: typing.Callable[[bmesh.types.BMesh], None]) -> bpy.types.Object:
|
def _rail_creator_wrapper(fct_poly_cret: typing.Callable[[bmesh.types.BMesh], None]) -> bpy.types.Object:
|
||||||
# create mesh first
|
# create mesh first
|
||||||
bm: bmesh.types.BMesh = bmesh.new()
|
bm: bmesh.types.BMesh = bmesh.new()
|
||||||
|
|
||||||
@ -197,178 +394,150 @@ def _polygon_adder_wrapper(fct_poly_cret: typing.Callable[[bmesh.types.BMesh], N
|
|||||||
# return rail
|
# return rail
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def _add_monorail_section(
|
def _create_rail_section(
|
||||||
bm: bmesh.types.BMesh,
|
bm: bmesh.types.BMesh,
|
||||||
matrix: mathutils.Matrix,
|
is_monorail: bool, rail_radius: float, rail_span: float,
|
||||||
rail_radius: float) -> None:
|
matrix: mathutils.Matrix = mathutils.Matrix.Identity(4)) -> None:
|
||||||
"""
|
|
||||||
Add a monorail section.
|
|
||||||
The original point locate at the center of section. The section will be placed in XZ panel.
|
|
||||||
"""
|
|
||||||
# create
|
|
||||||
bmesh.ops.create_circle(
|
|
||||||
bm,
|
|
||||||
cap_ends = False, cap_tris = False,
|
|
||||||
segments = 8,
|
|
||||||
radius = rail_radius,
|
|
||||||
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
|
||||||
None,
|
|
||||||
mathutils.Euler((math.radians(90), math.radians(22.5), 0), 'XYZ'),
|
|
||||||
None
|
|
||||||
)),
|
|
||||||
calc_uvs = False
|
|
||||||
)
|
|
||||||
|
|
||||||
def _add_rail_section(
|
|
||||||
bm: bmesh.types.BMesh,
|
|
||||||
matrix: mathutils.Matrix,
|
|
||||||
rail_radius: float,
|
|
||||||
rail_span: float) -> None:
|
|
||||||
"""
|
"""
|
||||||
Add a rail section.
|
Add a rail section.
|
||||||
The original point locate at the center point of the line connecting between left rail section and right rail section.
|
|
||||||
|
If created is monorail, the original point locate at the center of section.
|
||||||
|
Otherwise, the original point locate at the center point of the line connecting between left rail section and right rail section.
|
||||||
The section will be placed in XZ panel.
|
The section will be placed in XZ panel.
|
||||||
"""
|
|
||||||
# create left one
|
|
||||||
bmesh.ops.create_circle(
|
|
||||||
bm,
|
|
||||||
cap_ends = False, cap_tris = False,
|
|
||||||
segments = 8,
|
|
||||||
radius = rail_radius,
|
|
||||||
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
|
||||||
mathutils.Vector((-rail_span / 2, 0, 0)),
|
|
||||||
mathutils.Euler((math.radians(90), 0, 0), 'XYZ'),
|
|
||||||
None
|
|
||||||
)),
|
|
||||||
calc_uvs = False
|
|
||||||
)
|
|
||||||
# create right one
|
|
||||||
bmesh.ops.create_circle(
|
|
||||||
bm,
|
|
||||||
cap_ends = False, cap_tris = False,
|
|
||||||
segments = 8,
|
|
||||||
radius = rail_radius,
|
|
||||||
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
|
||||||
mathutils.Vector((rail_span / 2, 0, 0)),
|
|
||||||
mathutils.Euler((math.radians(90), 0, 0), 'XYZ'),
|
|
||||||
None
|
|
||||||
)),
|
|
||||||
calc_uvs = False
|
|
||||||
)
|
|
||||||
|
|
||||||
def _add_straight_monorail(
|
If ordered is monorail, `rail_span` param will be ignored.
|
||||||
bm: bmesh.types.BMesh,
|
|
||||||
length: float,
|
|
||||||
matrix: mathutils.Matrix,
|
|
||||||
has_cap: bool,
|
|
||||||
rail_radius: float) -> None:
|
|
||||||
"""
|
"""
|
||||||
Add a straight monorail.
|
if is_monorail:
|
||||||
The original point is same as `_add_monorail_section()`.
|
# create monorail
|
||||||
The start terminal of this straight will be placed in XZ panel.
|
bmesh.ops.create_circle(
|
||||||
The expand direction is +Y.
|
bm, cap_ends = False, cap_tris = False, segments = 8, radius = rail_radius,
|
||||||
"""
|
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
||||||
# create left one
|
None,
|
||||||
bmesh.ops.create_cone(
|
mathutils.Euler((math.radians(90), math.radians(22.5), 0), 'XYZ'),
|
||||||
bm,
|
None
|
||||||
cap_ends = has_cap, cap_tris = True,
|
)),
|
||||||
segments = 8,
|
calc_uvs = False
|
||||||
radius1 = rail_radius, radius2 = rail_radius,
|
)
|
||||||
depth = length,
|
else:
|
||||||
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
# create rail
|
||||||
mathutils.Vector((0, length / 2, 0)),
|
# create left rail
|
||||||
mathutils.Euler((math.radians(90), math.radians(22.5), 0), 'XYZ'),
|
bmesh.ops.create_circle(
|
||||||
None
|
bm, cap_ends = False, cap_tris = False, segments = 8, radius = rail_radius,
|
||||||
)),
|
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
||||||
calc_uvs = False
|
mathutils.Vector((-rail_span / 2, 0, 0)),
|
||||||
)
|
mathutils.Euler((math.radians(90), 0, 0), 'XYZ'),
|
||||||
|
None
|
||||||
|
)),
|
||||||
|
calc_uvs = False
|
||||||
|
)
|
||||||
|
# create right rail
|
||||||
|
bmesh.ops.create_circle(
|
||||||
|
bm, cap_ends = False, cap_tris = False, segments = 8, radius = rail_radius,
|
||||||
|
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
||||||
|
mathutils.Vector((rail_span / 2, 0, 0)),
|
||||||
|
mathutils.Euler((math.radians(90), 0, 0), 'XYZ'),
|
||||||
|
None
|
||||||
|
)),
|
||||||
|
calc_uvs = False
|
||||||
|
)
|
||||||
|
|
||||||
def _add_straight_rail(
|
def _create_transition_section(
|
||||||
bm: bmesh.types.BMesh,
|
bm: bmesh.types.BMesh,
|
||||||
length: float,
|
|
||||||
matrix: mathutils.Matrix,
|
|
||||||
has_cap: bool,
|
|
||||||
rail_radius: float, rail_span: float) -> None:
|
rail_radius: float, rail_span: float) -> None:
|
||||||
"""
|
"""
|
||||||
|
Create the transition section between rail and monorail.
|
||||||
|
"""
|
||||||
|
# create rail section
|
||||||
|
_create_rail_section(bm, False, rail_radius, rail_span)
|
||||||
|
|
||||||
|
# create monorail
|
||||||
|
# calc sink first
|
||||||
|
monorail_sink: float
|
||||||
|
try:
|
||||||
|
monorail_sink = math.sqrt((rail_radius + 2) ** 2 - (rail_span / 2) ** 2) - 2 - rail_radius
|
||||||
|
except:
|
||||||
|
monorail_sink = -2 # if sqrt(minus number) happended, it mean no triangle relation. the depth should always be -2.
|
||||||
|
# create monorail with calculated sink
|
||||||
|
_create_rail_section(
|
||||||
|
bm, True, rail_radius, rail_span,
|
||||||
|
mathutils.Matrix.Translation((0, 0, monorail_sink))
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_straight_rail(
|
||||||
|
bm: bmesh.types.BMesh,
|
||||||
|
is_monorail: bool, rail_radius: float, rail_span: float,
|
||||||
|
rail_length: float,
|
||||||
|
rail_start_cap: bool, rail_end_cap: bool) -> None:
|
||||||
|
"""
|
||||||
Add a straight rail.
|
Add a straight rail.
|
||||||
The original point is same as `_add_rail_section()`.
|
The original point is same as `_add_rail_section()`.
|
||||||
|
The expand direction is +Y.
|
||||||
|
If ordered is monorail, `rail_span` param will be ignored.
|
||||||
|
"""
|
||||||
|
# create section first
|
||||||
|
_create_rail_section(bm, is_monorail, rail_radius, rail_span)
|
||||||
|
|
||||||
|
# get start edges
|
||||||
|
start_edges: list[bmesh.types.BMEdge] = bm.edges[:]
|
||||||
|
# extrude and get end edges
|
||||||
|
end_edges: list[bmesh.types.BMEdge] = _bmesh_extrude(
|
||||||
|
bm, start_edges, mathutils.Vector((0, rail_length, 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
# cap start and end edges if needed
|
||||||
|
if rail_start_cap:
|
||||||
|
_bmesh_cap(bm, start_edges)
|
||||||
|
if rail_end_cap:
|
||||||
|
_bmesh_cap(bm, end_edges)
|
||||||
|
|
||||||
|
# mark sharp
|
||||||
|
_bmesh_mark_sharp(bm, (start_edges, end_edges, ))
|
||||||
|
|
||||||
|
def _create_screw_rail(
|
||||||
|
bm: bmesh.types.BMesh,
|
||||||
|
is_monorail: bool, rail_radius: float, rail_span: float,
|
||||||
|
rail_start_cap: bool, rail_end_cap: bool,
|
||||||
|
rail_screw_angle: float, rail_screw_screw: float, rail_screw_iterations: int,
|
||||||
|
rail_screw_steps: int, rail_screw_radius: float) -> None:
|
||||||
|
"""
|
||||||
|
Add a screw rail.
|
||||||
|
The original point is same as `_add_rail_section()`.
|
||||||
The start terminal of this straight will be placed in XZ panel.
|
The start terminal of this straight will be placed in XZ panel.
|
||||||
The expand direction is +Y.
|
The expand direction is +Y.
|
||||||
|
If ordered is monorail, `rail_span` param will be ignored.
|
||||||
"""
|
"""
|
||||||
# create left one
|
# create section first
|
||||||
bmesh.ops.create_cone(
|
_create_rail_section(bm, is_monorail, rail_radius, rail_span)
|
||||||
|
|
||||||
|
start_edges: list[bmesh.types.BMEdge] = bm.edges[:]
|
||||||
|
end_edges: list[bmesh.types.BMEdge] = _bmesh_screw(
|
||||||
bm,
|
bm,
|
||||||
cap_ends = has_cap, cap_tris = True,
|
bm.verts[:], start_edges,
|
||||||
segments = 8,
|
math.radians(rail_screw_angle),
|
||||||
radius1 = rail_radius, radius2 = rail_radius,
|
rail_screw_steps, rail_screw_iterations,
|
||||||
depth = length,
|
mathutils.Vector((rail_screw_radius, 0, 0)),
|
||||||
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
rail_screw_screw
|
||||||
mathutils.Vector((-rail_span / 2, length / 2, 0)),
|
|
||||||
mathutils.Euler((math.radians(90), 0, 0), 'XYZ'),
|
|
||||||
None
|
|
||||||
)),
|
|
||||||
calc_uvs = False
|
|
||||||
)
|
|
||||||
# create right one
|
|
||||||
bmesh.ops.create_cone(
|
|
||||||
bm,
|
|
||||||
cap_ends = has_cap, cap_tris = True,
|
|
||||||
segments = 8,
|
|
||||||
radius1 = rail_radius, radius2 = rail_radius,
|
|
||||||
depth = length,
|
|
||||||
matrix = typing.cast(mathutils.Matrix, matrix @ mathutils.Matrix.LocRotScale(
|
|
||||||
mathutils.Vector((rail_span / 2, length / 2, 0)),
|
|
||||||
mathutils.Euler((math.radians(90), 0, 0), 'XYZ'),
|
|
||||||
None
|
|
||||||
)),
|
|
||||||
calc_uvs = False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#endregion
|
# cap start and end edges if needed
|
||||||
|
if rail_start_cap:
|
||||||
|
_bmesh_cap(bm, start_edges)
|
||||||
|
if rail_end_cap:
|
||||||
|
_bmesh_cap(bm, end_edges)
|
||||||
|
|
||||||
#region Rail Adder
|
_bmesh_mark_sharp(bm, (start_edges, end_edges, ))
|
||||||
|
|
||||||
def _create_monorail_section(rail_radius: float) -> bpy.types.Object:
|
|
||||||
return _polygon_adder_wrapper(
|
|
||||||
lambda bm: _add_monorail_section(bm, mathutils.Matrix.Identity(4), rail_radius)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _create_rail_section(rail_radius: float, rail_span: float) -> bpy.types.Object:
|
|
||||||
return _polygon_adder_wrapper(
|
|
||||||
lambda bm: _add_rail_section(bm, mathutils.Matrix.Identity(4), rail_radius, rail_span)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _create_transition_section(rail_radius: float, rail_span: float, monorail_sink: float) -> bpy.types.Object:
|
|
||||||
def invoker(bm: bmesh.types.BMesh) -> None:
|
|
||||||
_add_rail_section(bm, mathutils.Matrix.Identity(4), rail_radius, rail_span)
|
|
||||||
_add_monorail_section(bm, mathutils.Matrix.Translation((0, 0, monorail_sink)), rail_radius)
|
|
||||||
return _polygon_adder_wrapper(invoker)
|
|
||||||
|
|
||||||
def _create_straight_monorail(rail_radius: float, rail_length: float, rail_cap: bool) -> bpy.types.Object:
|
|
||||||
return _polygon_adder_wrapper(
|
|
||||||
lambda bm: _add_straight_monorail(bm, rail_length, mathutils.Matrix.Identity(4), rail_cap, rail_radius)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _create_straight_rail(rail_radius: float, rail_span: float, rail_length: float, rail_cap: bool) -> bpy.types.Object:
|
|
||||||
return _polygon_adder_wrapper(
|
|
||||||
lambda bm: _add_straight_rail(bm, rail_length, mathutils.Matrix.Identity(4), rail_cap, rail_radius, rail_span)
|
|
||||||
)
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(BBP_OT_add_monorail_section)
|
|
||||||
bpy.utils.register_class(BBP_OT_add_rail_section)
|
bpy.utils.register_class(BBP_OT_add_rail_section)
|
||||||
bpy.utils.register_class(BBP_OT_add_transition_section)
|
bpy.utils.register_class(BBP_OT_add_transition_section)
|
||||||
|
|
||||||
bpy.utils.register_class(BBP_OT_add_straight_monorail)
|
|
||||||
bpy.utils.register_class(BBP_OT_add_straight_rail)
|
bpy.utils.register_class(BBP_OT_add_straight_rail)
|
||||||
|
bpy.utils.register_class(BBP_OT_add_screw_rail)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(BBP_OT_add_screw_rail)
|
||||||
bpy.utils.unregister_class(BBP_OT_add_straight_rail)
|
bpy.utils.unregister_class(BBP_OT_add_straight_rail)
|
||||||
bpy.utils.unregister_class(BBP_OT_add_straight_monorail)
|
|
||||||
|
|
||||||
bpy.utils.unregister_class(BBP_OT_add_transition_section)
|
bpy.utils.unregister_class(BBP_OT_add_transition_section)
|
||||||
bpy.utils.unregister_class(BBP_OT_add_rail_section)
|
bpy.utils.unregister_class(BBP_OT_add_rail_section)
|
||||||
bpy.utils.unregister_class(BBP_OT_add_monorail_section)
|
|
||||||
|
@ -74,14 +74,13 @@ class BBP_MT_AddRailMenu(bpy.types.Menu):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
layout.label(text = "Sections")
|
layout.label(text = "Sections")
|
||||||
layout.operator(OP_ADDS_rail.BBP_OT_add_monorail_section.bl_idname)
|
|
||||||
layout.operator(OP_ADDS_rail.BBP_OT_add_rail_section.bl_idname)
|
layout.operator(OP_ADDS_rail.BBP_OT_add_rail_section.bl_idname)
|
||||||
layout.operator(OP_ADDS_rail.BBP_OT_add_transition_section.bl_idname)
|
layout.operator(OP_ADDS_rail.BBP_OT_add_transition_section.bl_idname)
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text = "Straight Rails")
|
layout.label(text = "Rails")
|
||||||
layout.operator(OP_ADDS_rail.BBP_OT_add_straight_monorail.bl_idname)
|
|
||||||
layout.operator(OP_ADDS_rail.BBP_OT_add_straight_rail.bl_idname)
|
layout.operator(OP_ADDS_rail.BBP_OT_add_straight_rail.bl_idname)
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_screw_rail.bl_idname)
|
||||||
|
|
||||||
class BBP_MT_AddComponentsMenu(bpy.types.Menu):
|
class BBP_MT_AddComponentsMenu(bpy.types.Menu):
|
||||||
"""Add Ballance Components"""
|
"""Add Ballance Components"""
|
||||||
|
Loading…
Reference in New Issue
Block a user