add a part of rail adder
This commit is contained in:
parent
6b31401240
commit
b5565d796a
@ -1,7 +1,7 @@
|
||||
import bpy
|
||||
import typing
|
||||
from . import PROP_preferences, PROP_virtools_mesh, PROP_virtools_group, PROP_bme_material
|
||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_bme
|
||||
from . import PROP_preferences
|
||||
from . import UTIL_functions, UTIL_bme
|
||||
|
||||
#region BME Adder
|
||||
|
||||
|
374
bbp_ng/OP_ADDS_rail.py
Normal file
374
bbp_ng/OP_ADDS_rail.py
Normal file
@ -0,0 +1,374 @@
|
||||
import bpy, bmesh, mathutils, math
|
||||
import typing
|
||||
from . import UTIL_functions, UTIL_naming_convension
|
||||
|
||||
## Const Value Hint:
|
||||
# Default Rail Radius: 0.35 (in measure)
|
||||
# Default Rail Span: 3.75 (in convention)
|
||||
# Default Monorail Sink Depth in Rail & Monorail Transition: 0.6259 (calculated by ImbalancedDream)
|
||||
# Equation: Sink = sqrt( ((RailRadius + BallRadius) ^ 2) - ((RailSpan / 2) ^ 2) ) - BallRadius - RailRadius
|
||||
# BallRadius is the radius of player ball. It always is 2.
|
||||
# Ref: https://tieba.baidu.com/p/6557180791
|
||||
|
||||
#region Operators
|
||||
|
||||
class SharedRailInputProperty():
|
||||
rail_radius: bpy.props.FloatProperty(
|
||||
name = "Rail Radius",
|
||||
description = "Define rail section radius",
|
||||
default = 0.35,
|
||||
min = 0,
|
||||
) # type: ignore
|
||||
|
||||
rail_span: bpy.props.FloatProperty(
|
||||
name = "Rail Span",
|
||||
description = "The length between 2 single rails.",
|
||||
default = 3.75,
|
||||
min = 0,
|
||||
) # type: ignore
|
||||
|
||||
rail_length: bpy.props.FloatProperty(
|
||||
name = "Rail Length",
|
||||
description = "The length of this rail.",
|
||||
default = 5.0,
|
||||
min = 0,
|
||||
step = 50, # same unit as BME Struct
|
||||
) # type: ignore
|
||||
|
||||
rail_cap: bpy.props.BoolProperty(
|
||||
name = 'Rail Cap',
|
||||
description = 'Whether this rail should have terminal cap.',
|
||||
default = False
|
||||
) # type: ignore
|
||||
|
||||
def draw_rail_radius_input(self, layout: bpy.types.UILayout) -> None:
|
||||
layout.prop(self, "rail_radius")
|
||||
def draw_rail_span_input(self, layout: bpy.types.UILayout) -> None:
|
||||
layout.prop(self, "rail_span")
|
||||
def draw_rail_length_input(self, layout: bpy.types.UILayout) -> None:
|
||||
layout.prop(self, "rail_length")
|
||||
def draw_rail_cap_input(self, layout: bpy.types.UILayout) -> None:
|
||||
layout.prop(self, "rail_cap")
|
||||
|
||||
def general_get_rail_radius(self) -> float:
|
||||
return self.rail_radius
|
||||
def general_get_rail_span(self) -> float:
|
||||
return self.rail_span
|
||||
def general_get_rail_length(self) -> float:
|
||||
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):
|
||||
"""Add Monorail Section"""
|
||||
bl_idname = "bbp.add_monorail_section"
|
||||
bl_label = "Monorail Section"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
_create_monorail_section(self.general_get_rail_radius())
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
self.draw_rail_radius_input(layout)
|
||||
|
||||
class BBP_OT_add_rail_section(SharedRailInputProperty, bpy.types.Operator):
|
||||
"""Add Rail Section"""
|
||||
bl_idname = "bbp.add_rail_section"
|
||||
bl_label = "Rail Section"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
_create_rail_section(self.general_get_rail_radius(), self.general_get_rail_span())
|
||||
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_transition_section(SharedRailInputProperty, bpy.types.Operator):
|
||||
"""Add Transition Section"""
|
||||
bl_idname = "bbp.add_transition_section"
|
||||
bl_label = "Transition Section"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
# calc sink
|
||||
radius_: float = self.general_get_rail_radius()
|
||||
span_: float = self.general_get_rail_span()
|
||||
sink_: float
|
||||
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'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
self.draw_rail_radius_input(layout)
|
||||
self.draw_rail_length_input(layout)
|
||||
self.draw_rail_cap_input(layout)
|
||||
|
||||
class BBP_OT_add_straight_rail(SharedRailInputProperty, bpy.types.Operator):
|
||||
"""Add Straight Rail"""
|
||||
bl_idname = "bbp.add_straight_rail"
|
||||
bl_label = "Straight Rail"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
_create_straight_rail(
|
||||
self.general_get_rail_radius(),
|
||||
self.general_get_rail_span(),
|
||||
self.general_get_rail_length(),
|
||||
self.general_get_rail_cap()
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
self.draw_rail_radius_input(layout)
|
||||
self.draw_rail_span_input(layout)
|
||||
self.draw_rail_length_input(layout)
|
||||
self.draw_rail_cap_input(layout)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Modifier Adder
|
||||
|
||||
def _set_screw_modifier(obj: bpy.types.Object) -> None:
|
||||
pass
|
||||
|
||||
#endregion
|
||||
|
||||
#region Polygon Adders
|
||||
|
||||
def _polygon_adder_wrapper(fct_poly_cret: typing.Callable[[bmesh.types.BMesh], None]) -> bpy.types.Object:
|
||||
# create mesh first
|
||||
bm: bmesh.types.BMesh = bmesh.new()
|
||||
|
||||
# call cret fct
|
||||
fct_poly_cret(bm)
|
||||
|
||||
# finish up
|
||||
mesh: bpy.types.Mesh = bpy.data.meshes.new('Rail')
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
|
||||
# create object and assoc with it
|
||||
# create info first
|
||||
rail_info: UTIL_naming_convension.BallanceObjectInfo = UTIL_naming_convension.BallanceObjectInfo.create_from_others(
|
||||
UTIL_naming_convension.BallanceObjectType.RAIL
|
||||
)
|
||||
# then get object name
|
||||
rail_name: str | None = UTIL_naming_convension.YYCToolchainConvention.set_to_name(rail_info, None)
|
||||
if rail_name is None: raise UTIL_functions.BBPException('impossible null name')
|
||||
# create object by name
|
||||
obj: bpy.types.Object = bpy.data.objects.new(rail_name, mesh)
|
||||
# assign virtools groups
|
||||
UTIL_naming_convension.VirtoolsGroupConvention.set_to_object(obj, rail_info, None)
|
||||
|
||||
# move to cursor
|
||||
UTIL_functions.add_into_scene_and_move_to_cursor(obj)
|
||||
|
||||
# return rail
|
||||
return obj
|
||||
|
||||
def _add_monorail_section(
|
||||
bm: bmesh.types.BMesh,
|
||||
matrix: mathutils.Matrix,
|
||||
rail_radius: float) -> 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.
|
||||
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.
|
||||
"""
|
||||
# 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(
|
||||
bm: bmesh.types.BMesh,
|
||||
length: float,
|
||||
matrix: mathutils.Matrix,
|
||||
has_cap: bool,
|
||||
rail_radius: float) -> None:
|
||||
"""
|
||||
Add a straight monorail.
|
||||
The original point is same as `_add_monorail_section()`.
|
||||
The start terminal of this straight will be placed in XZ panel.
|
||||
The expand direction is +Y.
|
||||
"""
|
||||
# create left 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((0, length / 2, 0)),
|
||||
mathutils.Euler((math.radians(90), math.radians(22.5), 0), 'XYZ'),
|
||||
None
|
||||
)),
|
||||
calc_uvs = False
|
||||
)
|
||||
|
||||
def _add_straight_rail(
|
||||
bm: bmesh.types.BMesh,
|
||||
length: float,
|
||||
matrix: mathutils.Matrix,
|
||||
has_cap: bool,
|
||||
rail_radius: float, rail_span: float) -> None:
|
||||
"""
|
||||
Add a straight rail.
|
||||
The original point is same as `_add_rail_section()`.
|
||||
The start terminal of this straight will be placed in XZ panel.
|
||||
The expand direction is +Y.
|
||||
"""
|
||||
# create left 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
|
||||
)
|
||||
# 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
|
||||
|
||||
#region Rail Adder
|
||||
|
||||
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
|
||||
|
||||
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_transition_section)
|
||||
|
||||
bpy.utils.register_class(BBP_OT_add_straight_monorail)
|
||||
bpy.utils.register_class(BBP_OT_add_straight_rail)
|
||||
|
||||
|
||||
def unregister():
|
||||
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_rail_section)
|
||||
bpy.utils.unregister_class(BBP_OT_add_monorail_section)
|
@ -468,9 +468,9 @@ class YYCToolchainConvention():
|
||||
case BallanceObjectType.FLOOR:
|
||||
return 'A_Floor_'
|
||||
case BallanceObjectType.RAIL:
|
||||
return 'A_Wood_'
|
||||
case BallanceObjectType.WOOD:
|
||||
return 'A_Rail_'
|
||||
case BallanceObjectType.WOOD:
|
||||
return 'A_Wood_'
|
||||
case BallanceObjectType.STOPPER:
|
||||
return 'A_Stopper_'
|
||||
|
||||
|
@ -32,7 +32,7 @@ UTIL_icons_manager.register()
|
||||
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_virtools_group, PROP_ballance_element, PROP_bme_material
|
||||
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
|
||||
from . import OP_UV_flatten_uv, OP_UV_rail_uv
|
||||
from . import OP_ADDS_component, OP_ADDS_bme
|
||||
from . import OP_ADDS_component, OP_ADDS_bme, OP_ADDS_rail
|
||||
from . import OP_OBJECT_legacy_align, OP_OBJECT_virtools_group, OP_OBJECT_naming_convention
|
||||
|
||||
#region Menu
|
||||
@ -73,6 +73,16 @@ class BBP_MT_AddRailMenu(bpy.types.Menu):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
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_transition_section.bl_idname)
|
||||
|
||||
layout.separator()
|
||||
layout.label(text = "Straight 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)
|
||||
|
||||
class BBP_MT_AddComponentsMenu(bpy.types.Menu):
|
||||
"""Add Ballance Components"""
|
||||
bl_idname = "BBP_MT_AddComponentsMenu"
|
||||
@ -206,6 +216,7 @@ def register() -> None:
|
||||
OP_UV_flatten_uv.register()
|
||||
OP_ADDS_component.register()
|
||||
OP_ADDS_bme.register()
|
||||
OP_ADDS_rail.register()
|
||||
|
||||
OP_OBJECT_legacy_align.register()
|
||||
OP_OBJECT_virtools_group.register()
|
||||
@ -236,6 +247,7 @@ def unregister() -> None:
|
||||
OP_OBJECT_virtools_group.unregister()
|
||||
OP_OBJECT_legacy_align.unregister()
|
||||
|
||||
OP_ADDS_rail.unregister()
|
||||
OP_ADDS_bme.unregister()
|
||||
OP_ADDS_component.unregister()
|
||||
OP_UV_flatten_uv.unregister()
|
||||
|
Loading…
Reference in New Issue
Block a user