Анимация текстур по сетке

This commit is contained in:
2026-01-05 13:47:53 +06:00
parent d3add82c55
commit ad4b0d593a

View File

@@ -30,7 +30,8 @@ public:
enum AnimFlags : Word { enum AnimFlags : Word {
AnimSmooth = 1u << 0, AnimSmooth = 1u << 0,
AnimHorizontal = 1u << 1 AnimHorizontal = 1u << 1,
AnimGrid = 1u << 2
}; };
static constexpr uint16_t DefaultAnimFpsQ = uint16_t(8u * 256u); static constexpr uint16_t DefaultAnimFpsQ = uint16_t(8u * 256u);
@@ -787,9 +788,17 @@ private:
uint32_t fh = frameH ? frameH : sheet.H; uint32_t fh = frameH ? frameH : sheet.H;
if(fw == 0 || fh == 0) return _bad(err, "Base_Anim invalid frame size"); if(fw == 0 || fh == 0) return _bad(err, "Base_Anim invalid frame size");
bool useGrid = (flags & AnimGrid) != 0;
bool horizontal = (flags & AnimHorizontal) != 0; bool horizontal = (flags & AnimHorizontal) != 0;
if(frameCount == 0) { if(frameCount == 0) {
uint32_t avail = horizontal ? (sheet.W / fw) : (sheet.H / fh); uint32_t avail = 0;
if(useGrid) {
uint32_t cols = sheet.W / fw;
uint32_t rows = sheet.H / fh;
avail = cols * rows;
} else {
avail = horizontal ? (sheet.W / fw) : (sheet.H / fh);
}
frameCount = std::max<uint32_t>(1u, avail); frameCount = std::max<uint32_t>(1u, avail);
} }
@@ -801,11 +810,13 @@ private:
uint32_t frameIndex = frameCount ? (uint32_t(frameTime) % frameCount) : 0u; uint32_t frameIndex = frameCount ? (uint32_t(frameTime) % frameCount) : 0u;
double frac = frameTime - std::floor(frameTime); double frac = frameTime - std::floor(frameTime);
cur = _cropFrame(sheet, frameIndex, fw, fh, horizontal); cur = useGrid ? _cropFrameGrid(sheet, frameIndex, fw, fh)
: _cropFrame(sheet, frameIndex, fw, fh, horizontal);
if(flags & AnimSmooth) { if(flags & AnimSmooth) {
uint32_t nextIndex = frameCount ? ((frameIndex + 1u) % frameCount) : 0u; uint32_t nextIndex = frameCount ? ((frameIndex + 1u) % frameCount) : 0u;
Image next = _cropFrame(sheet, nextIndex, fw, fh, horizontal); Image next = useGrid ? _cropFrameGrid(sheet, nextIndex, fw, fh)
: _cropFrame(sheet, nextIndex, fw, fh, horizontal);
_lerp(cur, next, frac); _lerp(cur, next, frac);
} }
} break; } break;
@@ -825,9 +836,17 @@ private:
uint32_t fh = frameH ? frameH : sheet.H; uint32_t fh = frameH ? frameH : sheet.H;
if(fw == 0 || fh == 0) return _bad(err, "Anim invalid frame size"); if(fw == 0 || fh == 0) return _bad(err, "Anim invalid frame size");
bool useGrid = (flags & AnimGrid) != 0;
bool horizontal = (flags & AnimHorizontal) != 0; bool horizontal = (flags & AnimHorizontal) != 0;
if(frameCount == 0) { if(frameCount == 0) {
uint32_t avail = horizontal ? (sheet.W / fw) : (sheet.H / fh); uint32_t avail = 0;
if(useGrid) {
uint32_t cols = sheet.W / fw;
uint32_t rows = sheet.H / fh;
avail = cols * rows;
} else {
avail = horizontal ? (sheet.W / fw) : (sheet.H / fh);
}
frameCount = std::max<uint32_t>(1u, avail); frameCount = std::max<uint32_t>(1u, avail);
} }
@@ -839,10 +858,12 @@ private:
uint32_t frameIndex = frameCount ? (uint32_t(frameTime) % frameCount) : 0u; uint32_t frameIndex = frameCount ? (uint32_t(frameTime) % frameCount) : 0u;
double frac = frameTime - std::floor(frameTime); double frac = frameTime - std::floor(frameTime);
cur = _cropFrame(sheet, frameIndex, fw, fh, horizontal); cur = useGrid ? _cropFrameGrid(sheet, frameIndex, fw, fh)
: _cropFrame(sheet, frameIndex, fw, fh, horizontal);
if(flags & AnimSmooth) { if(flags & AnimSmooth) {
uint32_t nextIndex = frameCount ? ((frameIndex + 1u) % frameCount) : 0u; uint32_t nextIndex = frameCount ? ((frameIndex + 1u) % frameCount) : 0u;
Image next = _cropFrame(sheet, nextIndex, fw, fh, horizontal); Image next = useGrid ? _cropFrameGrid(sheet, nextIndex, fw, fh)
: _cropFrame(sheet, nextIndex, fw, fh, horizontal);
_lerp(cur, next, frac); _lerp(cur, next, frac);
} }
} break; } break;
@@ -1090,6 +1111,36 @@ private:
return out; return out;
} }
static Image _cropFrameGrid(const Image& sheet, uint32_t index, uint32_t fw, uint32_t fh) {
Image out;
out.W = fw;
out.H = fh;
out.Px.assign(size_t(fw) * size_t(fh), 0u);
if(fw == 0 || fh == 0) return out;
uint32_t cols = sheet.W / fw;
uint32_t rows = sheet.H / fh;
if(cols == 0 || rows == 0) return out;
uint32_t col = index % cols;
uint32_t row = index / cols;
if(row >= rows) return out;
uint32_t baseX = col * fw;
uint32_t baseY = row * fh;
for(uint32_t y = 0; y < fh; ++y) {
uint32_t sy = baseY + y;
if(sy >= sheet.H) continue;
for(uint32_t x = 0; x < fw; ++x) {
uint32_t sx = baseX + x;
if(sx >= sheet.W) continue;
out.Px[size_t(y) * fw + x] = sheet.Px[size_t(sy) * sheet.W + sx];
}
}
return out;
}
static void _lerp(Image& base, const Image& over, double t) { static void _lerp(Image& base, const Image& over, double t) {
if(t <= 0.0) return; if(t <= 0.0) return;
if(t >= 1.0) { base = over; return; } if(t >= 1.0) { base = over; return; }
@@ -1607,7 +1658,9 @@ private:
uint32_t smooth = namedU("smooth").value_or(posU(5).value_or(0)); uint32_t smooth = namedU("smooth").value_or(posU(5).value_or(0));
std::string axis = namedS("axis").value_or(""); std::string axis = namedS("axis").value_or("");
bool horizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h')); bool axisHorizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h'));
bool axisVertical = (!axis.empty() && (axis[0] == 'y' || axis[0] == 'v'));
bool useGrid = axis.empty() || axis[0] == 'g';
if(frameW > 65535u || frameH > 65535u || frames > 65535u) { if(frameW > 65535u || frameH > 65535u || frames > 65535u) {
if(err) *err="параметры anim должны помещаться в uint16"; if(err) *err="параметры anim должны помещаться в uint16";
@@ -1615,7 +1668,10 @@ private:
} }
uint32_t fpsQ = fps ? std::min<uint32_t>(0xFFFFu, fps * 256u) : DefaultAnimFpsQ; uint32_t fpsQ = fps ? std::min<uint32_t>(0xFFFFu, fps * 256u) : DefaultAnimFpsQ;
uint32_t flags = (smooth ? AnimSmooth : 0) | (horizontal ? AnimHorizontal : 0); uint32_t flags = (smooth ? AnimSmooth : 0);
if(useGrid) flags |= AnimGrid;
else if(axisHorizontal) flags |= AnimHorizontal;
else if(!axisVertical) flags |= AnimGrid;
_emitOp(out, Op::Base_Anim); _emitOp(out, Op::Base_Anim);
_emitSrcTexName(out, absPatches, relPatches, tex); _emitSrcTexName(out, absPatches, relPatches, tex);
@@ -2059,7 +2115,9 @@ private:
uint32_t smooth = namedU("smooth").value_or(posU(4).value_or(0)); uint32_t smooth = namedU("smooth").value_or(posU(4).value_or(0));
std::string axis = namedS("axis").value_or(""); std::string axis = namedS("axis").value_or("");
bool horizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h')); bool axisHorizontal = (!axis.empty() && (axis[0] == 'x' || axis[0] == 'h'));
bool axisVertical = (!axis.empty() && (axis[0] == 'y' || axis[0] == 'v'));
bool useGrid = axis.empty() || axis[0] == 'g';
if(frameW > 65535u || frameH > 65535u || frames > 65535u) { if(frameW > 65535u || frameH > 65535u || frames > 65535u) {
if(err) *err="параметры anim должны помещаться в uint16"; if(err) *err="параметры anim должны помещаться в uint16";
@@ -2067,7 +2125,10 @@ private:
} }
uint32_t fpsQ = fps ? std::min<uint32_t>(0xFFFFu, fps * 256u) : DefaultAnimFpsQ; uint32_t fpsQ = fps ? std::min<uint32_t>(0xFFFFu, fps * 256u) : DefaultAnimFpsQ;
uint32_t flags = (smooth ? AnimSmooth : 0) | (horizontal ? AnimHorizontal : 0); uint32_t flags = (smooth ? AnimSmooth : 0);
if(useGrid) flags |= AnimGrid;
else if(axisHorizontal) flags |= AnimHorizontal;
else if(!axisVertical) flags |= AnimGrid;
_emitOp(out, Op::Anim); _emitOp(out, Op::Anim);
_emitU16(out, frameW); _emitU16(out, frameW);