chore:模型调整

This commit is contained in:
2026-07-21 14:44:44 +08:00
parent 269615a7fa
commit b283097c03
836 changed files with 483661 additions and 42749 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+51
View File
@@ -0,0 +1,51 @@
"""Compare dimensions of new GLB assets with matching project FBX assets."""
from pathlib import Path
import csv
import bpy
from mathutils import Vector
PROJECT = Path(__file__).resolve().parent.parent
ASSET_ROOT = PROJECT / "docs" / "asset"
NEW_ROOT = next(path for path in ASSET_ROOT.iterdir() if path.is_dir() and any(path.rglob("*.glb")))
REFERENCE_ROOT = PROJECT / "assets" / "Models"
OUTPUT = PROJECT / "tools" / "new_model_scale_comparison.csv"
def clear_scene():
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)
def dimensions(path: Path, extension: str):
clear_scene()
if extension == ".glb":
bpy.ops.import_scene.gltf(filepath=str(path))
else:
bpy.ops.import_scene.fbx(filepath=str(path))
points = []
for obj in bpy.context.scene.objects:
if obj.type == "MESH":
points.extend(obj.matrix_world @ Vector(corner) for corner in obj.bound_box)
low = [min(point[i] for point in points) for i in range(3)]
high = [max(point[i] for point in points) for i in range(3)]
return tuple(round(high[i] - low[i], 5) for i in range(3))
references = {path.stem: path for path in REFERENCE_ROOT.rglob("*.fbx")}
rows = []
for glb in sorted(NEW_ROOT.rglob("*.glb")):
reference = references.get(glb.stem)
if reference is None:
continue
glb_size = dimensions(glb, ".glb")
fbx_size = dimensions(reference, ".fbx")
ratios = [fbx_size[i] / glb_size[i] for i in range(3) if glb_size[i] > 0 and fbx_size[i] > 0]
rows.append((glb.stem, *glb_size, *fbx_size, round(sum(ratios) / len(ratios), 5)))
with OUTPUT.open("w", newline="", encoding="utf-8-sig") as file:
writer = csv.writer(file)
writer.writerow(("model", "glb_x", "glb_y", "glb_z", "project_fbx_x", "project_fbx_y", "project_fbx_z", "project_to_new_ratio"))
writer.writerows(rows)
print(f"Wrote {len(rows)} comparisons to {OUTPUT}")
+86
View File
@@ -0,0 +1,86 @@
"""Batch-convert GLB files to FBX through Blender.
Run this file with Blender's Python, not the system Python. The script keeps
the source folder layout and writes an errors.csv next to the output files.
"""
from __future__ import annotations
import argparse
import csv
import sys
from pathlib import Path
import bpy
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--source", required=True, type=Path)
parser.add_argument("--destination", required=True, type=Path)
parser.add_argument("--overwrite", action="store_true")
return parser.parse_args(sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else [])
def clear_scene() -> None:
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)
for collection in (bpy.data.meshes, bpy.data.materials, bpy.data.images, bpy.data.armatures):
for item in list(collection):
collection.remove(item)
def main() -> int:
args = parse_args()
source = args.source.resolve()
destination = args.destination.resolve()
if not source.is_dir():
print(f"ERROR: source folder does not exist: {source}")
return 2
if source == destination:
print("ERROR: destination must be different from source")
return 2
glb_files = sorted(source.rglob("*.glb"))
destination.mkdir(parents=True, exist_ok=True)
errors: list[tuple[str, str]] = []
converted = skipped = 0
for index, input_file in enumerate(glb_files, start=1):
output_file = destination / input_file.relative_to(source).with_suffix(".fbx")
if output_file.exists() and not args.overwrite:
print(f"[{index}/{len(glb_files)}] SKIP {input_file.name}")
skipped += 1
continue
output_file.parent.mkdir(parents=True, exist_ok=True)
try:
clear_scene()
bpy.ops.import_scene.gltf(filepath=str(input_file))
bpy.ops.export_scene.fbx(
filepath=str(output_file),
use_selection=False,
object_types={"EMPTY", "CAMERA", "LIGHT", "ARMATURE", "MESH", "OTHER"},
use_mesh_modifiers=True,
add_leaf_bones=False,
path_mode="COPY",
embed_textures=True,
)
converted += 1
print(f"[{index}/{len(glb_files)}] OK {input_file.name}")
except Exception as exc: # continue converting the remaining assets
errors.append((str(input_file), repr(exc)))
print(f"[{index}/{len(glb_files)}] FAIL {input_file.name}: {exc}")
if errors:
error_log = destination / "errors.csv"
with error_log.open("w", newline="", encoding="utf-8-sig") as handle:
writer = csv.writer(handle)
writer.writerow(("source_file", "error"))
writer.writerows(errors)
print(f"Error log: {error_log}")
print(f"Finished: {converted} converted, {skipped} skipped, {len(errors)} failed.")
return 1 if errors else 0
if __name__ == "__main__":
raise SystemExit(main())
+25
View File
@@ -0,0 +1,25 @@
param(
[string]$Source,
[string]$Destination,
[switch]$Overwrite
)
$Blender = "E:\Program Files\Blender Foundation\Blender 5.2\blender.exe"
$Script = Join-Path $PSScriptRoot "convert_glb_to_fbx.py"
if ([string]::IsNullOrWhiteSpace($Source)) {
$AssetRoot = Join-Path (Split-Path $PSScriptRoot -Parent) "docs\asset"
$Source = Get-ChildItem -LiteralPath $AssetRoot -Directory |
Where-Object { @(Get-ChildItem -LiteralPath $_.FullName -Recurse -File -Filter "*.glb").Count -gt 0 } |
Select-Object -First 1 -ExpandProperty FullName
}
if ([string]::IsNullOrWhiteSpace($Destination)) { $Destination = "$Source`_fbx" }
if (-not (Test-Path -LiteralPath $Blender)) { throw "Blender was not found: $Blender" }
if (-not (Test-Path -LiteralPath $Source)) { throw "Source folder was not found: $Source" }
$Arguments = @("--background", "--python", $Script, "--", "--source", $Source, "--destination", $Destination)
if ($Overwrite) { $Arguments += "--overwrite" }
& $Blender @Arguments
exit $LASTEXITCODE
+102
View File
@@ -0,0 +1,102 @@
model,glb_x,glb_y,glb_z,project_fbx_x,project_fbx_y,project_fbx_z,project_to_new_ratio
SM_Background_City_02,0.69548,2.21799,7.25709,14.29015,0.0,15.37664,11.33301
SM_Bld_Apartments_01,4.68933,3.14799,6.58278,1.08974,1.31511,2.31951,0.33417
SM_Bld_Apartments_02,685.05756,507.09831,1023.39322,1.09103,1.25694,2.46071,0.00216
SM_Bld_Apartments_03,0.11738,0.09717,0.14042,1.08236,1.34601,2.46071,13.53234
SM_Bld_Apartments_04,863.37424,1090.10559,1654.52451,1.06326,1.22201,2.13264,0.00121
SM_Bld_Apartments_Block_01,25.34438,23.36829,22.96318,2.70139,1.55609,2.46071,0.09345
SM_Bld_Apartments_Block_02,18.94554,17.62445,17.04079,3.10854,2.32077,2.46071,0.14672
SM_Bld_Church_01,1381.84839,1884.85574,1934.2692,1.26475,1.861,2.64343,0.00109
SM_Bld_CityHall_01,1.28757,1.04367,1.08939,2.47677,2.18277,1.37282,1.7584
SM_Bld_House_02,475.6608,884.38977,644.3027,0.78916,1.18354,0.81988,0.00142
SM_Bld_House_03,6.36,7.45,5.85,1.30453,1.5264,1.09218,0.1989
SM_Bld_House_05,7.7198,7.37393,10.36916,1.70929,0.99749,1.09207,0.154
SM_Bld_House_07,1132.9505,2009.90247,990.99037,1.75699,1.68834,1.33679,0.00125
SM_Bld_House_08,370.99999,256.02248,333.54853,1.40633,2.41373,1.22004,0.00563
SM_Bld_House_10,368.8779,214.66895,290.11153,1.54676,1.44984,1.4634,0.00533
SM_Bld_House_11,6.86464,6.03142,8.04465,2.4441,2.37924,1.76332,0.32324
SM_Bld_OfficeOctagon_01,103.77404,103.81444,235.11004,1.6757,1.6757,2.79256,0.01472
SM_Bld_OfficeOld_Large_01,5.38043,3.44203,5.37686,1.6334,1.6334,2.55487,0.41776
SM_Bld_OfficeRound_01,25.15329,27.18591,56.368,1.47989,1.4799,2.59303,0.05309
SM_Bld_OfficeSquare_01,108.28931,60.00002,326.79121,1.22006,1.22006,2.88175,0.01347
SM_Bld_Shop_01,13.54689,11.39883,5.07535,1.36853,1.2948,1.11662,0.14487
SM_Bld_Shop_02,113.9176,98.98986,52.56013,1.93887,1.34646,1.07373,0.01702
SM_Bld_Shop_03,1574.65564,1559.59204,786.92122,2.09754,2.2047,1.06234,0.00137
SM_Bld_Station_01,2941.36243,2393.23535,2560.70125,4.59497,1.67788,1.69871,0.00098
SM_Bld_Station_02,1.91491,2.13875,2.66549,1.62324,1.62323,1.69871,0.74798
SM_Env_Fence_01,4.20314,0.3206,2.73405,0.43742,0.01741,0.23644,0.08162
SM_Env_Fence_02,0.60854,9.84975,6.33737,0.43641,0.06116,0.25433,0.25449
SM_Env_Generic_Bush_01,6.73399,7.18018,5.23314,0.48032,0.31855,0.2597,0.05511
SM_Env_Generic_Bush_02,123.72953,77.31821,60.72777,0.28091,0.29047,0.25134,0.00339
SM_Env_Generic_River_Stone_02,0.10468,0.15147,0.07811,0.61023,0.54089,0.10295,3.57281
SM_Env_Generic_Rock_04,2.00732,1.99077,1.55727,0.85123,0.74968,0.1652,0.30224
SM_Env_Generic_Tree_01,0.6856,0.5899,0.7368,0.54981,0.47615,1.11742,1.0419
SM_Env_Generic_Tree_03,0.36783,0.38901,0.56396,1.02748,1.01284,1.08527,2.44045
SM_Env_Generic_Tree_04,335.77576,335.60579,576.65575,0.52926,0.48412,0.9674,0.00157
SM_Env_Grass_01,1.37798,1.58631,1.80551,0.33247,0.42413,0.31007,0.22679
SM_Env_Pool_01,2.0,2.0,1.77263,1.14304,1.14304,0.27115,0.432
SM_Env_RoadPatch_01,38.2175,38.94163,1.36702,0.71528,0.47709,0.02005,0.01521
SM_Env_WallPark_Corner_01,0.80317,0.79611,0.45142,1.12481,1.12481,0.63994,1.41032
SM_Env_WallPark_Corner_02,1.11296,0.49945,0.33097,1.63778,1.11307,0.63994,1.87789
SM_Env_WallPark_Straight_01,106.5006,23.27893,53.24705,1.12171,0.26935,0.63994,0.01137
SM_Generic_Ground_01,1018.54285,1018.54303,844.66702,3.50151,3.59726,0.16318,0.00239
SM_Prop_BBQ_01,279.16141,95.69674,184.67297,0.53033,0.3585,0.45431,0.0027
SM_Prop_Billboard_Base_01,44.29373,271.14278,147.36865,0.4614,0.12016,0.37081,0.00446
SM_Prop_BusStop_01,3.81107,1.61119,2.525,0.77668,0.30664,0.52219,0.20031
SM_Prop_CardboardBox_01,8.71478,7.08694,6.84178,0.34782,0.33854,0.27174,0.04247
SM_Prop_Drain_01,19.23742,19.2913,0.66166,0.48729,0.29083,0.06054,0.04397
SM_Prop_Dumpster_Closed_01,1.19785,1.97377,1.5135,0.60115,0.41735,0.52524,0.35345
SM_Prop_Dumpster_Open_01,175.42211,103.07324,129.3989,0.60115,0.45428,0.89502,0.00492
SM_Prop_FireHydrant_01,0.60019,0.51805,1.12728,0.24522,0.23678,0.44447,0.41997
SM_Prop_FlagPole_01,161.82344,227.43806,57.60232,0.0603,0.40035,0.4776,0.00347
SM_Prop_Flamingo_01,48.91436,16.33452,69.17754,0.30538,0.0908,0.44651,0.00609
SM_Prop_Fountain_01,13.906,13.906,8.45385,0.76971,0.66661,0.40239,0.0503
SM_Prop_Garden_01,15.37682,15.37682,3.04318,0.40087,0.40087,0.21626,0.04107
SM_Prop_Garden_02,1.62948,1.10473,0.07707,0.75537,0.40087,0.2212,1.23218
SM_Prop_Helipad_01,10.91023,10.91023,0.23701,0.92692,0.92693,0.06836,0.15278
SM_Prop_HotdogCart_01,3.84464,3.84464,3.59613,0.67027,0.5935,0.8584,0.18914
SM_Prop_LetterBox_01,0.00998,0.01461,0.04365,0.10155,0.17545,0.46047,10.91113
SM_Prop_OutdoorHeader_01,2.96217,2.96217,2.77573,0.353,0.30571,0.67326,0.15498
SM_Prop_ParkBench_01,7.28595,3.71581,5.65442,0.45142,0.16979,0.28417,0.05264
SM_Prop_PhoneBooth_01,38.87959,37.3667,156.15133,0.79749,0.35382,0.69589,0.01148
SM_Prop_Playground_01,7.0331,11.45797,6.56647,0.6233,0.92598,0.58828,0.08634
SM_Prop_Playground_02,1.5,2.0,0.75,0.52417,0.52464,0.22678,0.30471
SM_Prop_Playground_03,2.73628,6.26311,5.25295,0.28082,0.70537,0.35804,0.09447
SM_Prop_Playground_04,2.0,2.0,0.789,0.83021,0.83021,0.22225,0.37063
SM_Prop_Pool_Float_Ring_05,0.97817,1.00072,0.82379,0.3303,0.32967,0.19202,0.30007
SM_Prop_Potplants_01,11.36936,1.24811,2.17385,0.36419,0.15104,0.18395,0.07922
SM_Prop_PowerBox_01,0.39528,0.66,0.75845,0.42847,0.23621,0.40737,0.65966
SM_Prop_RoadBump_01,107.00941,11.60036,53.23364,1.28321,0.2008,0.05406,0.01011
SM_Prop_Roadcone_01,1.524,1.524,2.54,0.19298,0.19299,0.24189,0.11616
SM_Prop_Sign_02,0.24552,1.84776,7.0,0.2689,0.09427,0.74396,0.41751
SM_Prop_Sign_IceCream_01,10.31887,10.50033,30.03268,0.42924,0.37173,1.00917,0.03687
SM_Prop_Sign_Popcorn_01,1.35837,1.35837,2.0,0.42322,0.42127,0.70028,0.32394
SM_Prop_Spa_01,8.79603,8.79603,2.37212,0.81437,0.70527,0.28537,0.09769
SM_Prop_Swingset_01,17.69562,18.9807,21.45482,0.86507,0.65536,0.72705,0.0391
SM_Prop_TrafficLight_01,1.20177,0.99179,7.41,0.30846,0.36292,1.38417,0.2698
SM_Prop_Trashbag_01,2.0,1.67131,1.91278,0.33138,0.33138,0.36199,0.1844
SM_Prop_WaterTower_01,1.97973,3.08043,4.21858,0.44216,0.44216,1.05823,0.20591
SM_Tile_Bridge_01,4822.71106,998.0307,969.53262,1.79035,4.99597,0.85209,0.00209
SM_Tile_Cube_Concrete_01,11.81102,11.81102,1.96851,5.0,5.0,0.43242,0.35545
SM_Tile_Cube_Concrete_03,6.00927,5.84295,0.45,5.0,5.0,0.43242,0.8829
SM_Tile_Cube_Concrete_04,1.0,1.0,0.0,5.00027,5.00076,0.43147,5.00052
SM_Tile_Cube_Grass_01,5.0,5.0,0.13838,5.0,5.0,0.50198,1.87585
SM_Tile_Cube_Path_Grass_03,0.84288,0.84628,0.14712,5.0,5.0,0.50198,5.0841
SM_Tile_Cube_Road_Concrete_01,2307.99976,1460.00024,48.00039,5.0,5.0,0.43242,0.00487
SM_Tile_Cube_Road_Concrete_02,996.85956,1141.18781,21.35264,5.0,5.0,0.43242,0.00988
SM_Tile_Cube_Road_Concrete_04,0.90794,0.14309,0.82242,5.0,5.0,0.43242,13.6586
SM_Tile_Cube_Road_Grass_07,84.8795,84.85878,15.86925,5.0,5.0,0.50364,0.04986
SM_Tile_Hex_Grass_01,1.70759,0.9076,0.20707,5.77417,5.00076,0.49279,3.75706
SM_Tile_Ramp_14,1200.00006,555.99994,467.57274,1.79035,5.00005,0.96407,0.00418
SM_Veh_Bus_01,11.50354,4.82497,4.84264,1.03079,2.40287,0.88361,0.25669
SM_Veh_Car_Ambo_01,2.747,5.6364,3.0023,0.81924,1.41634,0.97278,0.29118
SM_Veh_Car_Medium_01,1.94016,4.23,1.92969,0.64088,1.48005,0.73093,0.353
SM_Veh_Car_Muscle_01,1.93382,4.55527,1.22209,0.84551,1.66101,0.53417,0.41298
SM_Veh_Car_Police_01,3.77022,1.97826,1.55441,0.79713,1.47207,0.71478,0.4718
SM_Veh_Car_Sedan_01,5.14178,9.38673,5.15166,0.79713,1.44128,0.59798,0.14155
SM_Veh_Car_Small_01,1.8559,3.80523,1.49906,0.70371,1.17695,0.5327,0.34794
SM_Veh_Car_Taxi_01,0.13286,0.25675,0.12331,0.79713,1.44128,0.73805,5.86622
SM_Veh_Car_Van_01,5.20041,11.17683,4.98729,0.81924,1.38511,0.74,0.14328
SM_Veh_Convertable_01,1.90525,0.80117,0.54993,0.83514,1.51832,0.48688,1.07294
SM_Veh_Pickup_01,7.51748,2.88886,3.06634,0.88997,1.5441,0.85548,0.31063
SM_Veh_Truck_01,0.99336,2.0,1.05006,0.83043,1.91525,1.04004,0.92802
1 model glb_x glb_y glb_z project_fbx_x project_fbx_y project_fbx_z project_to_new_ratio
2 SM_Background_City_02 0.69548 2.21799 7.25709 14.29015 0.0 15.37664 11.33301
3 SM_Bld_Apartments_01 4.68933 3.14799 6.58278 1.08974 1.31511 2.31951 0.33417
4 SM_Bld_Apartments_02 685.05756 507.09831 1023.39322 1.09103 1.25694 2.46071 0.00216
5 SM_Bld_Apartments_03 0.11738 0.09717 0.14042 1.08236 1.34601 2.46071 13.53234
6 SM_Bld_Apartments_04 863.37424 1090.10559 1654.52451 1.06326 1.22201 2.13264 0.00121
7 SM_Bld_Apartments_Block_01 25.34438 23.36829 22.96318 2.70139 1.55609 2.46071 0.09345
8 SM_Bld_Apartments_Block_02 18.94554 17.62445 17.04079 3.10854 2.32077 2.46071 0.14672
9 SM_Bld_Church_01 1381.84839 1884.85574 1934.2692 1.26475 1.861 2.64343 0.00109
10 SM_Bld_CityHall_01 1.28757 1.04367 1.08939 2.47677 2.18277 1.37282 1.7584
11 SM_Bld_House_02 475.6608 884.38977 644.3027 0.78916 1.18354 0.81988 0.00142
12 SM_Bld_House_03 6.36 7.45 5.85 1.30453 1.5264 1.09218 0.1989
13 SM_Bld_House_05 7.7198 7.37393 10.36916 1.70929 0.99749 1.09207 0.154
14 SM_Bld_House_07 1132.9505 2009.90247 990.99037 1.75699 1.68834 1.33679 0.00125
15 SM_Bld_House_08 370.99999 256.02248 333.54853 1.40633 2.41373 1.22004 0.00563
16 SM_Bld_House_10 368.8779 214.66895 290.11153 1.54676 1.44984 1.4634 0.00533
17 SM_Bld_House_11 6.86464 6.03142 8.04465 2.4441 2.37924 1.76332 0.32324
18 SM_Bld_OfficeOctagon_01 103.77404 103.81444 235.11004 1.6757 1.6757 2.79256 0.01472
19 SM_Bld_OfficeOld_Large_01 5.38043 3.44203 5.37686 1.6334 1.6334 2.55487 0.41776
20 SM_Bld_OfficeRound_01 25.15329 27.18591 56.368 1.47989 1.4799 2.59303 0.05309
21 SM_Bld_OfficeSquare_01 108.28931 60.00002 326.79121 1.22006 1.22006 2.88175 0.01347
22 SM_Bld_Shop_01 13.54689 11.39883 5.07535 1.36853 1.2948 1.11662 0.14487
23 SM_Bld_Shop_02 113.9176 98.98986 52.56013 1.93887 1.34646 1.07373 0.01702
24 SM_Bld_Shop_03 1574.65564 1559.59204 786.92122 2.09754 2.2047 1.06234 0.00137
25 SM_Bld_Station_01 2941.36243 2393.23535 2560.70125 4.59497 1.67788 1.69871 0.00098
26 SM_Bld_Station_02 1.91491 2.13875 2.66549 1.62324 1.62323 1.69871 0.74798
27 SM_Env_Fence_01 4.20314 0.3206 2.73405 0.43742 0.01741 0.23644 0.08162
28 SM_Env_Fence_02 0.60854 9.84975 6.33737 0.43641 0.06116 0.25433 0.25449
29 SM_Env_Generic_Bush_01 6.73399 7.18018 5.23314 0.48032 0.31855 0.2597 0.05511
30 SM_Env_Generic_Bush_02 123.72953 77.31821 60.72777 0.28091 0.29047 0.25134 0.00339
31 SM_Env_Generic_River_Stone_02 0.10468 0.15147 0.07811 0.61023 0.54089 0.10295 3.57281
32 SM_Env_Generic_Rock_04 2.00732 1.99077 1.55727 0.85123 0.74968 0.1652 0.30224
33 SM_Env_Generic_Tree_01 0.6856 0.5899 0.7368 0.54981 0.47615 1.11742 1.0419
34 SM_Env_Generic_Tree_03 0.36783 0.38901 0.56396 1.02748 1.01284 1.08527 2.44045
35 SM_Env_Generic_Tree_04 335.77576 335.60579 576.65575 0.52926 0.48412 0.9674 0.00157
36 SM_Env_Grass_01 1.37798 1.58631 1.80551 0.33247 0.42413 0.31007 0.22679
37 SM_Env_Pool_01 2.0 2.0 1.77263 1.14304 1.14304 0.27115 0.432
38 SM_Env_RoadPatch_01 38.2175 38.94163 1.36702 0.71528 0.47709 0.02005 0.01521
39 SM_Env_WallPark_Corner_01 0.80317 0.79611 0.45142 1.12481 1.12481 0.63994 1.41032
40 SM_Env_WallPark_Corner_02 1.11296 0.49945 0.33097 1.63778 1.11307 0.63994 1.87789
41 SM_Env_WallPark_Straight_01 106.5006 23.27893 53.24705 1.12171 0.26935 0.63994 0.01137
42 SM_Generic_Ground_01 1018.54285 1018.54303 844.66702 3.50151 3.59726 0.16318 0.00239
43 SM_Prop_BBQ_01 279.16141 95.69674 184.67297 0.53033 0.3585 0.45431 0.0027
44 SM_Prop_Billboard_Base_01 44.29373 271.14278 147.36865 0.4614 0.12016 0.37081 0.00446
45 SM_Prop_BusStop_01 3.81107 1.61119 2.525 0.77668 0.30664 0.52219 0.20031
46 SM_Prop_CardboardBox_01 8.71478 7.08694 6.84178 0.34782 0.33854 0.27174 0.04247
47 SM_Prop_Drain_01 19.23742 19.2913 0.66166 0.48729 0.29083 0.06054 0.04397
48 SM_Prop_Dumpster_Closed_01 1.19785 1.97377 1.5135 0.60115 0.41735 0.52524 0.35345
49 SM_Prop_Dumpster_Open_01 175.42211 103.07324 129.3989 0.60115 0.45428 0.89502 0.00492
50 SM_Prop_FireHydrant_01 0.60019 0.51805 1.12728 0.24522 0.23678 0.44447 0.41997
51 SM_Prop_FlagPole_01 161.82344 227.43806 57.60232 0.0603 0.40035 0.4776 0.00347
52 SM_Prop_Flamingo_01 48.91436 16.33452 69.17754 0.30538 0.0908 0.44651 0.00609
53 SM_Prop_Fountain_01 13.906 13.906 8.45385 0.76971 0.66661 0.40239 0.0503
54 SM_Prop_Garden_01 15.37682 15.37682 3.04318 0.40087 0.40087 0.21626 0.04107
55 SM_Prop_Garden_02 1.62948 1.10473 0.07707 0.75537 0.40087 0.2212 1.23218
56 SM_Prop_Helipad_01 10.91023 10.91023 0.23701 0.92692 0.92693 0.06836 0.15278
57 SM_Prop_HotdogCart_01 3.84464 3.84464 3.59613 0.67027 0.5935 0.8584 0.18914
58 SM_Prop_LetterBox_01 0.00998 0.01461 0.04365 0.10155 0.17545 0.46047 10.91113
59 SM_Prop_OutdoorHeader_01 2.96217 2.96217 2.77573 0.353 0.30571 0.67326 0.15498
60 SM_Prop_ParkBench_01 7.28595 3.71581 5.65442 0.45142 0.16979 0.28417 0.05264
61 SM_Prop_PhoneBooth_01 38.87959 37.3667 156.15133 0.79749 0.35382 0.69589 0.01148
62 SM_Prop_Playground_01 7.0331 11.45797 6.56647 0.6233 0.92598 0.58828 0.08634
63 SM_Prop_Playground_02 1.5 2.0 0.75 0.52417 0.52464 0.22678 0.30471
64 SM_Prop_Playground_03 2.73628 6.26311 5.25295 0.28082 0.70537 0.35804 0.09447
65 SM_Prop_Playground_04 2.0 2.0 0.789 0.83021 0.83021 0.22225 0.37063
66 SM_Prop_Pool_Float_Ring_05 0.97817 1.00072 0.82379 0.3303 0.32967 0.19202 0.30007
67 SM_Prop_Potplants_01 11.36936 1.24811 2.17385 0.36419 0.15104 0.18395 0.07922
68 SM_Prop_PowerBox_01 0.39528 0.66 0.75845 0.42847 0.23621 0.40737 0.65966
69 SM_Prop_RoadBump_01 107.00941 11.60036 53.23364 1.28321 0.2008 0.05406 0.01011
70 SM_Prop_Roadcone_01 1.524 1.524 2.54 0.19298 0.19299 0.24189 0.11616
71 SM_Prop_Sign_02 0.24552 1.84776 7.0 0.2689 0.09427 0.74396 0.41751
72 SM_Prop_Sign_IceCream_01 10.31887 10.50033 30.03268 0.42924 0.37173 1.00917 0.03687
73 SM_Prop_Sign_Popcorn_01 1.35837 1.35837 2.0 0.42322 0.42127 0.70028 0.32394
74 SM_Prop_Spa_01 8.79603 8.79603 2.37212 0.81437 0.70527 0.28537 0.09769
75 SM_Prop_Swingset_01 17.69562 18.9807 21.45482 0.86507 0.65536 0.72705 0.0391
76 SM_Prop_TrafficLight_01 1.20177 0.99179 7.41 0.30846 0.36292 1.38417 0.2698
77 SM_Prop_Trashbag_01 2.0 1.67131 1.91278 0.33138 0.33138 0.36199 0.1844
78 SM_Prop_WaterTower_01 1.97973 3.08043 4.21858 0.44216 0.44216 1.05823 0.20591
79 SM_Tile_Bridge_01 4822.71106 998.0307 969.53262 1.79035 4.99597 0.85209 0.00209
80 SM_Tile_Cube_Concrete_01 11.81102 11.81102 1.96851 5.0 5.0 0.43242 0.35545
81 SM_Tile_Cube_Concrete_03 6.00927 5.84295 0.45 5.0 5.0 0.43242 0.8829
82 SM_Tile_Cube_Concrete_04 1.0 1.0 0.0 5.00027 5.00076 0.43147 5.00052
83 SM_Tile_Cube_Grass_01 5.0 5.0 0.13838 5.0 5.0 0.50198 1.87585
84 SM_Tile_Cube_Path_Grass_03 0.84288 0.84628 0.14712 5.0 5.0 0.50198 5.0841
85 SM_Tile_Cube_Road_Concrete_01 2307.99976 1460.00024 48.00039 5.0 5.0 0.43242 0.00487
86 SM_Tile_Cube_Road_Concrete_02 996.85956 1141.18781 21.35264 5.0 5.0 0.43242 0.00988
87 SM_Tile_Cube_Road_Concrete_04 0.90794 0.14309 0.82242 5.0 5.0 0.43242 13.6586
88 SM_Tile_Cube_Road_Grass_07 84.8795 84.85878 15.86925 5.0 5.0 0.50364 0.04986
89 SM_Tile_Hex_Grass_01 1.70759 0.9076 0.20707 5.77417 5.00076 0.49279 3.75706
90 SM_Tile_Ramp_14 1200.00006 555.99994 467.57274 1.79035 5.00005 0.96407 0.00418
91 SM_Veh_Bus_01 11.50354 4.82497 4.84264 1.03079 2.40287 0.88361 0.25669
92 SM_Veh_Car_Ambo_01 2.747 5.6364 3.0023 0.81924 1.41634 0.97278 0.29118
93 SM_Veh_Car_Medium_01 1.94016 4.23 1.92969 0.64088 1.48005 0.73093 0.353
94 SM_Veh_Car_Muscle_01 1.93382 4.55527 1.22209 0.84551 1.66101 0.53417 0.41298
95 SM_Veh_Car_Police_01 3.77022 1.97826 1.55441 0.79713 1.47207 0.71478 0.4718
96 SM_Veh_Car_Sedan_01 5.14178 9.38673 5.15166 0.79713 1.44128 0.59798 0.14155
97 SM_Veh_Car_Small_01 1.8559 3.80523 1.49906 0.70371 1.17695 0.5327 0.34794
98 SM_Veh_Car_Taxi_01 0.13286 0.25675 0.12331 0.79713 1.44128 0.73805 5.86622
99 SM_Veh_Car_Van_01 5.20041 11.17683 4.98729 0.81924 1.38511 0.74 0.14328
100 SM_Veh_Convertable_01 1.90525 0.80117 0.54993 0.83514 1.51832 0.48688 1.07294
101 SM_Veh_Pickup_01 7.51748 2.88886 3.06634 0.88997 1.5441 0.85548 0.31063
102 SM_Veh_Truck_01 0.99336 2.0 1.05006 0.83043 1.91525 1.04004 0.92802
@@ -0,0 +1,64 @@
param([switch]$NoBackup)
$ErrorActionPreference = 'Stop'
$Project = Split-Path $PSScriptRoot -Parent
$AssetRoot = Join-Path $Project 'docs\asset'
$NewRoot = Get-ChildItem -LiteralPath $AssetRoot -Directory |
Where-Object { @(Get-ChildItem -LiteralPath $_.FullName -Recurse -File -Filter '*.glb').Count -gt 0 } |
Select-Object -First 1
if ($null -eq $NewRoot) { throw 'Could not find the new GLB asset folder.' }
$ScaledRoot = Join-Path $AssetRoot "$($NewRoot.Name)_project_scale_fbx\models"
$ReferenceDirs = @((Join-Path $Project 'assets\Models'), (Join-Path $Project 'assets\Model'))
$SpecialReferences = @{
'BulletTrail' = 'BulletTrail'
'SM_Item_Bullet_01' = 'bullet'
'SM_Tile_Cube_Water_0' = 'SM_Tile_Cube_Water_01'
'test' = 'test'
'WEP_98k' = 'Wep_Rifle_98k'
'WEP_M4A1 copy' = 'Wep_Rifle_1'
'WEP_Sniper_1' = 'Wep_Sniper_01'
}
function Normalize-ModelName([string]$Name) {
return (($Name.ToLowerInvariant().ToCharArray() | Where-Object { [char]::IsLetterOrDigit($_) }) -join '')
}
$References = @{}
foreach ($Directory in $ReferenceDirs) {
Get-ChildItem -LiteralPath $Directory -Recurse -File -Filter '*.fbx' | ForEach-Object {
$References[(Normalize-ModelName $_.BaseName)] = $_
}
}
$Replacements = foreach ($Glb in Get-ChildItem -LiteralPath $NewRoot.FullName -Recurse -File -Filter '*.glb' | Sort-Object FullName) {
$ReferenceName = if ($SpecialReferences.ContainsKey($Glb.BaseName)) { $SpecialReferences[$Glb.BaseName] } else { $Glb.BaseName }
$Target = $References[(Normalize-ModelName $ReferenceName)]
$Relative = $Glb.FullName.Substring($NewRoot.FullName.Length).TrimStart('\')
$Source = Join-Path $ScaledRoot ([IO.Path]::ChangeExtension($Relative, '.fbx'))
if ($null -eq $Target) { throw "No project target for: $($Glb.Name)" }
if (-not (Test-Path -LiteralPath $Source)) { throw "Scaled FBX missing: $Source" }
[PSCustomObject]@{ Source = $Source; Target = $Target.FullName; Reference = $Target.Name }
}
if (@($Replacements).Count -ne 113) { throw "Expected 113 replacements; found $(@($Replacements).Count)." }
if (@($Replacements.Target | Select-Object -Unique).Count -ne @($Replacements).Count) { throw 'Multiple new models map to the same project target.' }
if (-not $NoBackup) {
$BackupRoot = Join-Path $AssetRoot ('project_model_fbx_backup_' + (Get-Date -Format 'yyyyMMdd_HHmmss'))
New-Item -ItemType Directory -Path $BackupRoot | Out-Null
foreach ($Item in $Replacements) {
$RelativeTarget = $Item.Target.Substring($Project.Length).TrimStart('\')
$Backup = Join-Path $BackupRoot $RelativeTarget
New-Item -ItemType Directory -Force -Path (Split-Path $Backup -Parent) | Out-Null
Copy-Item -LiteralPath $Item.Target -Destination $Backup
}
}
foreach ($Item in $Replacements) {
Copy-Item -LiteralPath $Item.Source -Destination $Item.Target -Force
}
if ($BackupRoot) {
$Replacements | Export-Csv -LiteralPath (Join-Path $BackupRoot 'replacement_manifest.csv') -NoTypeInformation -Encoding UTF8
}
Write-Output "Replaced $(@($Replacements).Count) FBX files."
+117
View File
@@ -0,0 +1,117 @@
"""Export new GLB assets as FBX, scaled to the matching project asset size.
The uniform scale is based on the longest bounding-box edge, which preserves
each new model's proportions while matching the project's visual scale.
"""
from pathlib import Path
import csv
import bpy
from mathutils import Vector
PROJECT = Path(__file__).resolve().parent.parent
ASSET_ROOT = PROJECT / "docs" / "asset"
NEW_ROOT = next(path for path in ASSET_ROOT.iterdir() if path.is_dir() and any(path.rglob("*.glb")))
OUTPUT_ROOT = NEW_ROOT.parent / f"{NEW_ROOT.name}_project_scale_fbx" / "models"
REFERENCE_DIRS = (PROJECT / "assets" / "Models", PROJECT / "assets" / "Model")
SPECIAL_REFERENCES = {
"BulletTrail": "BulletTrail",
"SM_Item_Bullet_01": "bullet",
"SM_Tile_Cube_Water_0": "SM_Tile_Cube_Water_01",
"test": "test",
"WEP_98k": "Wep_Rifle_98k",
"WEP_M4A1 copy": "Wep_Rifle_1",
"WEP_Sniper_1": "Wep_Sniper_01",
}
def clear_scene():
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)
def bounds():
points = []
for obj in bpy.context.scene.objects:
if obj.type == "MESH":
points.extend(obj.matrix_world @ Vector(corner) for corner in obj.bound_box)
if not points:
raise RuntimeError("model has no mesh objects")
low = [min(point[i] for point in points) for i in range(3)]
high = [max(point[i] for point in points) for i in range(3)]
return tuple(high[i] - low[i] for i in range(3))
def load(path: Path):
clear_scene()
if path.suffix.lower() == ".glb":
bpy.ops.import_scene.gltf(filepath=str(path))
else:
bpy.ops.import_scene.fbx(filepath=str(path))
# Cocos Creator's FBX-to-glTF converter writes invalid accessors for
# mesh objects that have materials but contain no faces. Blender keeps
# those empty mesh objects when importing some GLB files, so remove them.
for obj in list(bpy.context.scene.objects):
if obj.type == "MESH" and len(obj.data.polygons) == 0:
bpy.data.objects.remove(obj, do_unlink=True)
def normalized(name: str) -> str:
return "".join(char.lower() for char in name if char.isalnum())
references = {}
for folder in REFERENCE_DIRS:
for file in folder.rglob("*.fbx"):
references[normalized(file.stem)] = file
OUTPUT_ROOT.mkdir(parents=True, exist_ok=True)
report = []
errors = []
for glb in sorted(NEW_ROOT.rglob("*.glb")):
reference_name = SPECIAL_REFERENCES.get(glb.stem, glb.stem)
reference = references.get(normalized(reference_name))
try:
if reference is None:
raise RuntimeError(f"no project reference found for {reference_name}")
load(reference)
reference_size = bounds()
target_length = max(reference_size)
load(glb)
source_size = bounds()
source_length = max(source_size)
if source_length == 0:
raise RuntimeError("model bounding box has zero size")
scale = target_length / source_length
for obj in bpy.context.scene.objects:
if obj.parent is None:
obj.location *= scale
obj.scale *= scale
output = OUTPUT_ROOT / glb.relative_to(NEW_ROOT).with_suffix(".fbx")
output.parent.mkdir(parents=True, exist_ok=True)
bpy.ops.export_scene.fbx(
filepath=str(output), use_selection=False,
object_types={"EMPTY", "CAMERA", "LIGHT", "ARMATURE", "MESH", "OTHER"},
use_mesh_modifiers=True, add_leaf_bones=False,
path_mode="COPY", embed_textures=True,
)
report.append((str(glb.relative_to(NEW_ROOT)), reference.name, scale, source_length, target_length))
print(f"OK {glb.name}: x{scale:.8g} -> {reference.name}")
except Exception as error:
errors.append((str(glb), str(error)))
print(f"FAIL {glb.name}: {error}")
with (OUTPUT_ROOT / "scale_report.csv").open("w", newline="", encoding="utf-8-sig") as file:
writer = csv.writer(file)
writer.writerow(("source", "reference", "uniform_scale", "source_longest_edge", "target_longest_edge"))
writer.writerows(report)
if errors:
with (OUTPUT_ROOT / "errors.csv").open("w", newline="", encoding="utf-8-sig") as file:
writer = csv.writer(file)
writer.writerow(("source", "error"))
writer.writerows(errors)
print(f"Finished: {len(report)} scaled, {len(errors)} failed. Output: {OUTPUT_ROOT}")
raise SystemExit(1 if errors else 0)