mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 17:43:51 +00:00
feat(editor): add --export-bones-dot for Graphviz bone-tree visualization
Renders WOM bone hierarchy as Graphviz DOT. Mirrors --export-quest-graph for skeleton trees: a 50-bone tree from --info-bones is hard to read in text; pipe this through 'dot -Tpng' for the picture: wowee_editor --export-bones-dot HumanMale dot -Tpng HumanMale.bones.dot -o bones.png Visual encoding: - lightgreen fill: keybones (named anchor points referenced by gameplay systems — head, hands, feet, etc.) - lightgrey fill: internal/blend bones (non-key, used for shape only) - goldenrod border: root bones (parent=-1, top of skeleton hierarchy) Edges flow parent -> child (rankdir=TB so root is at top, leaves at bottom). Useful for skeleton-debugging: - 'why is this finger not following its hand?' -> see the parent chain - 'is this bone really a root or did its parent get deleted?' -> goldenrod border makes intentional roots vs accidental ones obvious - understanding which bones gameplay code can reference by key id Verified on a 5-bone synthesized skeleton (3-deep chain + 1 detached root + mix of key/internal): DOT correctly emits 5 nodes with the right colors (3 lightgreen for key bones, 2 lightgrey for internal, 2 with goldenrod root borders), 3 edges traversing the chain.
This commit is contained in:
parent
8d9690a57a
commit
20bd417002
1 changed files with 60 additions and 1 deletions
|
|
@ -701,6 +701,8 @@ static void printUsage(const char* argv0) {
|
|||
std::printf(" List M2 animation sequences (id, duration, flags)\n");
|
||||
std::printf(" --info-bones <m2-path> [--json]\n");
|
||||
std::printf(" List M2 bones with parent tree, key-bone IDs, pivot offsets\n");
|
||||
std::printf(" --export-bones-dot <wom-base> [out.dot]\n");
|
||||
std::printf(" Render WOM bone hierarchy as Graphviz DOT (pipe to `dot -Tpng -o bones.png`)\n");
|
||||
std::printf(" --list-zone-textures <zoneDir> [--json]\n");
|
||||
std::printf(" Aggregate texture refs across all WOM models in a zone (deduped)\n");
|
||||
std::printf(" --info-wob <wob-base> [--json]\n");
|
||||
|
|
@ -820,7 +822,7 @@ int main(int argc, char* argv[]) {
|
|||
static const char* kArgRequired[] = {
|
||||
"--data", "--info", "--info-batches", "--info-textures", "--info-doodads",
|
||||
"--info-attachments", "--info-particles", "--info-sequences",
|
||||
"--info-bones", "--list-zone-textures",
|
||||
"--info-bones", "--export-bones-dot", "--list-zone-textures",
|
||||
"--info-wob", "--info-woc", "--info-wot",
|
||||
"--info-creatures", "--info-objects", "--info-quests",
|
||||
"--info-extract", "--info-extract-tree", "--info-extract-budget",
|
||||
|
|
@ -1526,6 +1528,63 @@ int main(int argc, char* argv[]) {
|
|||
b.pivot.x, b.pivot.y, b.pivot.z);
|
||||
}
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--export-bones-dot") == 0 && i + 1 < argc) {
|
||||
// Render WOM bone hierarchy as Graphviz DOT. Mirrors
|
||||
// --export-quest-graph for skeleton trees: trying to read
|
||||
// a 50-bone tree from --info-bones output is painful;
|
||||
// pipe this through `dot -Tpng` for the picture.
|
||||
std::string base = argv[++i];
|
||||
std::string outPath;
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') outPath = argv[++i];
|
||||
if (base.size() >= 4 && base.substr(base.size() - 4) == ".wom")
|
||||
base = base.substr(0, base.size() - 4);
|
||||
if (!wowee::pipeline::WoweeModelLoader::exists(base)) {
|
||||
std::fprintf(stderr,
|
||||
"export-bones-dot: WOM not found: %s.wom\n", base.c_str());
|
||||
return 1;
|
||||
}
|
||||
if (outPath.empty()) outPath = base + ".bones.dot";
|
||||
auto wom = wowee::pipeline::WoweeModelLoader::load(base);
|
||||
std::ofstream out(outPath);
|
||||
if (!out) {
|
||||
std::fprintf(stderr,
|
||||
"export-bones-dot: cannot write %s\n", outPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
out << "digraph BoneTree {\n";
|
||||
out << " // Generated by wowee_editor --export-bones-dot\n";
|
||||
out << " rankdir=TB;\n";
|
||||
out << " node [shape=box, style=filled, fontname=\"sans-serif\", fontsize=10];\n";
|
||||
// Color: green for keybones (named anchor points), gray for
|
||||
// internal/blend bones. Root bones (parent=-1) get yellow border.
|
||||
for (size_t k = 0; k < wom.bones.size(); ++k) {
|
||||
const auto& b = wom.bones[k];
|
||||
bool isKey = (b.keyBoneId >= 0);
|
||||
std::string fill = isKey ? "lightgreen" : "lightgrey";
|
||||
std::string label = "[" + std::to_string(k) + "]";
|
||||
if (isKey) label += "\\nkey=" + std::to_string(b.keyBoneId);
|
||||
out << " b" << k << " [label=\"" << label
|
||||
<< "\", fillcolor=" << fill;
|
||||
if (b.parentBone == -1) out << ", penwidth=2, color=goldenrod";
|
||||
out << "];\n";
|
||||
}
|
||||
// Edges: child -> parent (parent is up).
|
||||
int rootCount = 0;
|
||||
for (size_t k = 0; k < wom.bones.size(); ++k) {
|
||||
int16_t p = wom.bones[k].parentBone;
|
||||
if (p < 0 || p >= static_cast<int16_t>(wom.bones.size())) {
|
||||
rootCount++;
|
||||
continue;
|
||||
}
|
||||
out << " b" << p << " -> b" << k << ";\n";
|
||||
}
|
||||
out << "}\n";
|
||||
out.close();
|
||||
std::printf("Wrote %s\n", outPath.c_str());
|
||||
std::printf(" %zu bones, %d root(s)\n",
|
||||
wom.bones.size(), rootCount);
|
||||
std::printf(" next: dot -Tpng %s -o bones.png\n", outPath.c_str());
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--list-zone-textures") == 0 && i + 1 < argc) {
|
||||
// Aggregate texture references across every WOM model in a
|
||||
// zone directory. Companion to --list-zone-deps (which lists
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue