3 Commits
v3.1 ... v3.2

Author SHA1 Message Date
dafb679780 [doc] add document for flatten uv and bump up version.
- add document for flatten uv
- bump up version to 3.2
2023-03-11 16:57:17 +08:00
f3663a4280 [fix] fix fatal div zero issue
- fix div error exception when flatten uv - scale size == 0
2023-03-09 21:24:05 +08:00
9c8d365ab6 [feat] add ref. point feature for flatten uv
- add reference point & uv feature for flatten.
- due to first feature, flatten uv now can process more structure of ballance floor, such as looping floor with low edge count.
- give flatten uv 2 different modes.
- original flatten uv method still existed as a scale size mode.
2023-03-09 21:18:13 +08:00
4 changed files with 137 additions and 46 deletions

View File

@ -68,6 +68,10 @@ Note that only convex face is supported. Applying this for a concave face will c
In the edit mode, select the surface, click Flatten UV, and then scroll the slider to select an edge as a reference. In the edit mode, select the surface, click Flatten UV, and then scroll the slider to select an edge as a reference.
If the generated UV is not attached correctly, such as the FloorSide's band is pasted to the bottom, you can reselect the reference edge and redo the operation until it is correct. If the generated UV is not attached correctly, such as the FloorSide's band is pasted to the bottom, you can reselect the reference edge and redo the operation until it is correct.
For the UV flatten by plugin, it must have a scale property. For example, the UV scale of normal floor is 5. However, the UV scale of sink floor is slightly larger than 5. Because the sink floor is "sink" in the floor block. There are 2 methods provided by plugin to getting this proper scale number. You can choose one from Scale Mode.
The first method is that user specify a direct scale number. You just need select Scale Size in Scale Mode and fill with a proper scale number. This option is frequently used for fill a large borderless floor.
The second method is reference point mode. You need specify a reference point and corresponding U component of its UV. Plugin will calculate the scale size automatically. This method is used for expanding a path of floor.
### Quick Struct Adder ### Quick Struct Adder
In the add menu, we have added a set of commonly used objects. After adding, the object will move to the 3D cursor. In the add menu, we have added a set of commonly used objects. After adding, the object will move to the 3D cursor.

View File

@ -68,6 +68,10 @@ Ballance 3D是一套简单的用于制图3D相关的轻型工具集合可以
编辑模式下选中面点击Flatten UV然后选中一个边作为参考。 编辑模式下选中面点击Flatten UV然后选中一个边作为参考。
如果最后生成的边贴附不对,比如把路面花纹贴到了下部,可以重新选择参考边再进行操作,直到正确为止。 如果最后生成的边贴附不对,比如把路面花纹贴到了下部,可以重新选择参考边再进行操作,直到正确为止。
对于粘贴的贴图的UV需要具有一定缩放比如对于平路面这个缩放比是5而对于凹路面则要比5大一些因为凹路面由于凹进路面。为了方便确认这个缩放值我们提供了两种方式可以在Scale Mode种选择。
一种是用户直接指定选择Scale Mode为Scale Size并填写合适的缩放数值即可。此选项适合平谱无边框路面。
另一种即为参考点模式。用户指定一个参考点并指定此参考点在U轴上的位置插件会自动计算缩放值应为多少。此选项适合展开路面路径的贴图。
### 快速添加结构 ### 快速添加结构
在添加菜单中我们添加了一系列较为常用的物体。添加后物体会移动到3D游标处。 在添加菜单中我们添加了一系列较为常用的物体。添加后物体会移动到3D游标处。

View File

