Check for broken animated meshes in checkrefs.

Added and integrated P293 by @trompetin17 and @Stan
Addresses #6714
This commit is contained in:
scuti 2025-02-05 22:09:46 -08:00 committed by Stanislas Dolcini
parent e218cfa170
commit 0e1c881fef
3 changed files with 96 additions and 3 deletions

View file

@ -11,6 +11,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Add remote fork origin for LFS
run: |
PR_REPO="${{ gitea.event.pull_request.head.repo.full_name || gitea.repository }}"
@ -31,9 +33,10 @@ jobs:
- name: Download necessary LFS assets
shell: sh {0}
run: |
git lfs pull -I binaries/data/mods/public/maps
git lfs pull -I binaries/data/mods/public/art/meshes,binaries/data/mods/public/maps
ORIGIN_LFS_PULL_RESULT=$?
git lfs pull ${{ gitea.actor }} -I binaries/data/mods/public/maps
git lfs pull ${{ gitea.actor }} \
-I binaries/data/mods/public/art/meshes,binaries/data/mods/public/maps
FORK_LFS_PULL_RESULT=$?
PR_REPO="${{ gitea.event.pull_request.head.repo.full_name || gitea.repository }}"
@ -47,9 +50,12 @@ jobs:
fi
- name: Install lxml
run: pip3 install lxml
- name: Install collada
run: pip3 install "pycollada>=0.9"
- name: Check for missing references
run: |
./source/tools/entity/checkrefs.py \
--check-map-xml \
--validate-templates \
--validate-actors
--validate-actors \
--validate-meshes

View file

@ -119,6 +119,12 @@ class CheckRefs:
default=["public"],
help="specify which mods to check. Default to public.",
)
ap.add_argument(
"-d",
"--validate-meshes",
action="store_true",
help="check if meshes have vertices with no weight.",
)
args = ap.parse_args()
# force check_map_xml if check_unused is used to avoid false positives.
args.check_map_xml |= args.check_unused
@ -161,6 +167,12 @@ class CheckRefs:
if not validator.run():
self.inError = True
if args.validate_meshes:
from validate_dae import DaeValidator
dv = DaeValidator(self.vfs_root, self.mods)
self.inError = not dv.run()
return not self.inError
def get_mod_dependencies(self, *mods):

View file

@ -0,0 +1,75 @@
#!/usr/bin/env python3
import sys
from logging import INFO, Formatter, StreamHandler, getLogger
from pathlib import Path
from collada import Collada, DaeBrokenRefError, DaeError, common, controller
from scriptlib import find_files
def validate_vertex(path_dae, log=print):
has_weightless = False
had_exception = False
dae = None
try:
dae = Collada(path_dae, ignore=[common.DaeUnsupportedError, DaeBrokenRefError])
except DaeError as inst:
log("Failed to load %s", path_dae)
log(type(inst), inst)
had_exception = True
for ctr in dae.controllers:
if type(ctr) is not controller.Skin:
had_exception = True
break
totalv = len(ctr.vcounts)
totalv_0 = len(ctr.vcounts[ctr.vcounts == 0])
if totalv_0 > 0:
log(
"Mesh %s has %i (out of %i) vertices with no weight"
" and no bone assigned. Use P294 to find them in Blender.",
path_dae,
totalv_0,
totalv,
)
has_weightless = True
return (int(has_weightless) << 1) | int(had_exception)
class DaeValidator:
def __init__(self, vfs_root, mods):
self.has_weightless_vtx = []
self.vfs_root = vfs_root
self.mods = mods
self.log = getLogger()
self.log.setLevel(INFO)
sh = StreamHandler(sys.stdout)
sh.setLevel(INFO)
sh.setFormatter(Formatter("%(levelname)s - %(message)s"))
self.log.addHandler(sh)
def run(self):
is_ok = True
files = find_files(self.vfs_root, self.mods, "art/meshes", "dae")
self.log.info("Checking %i meshes for invalid weights.", len(files))
for _, dae in files:
status = validate_vertex(dae.as_posix(), self.log.warning)
if status >= 1:
is_ok = False
if status >= 2:
self.has_weightless_vtx.append(dae)
self.log.info(
"%i out of %i files have vertices with no weight or bones.",
len(self.has_weightless_vtx),
len(files),
)
return is_ok
if __name__ == "__main__":
vfsr = Path(__file__).resolve().parents[3] / "binaries" / "data" / "mods"
mods = ["public"]
dv = DaeValidator(vfsr, mods)
print(f"DaeValidator returns {dv.run()}")
print(dv.has_weightless_vtx)