add a part of rail adder

This commit is contained in:
yyc12345 2024-01-06 23:51:11 +08:00
parent 6b31401240
commit b5565d796a
4 changed files with 395 additions and 9 deletions

View File

@ -1,7 +1,7 @@
import bpy import bpy
import typing import typing
from . import PROP_preferences, PROP_virtools_mesh, PROP_virtools_group, PROP_bme_material from . import PROP_preferences
from . import UTIL_functions, UTIL_icons_manager, UTIL_bme from . import UTIL_functions, UTIL_bme
#region BME Adder #region BME Adder

374
bbp_ng/OP_ADDS_rail.py Normal file
View 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)

View File

@ -468,9 +468,9 @@ class YYCToolchainConvention():
case BallanceObjectType.FLOOR: case BallanceObjectType.FLOOR:
return 'A_Floor_' return 'A_Floor_'
case BallanceObjectType.RAIL: case BallanceObjectType.RAIL:
return 'A_Wood_'
case BallanceObjectType.WOOD:
return 'A_Rail_' return 'A_Rail_'
case BallanceObjectType.WOOD:
return 'A_Wood_'
case BallanceObjectType.STOPPER: case BallanceObjectType.STOPPER:
return 'A_Stopper_' return 'A_Stopper_'

View File

@ -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 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_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
from . import OP_UV_flatten_uv, OP_UV_rail_uv 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 from . import OP_OBJECT_legacy_align, OP_OBJECT_virtools_group, OP_OBJECT_naming_convention
#region Menu #region Menu
@ -72,6 +72,16 @@ class BBP_MT_AddRailMenu(bpy.types.Menu):
def draw(self, context): def draw(self, context):
layout = self.layout 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): class BBP_MT_AddComponentsMenu(bpy.types.Menu):
"""Add Ballance Components""" """Add Ballance Components"""
@ -80,21 +90,21 @@ class BBP_MT_AddComponentsMenu(bpy.types.Menu):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text="Basic Components") layout.label(text = "Basic Components")
OP_ADDS_component.BBP_OT_add_component.draw_blc_menu(layout) OP_ADDS_component.BBP_OT_add_component.draw_blc_menu(layout)
layout.separator() layout.separator()
layout.label(text="Nong Components") layout.label(text = "Nong Components")
OP_ADDS_component.BBP_OT_add_nong_extra_point.draw_blc_menu(layout) OP_ADDS_component.BBP_OT_add_nong_extra_point.draw_blc_menu(layout)
OP_ADDS_component.BBP_OT_add_nong_ventilator.draw_blc_menu(layout) OP_ADDS_component.BBP_OT_add_nong_ventilator.draw_blc_menu(layout)
layout.separator() layout.separator()
layout.label(text="Series Components") layout.label(text = "Series Components")
OP_ADDS_component.BBP_OT_add_tilting_block_series.draw_blc_menu(layout) OP_ADDS_component.BBP_OT_add_tilting_block_series.draw_blc_menu(layout)
OP_ADDS_component.BBP_OT_add_ventilator_series.draw_blc_menu(layout) OP_ADDS_component.BBP_OT_add_ventilator_series.draw_blc_menu(layout)
layout.separator() layout.separator()
layout.label(text="Components Pair") layout.label(text = "Components Pair")
OP_ADDS_component.BBP_OT_add_sector_component_pair.draw_blc_menu(layout) OP_ADDS_component.BBP_OT_add_sector_component_pair.draw_blc_menu(layout)
# ===== Menu Drawer ===== # ===== Menu Drawer =====
@ -206,6 +216,7 @@ def register() -> None:
OP_UV_flatten_uv.register() OP_UV_flatten_uv.register()
OP_ADDS_component.register() OP_ADDS_component.register()
OP_ADDS_bme.register() OP_ADDS_bme.register()
OP_ADDS_rail.register()
OP_OBJECT_legacy_align.register() OP_OBJECT_legacy_align.register()
OP_OBJECT_virtools_group.register() OP_OBJECT_virtools_group.register()
@ -236,6 +247,7 @@ def unregister() -> None:
OP_OBJECT_virtools_group.unregister() OP_OBJECT_virtools_group.unregister()
OP_OBJECT_legacy_align.unregister() OP_OBJECT_legacy_align.unregister()
OP_ADDS_rail.unregister()
OP_ADDS_bme.unregister() OP_ADDS_bme.unregister()
OP_ADDS_component.unregister() OP_ADDS_component.unregister()
OP_UV_flatten_uv.unregister() OP_UV_flatten_uv.unregister()