@ -3,32 +3,61 @@ import bmesh
import math import math
from . import UTILS_functions from . import UTILS_functions
class ScaleDataUnion(object):
def __init__(self):
self.UseRefPoint: bool = None
def SetAsScale(self, scale_num: float):
self.UseRefPoint: bool = False
self.ScaleSize: float = scale_num
def SetAsRefPoint(self, ref_point: int, ref_point_uv: float):
self.UseRefPoint: bool = True
self.ReferencePoint: int = ref_point
self.ReferenceUV: float = ref_point_uv
class BALLANCE_OT_flatten_uv(bpy.types.Operator): class BALLANCE_OT_flatten_uv(bpy.types.Operator):
"""Flatten selected face UV. Only works for convex face""" """Flatten selected face UV. Only works for convex face"""
bl_idname = "ballance.flatten_uv" bl_idname = "ballance.flatten_uv"
bl_label = "Flatten UV" bl_label = "Flatten UV"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
normal_scale_correction = 5.0 reference_edge : bpy.props.IntProperty(
sink_scale_correction = 5.0 * (math.sqrt(2.5 ** 2 + 0.7 ** 2) / 2.5) name="Reference Edge",
description="The references edge of UV.\nIt will be placed in V axis.",
scale_correction: bpy.props.EnumProperty( min=0,
name="Scale Correction", soft_min=0, soft_max=3,
description="Choose your UV scale.", default=0,
items=(
("NORMAL", "Normal Floor", "Normal floor scale, 5.0"),
("SINK", "Sink Floor", "Sink floor scale, around 5.19")
),
default='NORMAL',
) )
reference_edge : bpy.props.IntProperty( scale_mode: bpy.props.EnumProperty(
name="Reference edge", name="Scale Mode",
description="The references edge of UV. It will be placed in V axis.", items=(('NUM', "Scale Size", "Scale UV with specific number."),
('REF', "Ref. Point", "Scale UV with Reference Point feature."),
),
)
scale_number : bpy.props.FloatProperty(
name="Scale Size",
description="The size which will be applied for scale.",
min=0, min=0,
soft_min=0, soft_min=0, soft_max=5,
soft_max=3, default=5.0,
default=0, step=0.1, precision=1,
)
reference_point : bpy.props.IntProperty(
name="Reference Point",
description="The references point of UV.\nIt's U component will be set to the number specified by Reference Point UV.\nThis point index is related to the start point of reference edge.",
min=2, # 0 and 1 is invalid. we can not order the reference edge to be set on the outside of uv axis
soft_min=2, soft_max=3,
default=2,
)
reference_uv : bpy.props.FloatProperty(
name="Reference Point UV",
description="The U component which should be applied to references point in UV.",
soft_min=0, soft_max=1,
default=0.5,
step=0.1, precision=2,
) )
@classmethod @classmethod
@ -42,26 +71,38 @@ class BALLANCE_OT_flatten_uv(bpy.types.Operator):
return False return False
return True return True
def get_scale_correction(self):
if self.scale_correction == 'NORMAL':
return BALLANCE_OT_flatten_uv.normal_scale_correction
elif self.scale_correction == 'SINK':
return BALLANCE_OT_flatten_uv.sink_scale_correction
else:
raise Exception("Unknow scale correction.")
def execute(self, context): def execute(self, context):
no_processed_count = _real_flatten_uv(bpy.context.active_object.data, self.reference_edge, self.get_scale_correction()) # construct scale data
scale_data: ScaleDataUnion = ScaleDataUnion()
if self.scale_mode == 'NUM':
scale_data.SetAsScale(self.scale_number)
else:
scale_data.SetAsRefPoint(self.reference_point, self.reference_uv)
# do flatten uv and report
no_processed_count = _real_flatten_uv(bpy.context.active_object.data, self.reference_edge, scale_data)
if no_processed_count != 0: if no_processed_count != 0:
print("[Flatten UV] {} faces may not be processed correctly because they have problem.".format(no_processed_count)) print("[Flatten UV] {} faces may not be processed correctly because they have problem.".format(no_processed_count))
return {'FINISHED'} return {'FINISHED'}
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.prop(self, "scale_correction") layout.emboss = 'NORMAL'
layout.prop(self, "reference_edge") layout.prop(self, "reference_edge")
def _real_flatten_uv(mesh, reference_edge, scale_correction): layout.separator()
layout.label(text="Scale Mode")
layout.prop(self, "scale_mode", expand=True)
layout.separator()
layout.label(text="Scale Config")
if self.scale_mode == 'NUM':
layout.prop(self, "scale_number")
else:
layout.prop(self, "reference_point")
layout.prop(self, "reference_uv")
def _real_flatten_uv(mesh, reference_edge, scale_data: ScaleDataUnion):
no_processed_count = 0 no_processed_count = 0
if mesh.uv_layers.active is None: if mesh.uv_layers.active is None:
@ -71,16 +112,29 @@ def _real_flatten_uv(mesh, reference_edge, scale_correction):
bm = bmesh.from_edit_mesh(mesh) bm = bmesh.from_edit_mesh(mesh)
uv_lay = bm.loops.layers.uv.active uv_lay = bm.loops.layers.uv.active
for face in bm.faces: for face in bm.faces:
# ========== only process selected face ==========
if not face.select: if not face.select:
continue continue
# check whether ref edge is legal # ========== resolve reference edge and point ==========
# check reference validation
allPoint = len(face.loops) allPoint = len(face.loops)
if allPoint <= reference_edge: if reference_edge >= allPoint: # reference edge overflow
no_processed_count += 1 no_processed_count += 1
continue continue
# get correct new corrdinate system # check scale validation
if scale_data.UseRefPoint:
if ((scale_data.ReferencePoint <= 1) # reference point too low
or (scale_data.ReferencePoint >= allPoint)): # reference point overflow
no_processed_count += 1
continue
else:
if round(scale_data.ScaleSize, 7) == 0.0: # invalid scale size
no_processed_count += 1
continue
# ========== get correct new corrdinate system ==========
# yyc mark: # yyc mark:
# we use 3 points located in this face to calc # we use 3 points located in this face to calc
# the base of this local uv corredinate system. # the base of this local uv corredinate system.
@ -90,7 +144,7 @@ def _real_flatten_uv(mesh, reference_edge, scale_correction):
# if z axis is zero vector, we will try using face normal instead # if z axis is zero vector, we will try using face normal instead
# to try getting correct data. # to try getting correct data.
# #
# zero base is not important. because it will not raise any math exceptio # zero base is not important. because it will not raise any math exception
# just a weird uv. user will notice this problem. # just a weird uv. user will notice this problem.
# get point # get point
@ -115,7 +169,7 @@ def _real_flatten_uv(mesh, reference_edge, scale_correction):
# get z axis # get z axis
new_z_axis = new_y_axis.cross(vec1) new_z_axis = new_y_axis.cross(vec1)
new_z_axis.normalize() new_z_axis.normalize()
if not any(round(v, 7) for v in new_z_axis): if not any(round(v, 7) for v in new_z_axis): # if z is a zero vector, use face normal instead
new_z_axis = face.normal.normalized() new_z_axis = face.normal.normalized()
# get x axis # get x axis
@ -137,21 +191,50 @@ def _real_flatten_uv(mesh, reference_edge, scale_correction):
transition_matrix = origin_base @ new_base transition_matrix = origin_base @ new_base
transition_matrix.invert_safe() transition_matrix.invert_safe()
# process each face # ========== rescale correction ==========
if scale_data.UseRefPoint:
# ref point method
# get reference point from loop
refpRelative = p1Relative + scale_data.ReferencePoint
if refpRelative >= allPoint:
refpRelative -= allPoint
pRef = mathutils.Vector(tuple(face.loops[refpRelative].vert.co[x] for x in range(3))) - p1
# calc its U component
vec_u = abs((transition_matrix @ pRef).x)
if round(vec_u, 7) == 0.0:
rescale = 1 # fallback. rescale = 1 will not affect anything
else:
rescale = scale_data.ReferenceUV / vec_u
else:
# scale size method
# apply rescale directly
rescale = 1.0 / scale_data.ScaleSize
# construct matrix
# we only rescale U component (X component)
# and 5.0 scale for V component (Y component)
scale_matrix = mathutils.Matrix((
(rescale, 0, 0),
(0, 1.0 / 5.0, 0),
(0, 0, 1.0)
))
# order can not be changed. we order do transition first, then scale it.
rescale_transition_matrix = scale_matrix @ transition_matrix
# ========== process each face ==========
for loop_index in range(allPoint): for loop_index in range(allPoint):
pp = mathutils.Vector(tuple(face.loops[loop_index].vert.co[x] for x in range(3))) pp = mathutils.Vector(tuple(face.loops[loop_index].vert.co[x] for x in range(3))) - p1
vec = pp-p1 ppuv = rescale_transition_matrix @ pp
new_vec = transition_matrix @ vec
# y axis always use 5.0 to scale # y axis always use 5.0 to scale
# however, x need use custom scale correction. # however, x need use custom scale correction which has been calculated by our matrix.
face.loops[loop_index][uv_lay].uv = ( face.loops[loop_index][uv_lay].uv = (
(new_vec.x if new_vec.x >=0 else -new_vec.x) / scale_correction, abs(ppuv.x),
(new_vec.y) / 5.0 ppuv.y
) )
# Show the updates in the viewport # Show the updates in the viewport
bmesh.update_edit_mesh(mesh) bmesh.update_edit_mesh(mesh)
return no_processed_count return no_processed_count

View File

@ -2,7 +2,7 @@ bl_info={
"name":"Ballance Blender Plugin", "name":"Ballance Blender Plugin",
"description":"Ballance mapping tools for Blender", "description":"Ballance mapping tools for Blender",
"author":"yyc12345", "author":"yyc12345",
"version":(3,1), "version":(3,2),
"blender":(3,3,0), "blender":(3,3,0),
"category":"Object", "category":"Object",
"support":"TESTING", "support":"TESTING",