6#include <assimp/mesh.h>
7#include <assimp/scene.h>
17void computeAxis(
float u,
int divisions,
bool clamp,
int&
i0,
float& fu) {
18 const float scaled =
u *
static_cast<float>(divisions - 1);
25 if (scaled >=
static_cast<float>(divisions - 1)) {
31 i0 =
static_cast<int>(std::floor(scaled));
32 fu = scaled -
static_cast<float>(
i0);
35 }
else if (
i0 > divisions - 2) {
53 points_.assign(
static_cast<size_t>(divX_) *
static_cast<size_t>(divY_) *
static_cast<size_t>(divZ_),
59 if (sx <= 0.f || sy <= 0.f || sz <= 0.f) {
60 throw Exception(
"AnimLattice.setSize: size components must be > 0");
73int AnimLattice::pointIndex(
int ix,
int iy,
int iz)
const {
return (iz * divY_ + iy) * divX_ + ix; }
75void AnimLattice::requirePoint(
int ix,
int iy,
int iz)
const {
76 if (ix < 0 || ix >= divX_ || iy < 0 || iy >= divY_ || iz < 0 || iz >= divZ_) {
77 throw Exception(
"AnimLattice: invalid control point (%d,%d,%d) for divisions (%d,%d,%d)", ix, iy, iz, divX_,
82void AnimLattice::requireVertex(
int vertexIndex)
const {
83 if (vertexIndex < 0 || vertexIndex >= vertexCount_) {
84 throw Exception(
"AnimLattice: invalid vertex index %d", vertexIndex);
89 requirePoint(ix, iy, iz);
90 ControlPoint&
p = points_[
static_cast<size_t>(pointIndex(ix, iy, iz))];
97 requirePoint(ix, iy, iz);
98 ControlPoint&
p = points_[
static_cast<size_t>(pointIndex(ix, iy, iz))];
105 for (ControlPoint&
p : points_) {
115 requirePoint(ix, iy, iz);
116 return points_[
static_cast<size_t>(pointIndex(ix, iy, iz))].sx;
119 requirePoint(ix, iy, iz);
120 return points_[
static_cast<size_t>(pointIndex(ix, iy, iz))].sy;
123 requirePoint(ix, iy, iz);
124 return points_[
static_cast<size_t>(pointIndex(ix, iy, iz))].sz;
127 requirePoint(ix, iy, iz);
128 return points_[
static_cast<size_t>(pointIndex(ix, iy, iz))].ox;
131 requirePoint(ix, iy, iz);
132 return points_[
static_cast<size_t>(pointIndex(ix, iy, iz))].oy;
135 requirePoint(ix, iy, iz);
136 return points_[
static_cast<size_t>(pointIndex(ix, iy, iz))].oz;
141 throw Exception(
"AnimLattice.fromModel: null model");
143 const aiMesh*
mesh =
model->getMesh(meshIndex);
145 throw Exception(
"AnimLattice.fromModel: invalid mesh index %d", meshIndex);
147 if (
mesh->mNumVertices == 0 || !
mesh->mVertices) {
148 throw Exception(
"AnimLattice.fromModel: mesh %d has no vertices", meshIndex);
151 lattice->bindPositions(
reinterpret_cast<const float*
>(
mesh->mVertices),
static_cast<int>(
mesh->mNumVertices));
157 throw Exception(
"AnimLattice.bindModel: null model");
159 const aiMesh*
mesh =
model->getMesh(meshIndex);
161 throw Exception(
"AnimLattice.bindModel: invalid mesh index %d", meshIndex);
163 if (
mesh->mNumVertices == 0 || !
mesh->mVertices) {
164 throw Exception(
"AnimLattice.bindModel: mesh %d has no vertices", meshIndex);
166 bindPositions(
reinterpret_cast<const float*
>(
mesh->mVertices),
static_cast<int>(
mesh->mNumVertices));
170 if (!posXYZ || count < 0) {
171 throw Exception(
"AnimLattice.bindPositions: invalid position data");
173 vertexCount_ = count;
174 bindPos_.resize(
static_cast<size_t>(count) * 3u);
176 std::copy(posXYZ, posXYZ +
static_cast<size_t>(count) * 3u, bindPos_.begin());
178 deformedValid_ =
false;
179 deformedNrmValid_ =
false;
185 deformedValid_ =
false;
186 deformedNrmValid_ =
false;
190 requireVertex(vertexIndex);
191 return bindPos_[
static_cast<size_t>(vertexIndex) * 3u + 0];
194 requireVertex(vertexIndex);
195 return bindPos_[
static_cast<size_t>(vertexIndex) * 3u + 1];
198 requireVertex(vertexIndex);
199 return bindPos_[
static_cast<size_t>(vertexIndex) * 3u + 2];
202void AnimLattice::computeCell(
float x,
float y,
float z,
int&
i0,
int& j0,
int& k0,
float& fu,
float& fv,
204 const float u = (
x - (originX_ - sizeX_ * 0.5f)) / sizeX_;
205 const float v = (
y - (originY_ - sizeY_ * 0.5f)) / sizeY_;
206 const float w = (
z - (originZ_ - sizeZ_ * 0.5f)) / sizeZ_;
207 computeAxis(
u, divX_, clamp_,
i0, fu);
208 computeAxis(
v, divY_, clamp_, j0, fv);
209 computeAxis(
w, divZ_, clamp_, k0, fw);
213 if (!inPosXYZ || !outPosXYZ) {
214 throw Exception(
"AnimLattice.deformPositions: null position buffer");
217 throw Exception(
"AnimLattice.deformPositions: negative vertex count");
219 for (
int v = 0;
v < count; ++
v) {
220 const float x = inPosXYZ[
static_cast<size_t>(
v) * 3u + 0];
221 const float y = inPosXYZ[
static_cast<size_t>(
v) * 3u + 1];
222 const float z = inPosXYZ[
static_cast<size_t>(
v) * 3u + 2];
226 computeCell(
x,
y,
z,
i0, j0, k0, fu, fv, fw);
228 float ox = 0.f, oy = 0.f, oz = 0.f;
229 float sx = 0.f, sy = 0.f, sz = 0.f;
230 for (
int di = 0; di < 2; ++di) {
231 const float wu = di ? fu : 1.f - fu;
232 for (
int dj = 0; dj < 2; ++dj) {
233 const float wv = dj ? fv : 1.f - fv;
234 for (
int dk = 0; dk < 2; ++dk) {
235 const float ww = dk ? fw : 1.f - fw;
236 const float weight = wu * wv * ww;
237 const ControlPoint&
p = points_[
static_cast<size_t>(pointIndex(
i0 + di, j0 + dj, k0 + dk))];
248 const float rx =
x - originX_;
249 const float ry =
y - originY_;
250 const float rz =
z - originZ_;
251 outPosXYZ[
static_cast<size_t>(
v) * 3u + 0] = originX_ + ox + sx * rx;
252 outPosXYZ[
static_cast<size_t>(
v) * 3u + 1] = originY_ + oy + sy * ry;
253 outPosXYZ[
static_cast<size_t>(
v) * 3u + 2] = originZ_ + oz + sz * rz;
258 if (inPosXYZ.empty() || inPosXYZ.size() % 3u != 0)
return false;
259 const int count =
static_cast<int>(inPosXYZ.size() / 3u);
260 outPosXYZ.resize(inPosXYZ.size());
266 if (!posXYZ || !inNrmXYZ || !outNrmXYZ || count < 0)
return false;
267 if (vertexCount_ > 0 && count != vertexCount_)
return false;
268 for (
int v = 0;
v < count; ++
v) {
269 const float px = posXYZ[
static_cast<size_t>(
v) * 3u + 0];
270 const float py = posXYZ[
static_cast<size_t>(
v) * 3u + 1];
271 const float pz = posXYZ[
static_cast<size_t>(
v) * 3u + 2];
275 computeCell(
px, py, pz,
i0, j0, k0, fu, fv, fw);
277 float sx = 0.f, sy = 0.f, sz = 0.f;
278 for (
int di = 0; di < 2; ++di) {
279 const float wu = di ? fu : 1.f - fu;
280 for (
int dj = 0; dj < 2; ++dj) {
281 const float wv = dj ? fv : 1.f - fv;
282 for (
int dk = 0; dk < 2; ++dk) {
283 const float ww = dk ? fw : 1.f - fw;
284 const float weight = wu * wv * ww;
285 const ControlPoint&
p = points_[
static_cast<size_t>(pointIndex(
i0 + di, j0 + dj, k0 + dk))];
293 const float nx = sx * inNrmXYZ[
static_cast<size_t>(
v) * 3u + 0];
294 const float ny = sy * inNrmXYZ[
static_cast<size_t>(
v) * 3u + 1];
295 const float nz = sz * inNrmXYZ[
static_cast<size_t>(
v) * 3u + 2];
296 const float len = std::sqrt(nx * nx + ny * ny + nz * nz);
298 outNrmXYZ[
static_cast<size_t>(
v) * 3u + 0] = nx / len;
299 outNrmXYZ[
static_cast<size_t>(
v) * 3u + 1] = ny / len;
300 outNrmXYZ[
static_cast<size_t>(
v) * 3u + 2] = nz / len;
302 outNrmXYZ[
static_cast<size_t>(
v) * 3u + 0] = inNrmXYZ[
static_cast<size_t>(
v) * 3u + 0];
303 outNrmXYZ[
static_cast<size_t>(
v) * 3u + 1] = inNrmXYZ[
static_cast<size_t>(
v) * 3u + 1];
304 outNrmXYZ[
static_cast<size_t>(
v) * 3u + 2] = inNrmXYZ[
static_cast<size_t>(
v) * 3u + 2];
311 if (vertexCount_ <= 0 || inPosXYZ.size() <
static_cast<size_t>(vertexCount_) * 3u) {
312 deformedValid_ =
false;
315 deformedPos_.resize(
static_cast<size_t>(vertexCount_) * 3u);
317 deformedValid_ =
true;
322 if (vertexCount_ <= 0) {
323 deformedValid_ =
false;
330 if (vertexCount_ <= 0 || posXYZ.size() <
static_cast<size_t>(vertexCount_) * 3u ||
331 inNrmXYZ.size() <
static_cast<size_t>(vertexCount_) * 3u) {
332 deformedNrmValid_ =
false;
335 deformedNrm_.resize(
static_cast<size_t>(vertexCount_) * 3u);
336 if (!
deformNormals(posXYZ.data(), inNrmXYZ.data(), deformedNrm_.data(), vertexCount_)) {
337 deformedNrmValid_ =
false;
340 deformedNrmValid_ =
true;
345 requireVertex(vertexIndex);
346 if (!deformedValid_) {
347 throw Exception(
"AnimLattice.getDeformedPositionX: call updateDeformedPositions first");
349 return deformedPos_[
static_cast<size_t>(vertexIndex) * 3u + 0];
352 requireVertex(vertexIndex);
353 if (!deformedValid_) {
354 throw Exception(
"AnimLattice.getDeformedPositionY: call updateDeformedPositions first");
356 return deformedPos_[
static_cast<size_t>(vertexIndex) * 3u + 1];
359 requireVertex(vertexIndex);
360 if (!deformedValid_) {
361 throw Exception(
"AnimLattice.getDeformedPositionZ: call updateDeformedPositions first");
363 return deformedPos_[
static_cast<size_t>(vertexIndex) * 3u + 2];
367 return deformedValid_ ? deformedPos_ : std::vector<float>();
371 return deformedNrmValid_ ? deformedNrm_ : std::vector<float>();
CPU 3D lattice scale-deformer (晶格缩放变形).
void setDivisions(int divX, int divY, int divZ)
Changes divisions; resets all control points to identity and clears the bind.
float getDeformedPositionX(int vertexIndex) const
Cached deformed position component (requires updateDeformedPositions).
std::vector< float > getDeformedNormals() const
Copy of the cached deformed normals (xyz packed; empty when not updated).
std::vector< float > getDeformedPositions() const
Copy of the cached deformed positions (xyz packed; empty when not updated).
void setPointScale(int ix, int iy, int iz, float sx, float sy, float sz)
Per-control-point scale (squash / stretch about the lattice origin).
float getPointOffsetX(int ix, int iy, int iz) const
bool updateDeformedPositions()
Deform the bound bind positions into the internal cache.
bool deformNormals(const float *posXYZ, const float *inNrmXYZ, float *outNrmXYZ, int count) const
Transforms normals with the interpolated scale field and renormalizes. posXYZ provides the (deformed)...
float getBindPositionZ(int vertexIndex) const
void setOrigin(float ox, float oy, float oz)
Center of the lattice box in model space.
void bindModel(const model3d::ModelData *model, int meshIndex)
Binds the current lattice to meshIndex of model (uses current divisions).
bool updateDeformedNormals(const std::vector< float > &posXYZ, const std::vector< float > &inNrmXYZ)
Deform packed xyz normals (with packed positions for cell lookup) into the internal normal cache.
float getPointScaleX(int ix, int iy, int iz) const
void deformPositions(const float *inPosXYZ, float *outPosXYZ, int count) const
Deforms count packed xyz positions into outPosXYZ. Both buffers must hold count * 3 floats; count mus...
float getPointOffsetY(int ix, int iy, int iz) const
float getDeformedPositionY(int vertexIndex) const
void setSize(float sx, float sy, float sz)
World-space size of the lattice box (components must be > 0).
float getDeformedPositionZ(int vertexIndex) const
void setPointOffset(int ix, int iy, int iz, float dx, float dy, float dz)
Per-control-point translation offset (local bulge / pinch).
float getPointScaleY(int ix, int iy, int iz) const
void setScale(float sx, float sy, float sz)
Applies one scale to every control point (whole-lattice squash & stretch).
static constexpr int kMinDivisions
static AnimLattice * fromModel(const model3d::ModelData *model, int meshIndex, int divX, int divY, int divZ)
Builds a lattice bound to meshIndex of model (Assimp vertices). Throws on null model / invalid mesh /...
bool deformPositionsTo(const std::vector< float > &inPosXYZ, std::vector< float > &outPosXYZ) const
Convenience: deform into a vector sized count*3.
float getBindPositionX(int vertexIndex) const
Bind-pose position component for vertex v (0..vertexCount-1).
float getPointOffsetZ(int ix, int iy, int iz) const
void reset()
Resets every control point to identity (offset 0, scale 1).
float getPointScaleZ(int ix, int iy, int iz) const
void bindPositions(const float *posXYZ, int count)
Binds a packed xyz array (count * 3 floats). Uses current divisions/size/origin.
float getBindPositionY(int vertexIndex) const
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...