1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Fernando Sahmkow
#include <whiteout/models/wem/document.h>
#include <whiteout/models/wem/model.h>
namespace whiteout {
namespace models {
namespace wem {
// ============================================================================
// ProfileMaterialSet
// ============================================================================
void ProfileMaterialSet::resizeBindings(std::size_t slotCount) {
if (looks.empty()) {
looks = LookTable::Single();
}
slotBindings.resize(slotCount);
for (SlotBinding& binding : slotBindings) {
binding.byLook.resize(looks.size(), kInvalidIndex);
}
}
// ============================================================================
// Model
// ============================================================================
const ProfileMaterialSet* Model::setFor(ProfileId profile) const {
for (const ProfileMaterialSet& set : profileSets) {
if (set.profile == profile) {
return &set;
}
}
return nullptr;
}
ProfileMaterialSet* Model::setFor(ProfileId profile) {
return const_cast<ProfileMaterialSet*>(static_cast<const Model*>(this)->setFor(profile));
}
u32 Model::slotIndex(const std::string& slotName) const {
for (std::size_t i = 0; i < materialSlots.size(); ++i) {
if (materialSlots[i] == slotName) {
return static_cast<u32>(i);
}
}
return kInvalidIndex;
}
u32 Model::addSlot(const std::string& slotName) {
const u32 existing = slotIndex(slotName);
if (existing != kInvalidIndex) {
return existing;
}
materialSlots.push_back(slotName);
const u32 index = static_cast<u32>(materialSlots.size() - 1);
// The parallel-array invariant is the set's, so grow every set with the slot
// rather than leaving them to notice later.
for (ProfileMaterialSet& set : profileSets) {
set.resizeBindings(materialSlots.size());
}
return index;
}
ProfileMask Model::drawnProfiles() const {
ProfileMask mask = kNoProfiles;
for (const Mesh& mesh : meshes) {
for (const MeshSection& section : mesh.sections) {
mask |= section.profiles;
}
}
return mask;
}
const Material* Resolve(const Model& model, u32 slot, ProfileId profile, u32 look) {
const ProfileMaterialSet* set = model.setFor(profile);
if (set == nullptr || slot >= set->slotBindings.size()) {
return nullptr;
}
const SlotBinding& binding = set->slotBindings[slot];
if (look >= binding.byLook.size()) {
return nullptr;
}
const u32 material = binding.byLook[look];
if (material >= set->materials.size()) {
return nullptr;
}
return &set->materials[material];
}
// ============================================================================
// Document
// ============================================================================
bool Document::carries(ProfileId profile) const {
for (ProfileId declared : profiles) {
if (declared == profile) {
return true;
}
}
return false;
}
void Document::declare(ProfileId profile) {
if (!carries(profile)) {
profiles.push_back(profile);
}
}
ProfileMask Document::declaredMask() const {
ProfileMask mask = kNoProfiles;
for (ProfileId declared : profiles) {
mask |= ProfileBit(declared);
}
return mask;
}
} // namespace wem
} // namespace models
} // namespace whiteout