3#include <assimp/mesh.h>
13 std::any_of(
values.begin(),
values.end(), [](
float x) { return !std::isfinite(x); }))
17 texcoords_.insert_or_assign(set, std::move(
owned));
22 auto found = texcoords_.find(set);
23 return found == texcoords_.end() ? std::span<const float>{} : std::span<const float>(
found->second);
28const std::vector<float> kEmptyAttribute;
33 for (
unsigned channel = 0; channel <
mesh.GetNumUVChannels(); ++channel) {
34 if (!
mesh.HasTextureCoords(channel))
continue;
36 values.reserve(
size_t(
mesh.mNumVertices) * 2u);
37 for (
unsigned i = 0; i <
mesh.mNumVertices; ++i) {
38 values.push_back(
mesh.mTextureCoords[channel][i].x);
39 values.push_back(
mesh.mTextureCoords[channel][i].y);
41 importedUvs_.push_back(std::move(
values));
43 importedColors_.clear();
44 for (
unsigned channel = 0; channel <
mesh.GetNumColorChannels(); ++channel) {
45 if (!
mesh.HasVertexColors(channel))
continue;
47 values.reserve(
size_t(
mesh.mNumVertices) * 4u);
48 for (
unsigned i = 0; i <
mesh.mNumVertices; ++i) {
49 const aiColor4D &
c =
mesh.mColors[channel][i];
52 importedColors_.push_back(std::move(
values));
54 importedTangents_.clear();
55 importedBitangents_.clear();
56 if (
mesh.HasTangentsAndBitangents()) {
57 importedTangents_.reserve(
size_t(
mesh.mNumVertices) * 3u);
58 importedBitangents_.reserve(
size_t(
mesh.mNumVertices) * 3u);
59 for (
unsigned i = 0; i <
mesh.mNumVertices; ++i) {
60 const aiVector3D &
t =
mesh.mTangents[i];
61 const aiVector3D &
b =
mesh.mBitangents[i];
62 importedTangents_.insert(importedTangents_.end(), {t.x, t.y, t.z});
63 importedBitangents_.insert(importedBitangents_.end(), {b.x, b.y, b.z});
69 return channel >= 0 &&
static_cast<size_t>(channel) < importedUvs_.size()
70 ? importedUvs_[
static_cast<size_t>(channel)]
75 return channel >= 0 &&
static_cast<size_t>(channel) < importedColors_.size()
76 ? importedColors_[
static_cast<size_t>(channel)]
81 if (!matrices || matrixCount <= 0 ||
static_cast<size_t>(matrixCount) > skinPalette_.max_size() / 16u)
83 const size_t count =
static_cast<size_t>(matrixCount) * 16u;
84 for (
size_t i = 0; i <
count; ++i)
85 if (!std::isfinite(matrices[i]))
88 std::vector<float> replacement(matrices, matrices +
count);
89 skinPalette_.swap(replacement);
102 for (
size_t i = 0; i <
n; ++i) {
103 c.x += posXYZ[i * 3u + 0
u];
104 c.y += posXYZ[i * 3u + 1u];
105 c.z += posXYZ[i * 3u + 2u];
113 for (
size_t i = 0; i <
n; ++i) {
114 const float dx = posXYZ[i * 3u + 0
u] -
c.x;
115 const float dy = posXYZ[i * 3u + 1u] -
c.y;
116 const float dz = posXYZ[i * 3u + 2u] -
c.z;
127 const float x = matrix[3][0];
128 const float y = matrix[3][1];
129 const float w = std::sqrt(matrix[0][0] * matrix[0][0] + matrix[0][1] * matrix[0][1]);
130 const float h = std::sqrt(matrix[1][0] * matrix[1][0] + matrix[1][1] * matrix[1][1]);
131 if (
w <= 0.f ||
h <= 0.f)
return;
140 morphWeights_.clear();
149 basePos_.assign(posXYZ, posXYZ +
n * 3);
151 baseNrm_.assign(nrmXYZ, nrmXYZ +
n * 3);
153 baseNrm_.assign(
n * 3, 0.f);
155 baseUv_.assign(uvST, uvST +
n * 2);
157 baseUv_.assign(
n * 2, 0.f);
161 if (
name.empty() || !deltaPosXYZ || basePos_.empty())
return false;
165 t.deltaPos.assign(deltaPosXYZ, deltaPosXYZ + basePos_.size());
166 morphs_.push_back(std::move(
t));
167 morphWeights_[
name] = 0.f;
173 if (
name.empty() || !absPosXYZ || basePos_.empty())
return false;
177 t.deltaPos.resize(basePos_.size());
178 for (
size_t i = 0; i < basePos_.size(); ++i)
179 t.deltaPos[i] = absPosXYZ[i] - basePos_[i];
180 morphs_.push_back(std::move(
t));
181 morphWeights_[
name] = 0.f;
187 if (!basePos_.empty())
return int(basePos_.size() / 3);
194 if (
index < 0 ||
size_t(
index) >= morphs_.size())
return {};
195 return morphs_[size_t(
index)].name;
199 return morphWeights_.find(
name) != morphWeights_.end();
203 auto it = morphWeights_.find(
name);
204 if (it == morphWeights_.end())
return false;
207 if (std::fabs(it->second -
weight) > 1e-6f) {
215 auto it = morphWeights_.find(
name);
216 return it == morphWeights_.end() ? 0.f : it->second;
220 for (
auto &kv : morphWeights_) {
221 if (kv.second != 0.f) {
231 if (outPos.empty())
return;
232 for (
const MorphTarget &
t : morphs_) {
233 auto it = morphWeights_.find(
t.name);
234 const float w = (it == morphWeights_.end()) ? 0.f : it->second;
235 if (std::fabs(
w) < 1e-8f)
continue;
236 for (
size_t i = 0; i < outPos.size() && i <
t.deltaPos.size(); ++i)
237 outPos[i] +=
t.deltaPos[i] *
w;
std::map< std::string, Var > values
std::uint32_t vertexCount
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
bool getCastOcclusion() const
When false, skipped by Graphics::drawOcclusion / volumetric occluder passes.
virtual void drawOcclusionSolid(float x, float y, float w, float h)
Draws occlusion solid.
void initMorphBase(int vertexCount, const float *posXYZ, const float *nrmXYZ=nullptr, const float *uvST=nullptr)
Capture base pose (xyz packed). Optional normals/uvs (same vertex count).
const std::vector< float > & importedUv(int channel) const
Imported uv.
void clearMorphData()
Clears morph data.
void computeBounds(const float *posXYZ, int vertexCount)
Compute the bounding sphere (centroid + max radius) from positions.
int gpuVertexCount
Vertex count of the GPU buffer (morph CPU base may be empty).
bool setMorphWeight(const std::string &name, float weight)
Sets the morph weight.
void captureImportedAttributes(const ::aiMesh &mesh)
Retain imported UV/color/tangent streams for custom pipelines and baking.
bool addMorphTarget(const std::string &name, const float *deltaPosXYZ)
Adds morph target.
bool hasMorph(const std::string &name) const
True when morph.
float boundsCx
Model-space bounding sphere used for view/cascade frustum culling. Computed from vertex positions at ...
void drawOcclusion(Graphics *gfx, const glm::mat4 &matrix) const override
Screen-space black proxy for volumetric occlusion. Interprets matrix as 2D affine (translation + scal...
float getMorphWeight(const std::string &name) const
Returns the morph weight.
bool addMorphTargetAbsolute(const std::string &name, const float *absPosXYZ)
Absolute morph (Assimp aiAnimMesh style): stored as delta from base.
void clearMorphWeights()
Clears morph weights.
int getMorphCount() const
Returns the morph count.
Result< void > setTexcoordSet(uint32_t set, std::span< const float > values)
Copy one packed ST channel; finite values/count are checked atomically. Render-thread only,...
std::string getMorphName(int index) const
Returns the morph name.
std::span< const float > texcoordSet(uint32_t set) const
Borrow a channel until mutation/destruction; empty means absent. Render-thread only.
int getVertexCount() const
Returns the vertex count.
const std::vector< float > & importedColor(int channel) const
Imported color.
Result< void > setSkinPalette(const float *matrices, int matrixCount)
Copy a dynamically sized column-major palette on the render thread. Invalid/non-finite input leaves t...
void computeMorphedPositions(std::vector< float > &outPos, std::vector< float > &outNrm) const
Bake current weights into outPos / outNrm (xyz packed).
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...