The Omega Update(TM) Part 4 (Massive Refactor, Change All Frame Properties)
This commit is contained in:
16
README.md
16
README.md
@@ -5,14 +5,14 @@
|
||||
A reimplementation of *The Binding of Isaac: Rebirth*'s proprietary animation editor. Manipulates the XML-based ".anm2" format, used for in-game tweened animations.
|
||||
|
||||
## Features
|
||||
|
||||
- Most things present in the original IsaacAnimationEditor.exe, except some stuff like drawing (why not use an art program!)
|
||||
- Extended version of the original proprietary Nicalis animation editor
|
||||
- Smooth [Dear ImGui](https://github.com/ocornut/imgui) interface; docking, dragging and dropping, etc.
|
||||
- Keybinds/keyboard control for common actions(see [src/input.h](https://github.com/ShweetsStuff/anm2ed/blob/master/src/input.h))
|
||||
|
||||
### Known Issues
|
||||
- Root Transform doesn't work for scale/rotation (matrix math is hard; if you can help me fix it I will give you $100.)
|
||||
- Some .anm2 files used in Rebirth might not render correctly due to the ordering of layers; just drag and drop to fix the ordering and save, they will work fine afterwards.
|
||||
- New features
|
||||
- Can output .webm or *.png sequence
|
||||
- Cutting, copying and pasting
|
||||
- Robust snapshot (undo/redo) system
|
||||
- Additional hotkeys/shortcuts
|
||||
- Settings that will preserve on exit
|
||||
|
||||
## Dependencies
|
||||
Download these from your package manager:
|
||||
@@ -20,6 +20,8 @@ Download these from your package manager:
|
||||
- SDL3
|
||||
- GLEW
|
||||
|
||||
Note, to render animations, you'll need to download [FFmpeg](https://ffmpeg.org/download.html) and specify its install path in the program.
|
||||
|
||||
## Build
|
||||
|
||||
After cloning and enter the repository's directory, make sure to initialize the submodules:
|
||||
|
19
src/COMMON.h
19
src/COMMON.h
@@ -10,20 +10,20 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <print>
|
||||
#include <optional>
|
||||
#include <print>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
@@ -123,11 +123,12 @@ static inline bool string_to_bool(const std::string& string)
|
||||
return lower == "true";
|
||||
}
|
||||
|
||||
static inline void working_directory_from_file_set(const std::string& path)
|
||||
static inline std::string working_directory_from_file_set(const std::string& path)
|
||||
{
|
||||
std::filesystem::path filePath = path;
|
||||
std::filesystem::path parentPath = filePath.parent_path();
|
||||
std::filesystem::current_path(parentPath);
|
||||
return parentPath;
|
||||
};
|
||||
|
||||
static inline std::string path_extension_change(const std::string& path, const std::string& extension)
|
||||
@@ -178,6 +179,13 @@ static inline s32 string_to_enum(const std::string& string, const char* const* a
|
||||
return -1;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T& dummy_value()
|
||||
{
|
||||
static T value{};
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline s32 map_next_id_get(const std::map<s32, T>& map)
|
||||
{
|
||||
@@ -302,6 +310,7 @@ enum DataType
|
||||
TYPE_STRING,
|
||||
TYPE_IVEC2,
|
||||
TYPE_VEC2,
|
||||
TYPE_VEC3,
|
||||
TYPE_VEC4
|
||||
};
|
||||
|
||||
|
134
src/anm2.cpp
134
src/anm2.cpp
@@ -1,5 +1,3 @@
|
||||
// Anm2 file format; serializing/deserializing
|
||||
|
||||
#include "anm2.h"
|
||||
|
||||
using namespace tinyxml2;
|
||||
@@ -781,6 +779,7 @@ Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference)
|
||||
{
|
||||
Anm2Item* item = anm2_item_from_reference(self, reference);
|
||||
@@ -903,7 +902,7 @@ s32 anm2_animation_length_get(Anm2Animation* self)
|
||||
accumulate_max_delay(item.frames);
|
||||
|
||||
for (const auto& frame : self->triggers.frames)
|
||||
length = std::max(length, frame.atFrame);
|
||||
length = std::max(length, frame.atFrame + 1);
|
||||
|
||||
return length;
|
||||
}
|
||||
@@ -913,7 +912,7 @@ void anm2_animation_length_set(Anm2Animation* self)
|
||||
self->frameNum = anm2_animation_length_get(self);
|
||||
}
|
||||
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time)
|
||||
{
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self, reference);
|
||||
Anm2Item* item = anm2_item_from_reference(self, reference);
|
||||
@@ -923,46 +922,43 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
|
||||
|
||||
if (item)
|
||||
{
|
||||
Anm2Frame frame = Anm2Frame{};
|
||||
s32 index = INDEX_NONE;
|
||||
Anm2Frame frameAdd = frame ? *frame : Anm2Frame{};
|
||||
s32 index = reference->frameIndex + 1;
|
||||
|
||||
if (reference->itemType == ANM2_TRIGGERS)
|
||||
{
|
||||
for (auto& frameCheck : item->frames) if (frameCheck.atFrame == time) return nullptr;
|
||||
s32 index = time;
|
||||
|
||||
frame.atFrame = time;
|
||||
for (auto& frameCheck : item->frames)
|
||||
{
|
||||
if (frameCheck.atFrame == time)
|
||||
{
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
frameAdd.atFrame = index;
|
||||
index = item->frames.size();
|
||||
return &item->frames.emplace_back(frameAdd);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 frameDelayCount = 0;
|
||||
|
||||
for (auto& frameCheck : item->frames)
|
||||
frameDelayCount += frameCheck.delay;
|
||||
|
||||
if (frameDelayCount + ANM2_FRAME_DELAY_MIN > animation->frameNum) return nullptr;
|
||||
|
||||
Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference);
|
||||
|
||||
if (checkFrame)
|
||||
{
|
||||
if (frameDelayCount + checkFrame->delay > animation->frameNum)
|
||||
frame.delay = animation->frameNum - frameDelayCount;
|
||||
|
||||
index = reference->frameIndex + 1;
|
||||
}
|
||||
else
|
||||
index = (s32)item->frames.size();
|
||||
item->frames.insert(item->frames.begin() + index, frameAdd);
|
||||
return &item->frames[index];
|
||||
}
|
||||
|
||||
item->frames.insert(item->frames.begin() + index, frame);
|
||||
|
||||
return &item->frames[index];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void anm2_frame_erase(Anm2* self, Anm2Reference* reference)
|
||||
{
|
||||
Anm2Item* item = anm2_item_from_reference(self, reference);
|
||||
if (!item) return;
|
||||
item->frames.erase(item->frames.begin() + reference->frameIndex);
|
||||
}
|
||||
|
||||
void anm2_reference_clear(Anm2Reference* self)
|
||||
{
|
||||
*self = Anm2Reference{};
|
||||
@@ -970,9 +966,7 @@ void anm2_reference_clear(Anm2Reference* self)
|
||||
|
||||
void anm2_reference_item_clear(Anm2Reference* self)
|
||||
{
|
||||
self->itemType = ANM2_NONE;
|
||||
self->itemID = ID_NONE;
|
||||
self->frameIndex = INDEX_NONE;
|
||||
*self = {self->animationID};
|
||||
}
|
||||
|
||||
void anm2_reference_frame_clear(Anm2Reference* self)
|
||||
@@ -980,20 +974,70 @@ void anm2_reference_frame_clear(Anm2Reference* self)
|
||||
self->frameIndex = INDEX_NONE;
|
||||
}
|
||||
|
||||
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count)
|
||||
{
|
||||
Anm2Item* item = anm2_item_from_reference(self, reference);
|
||||
if (!item) return;
|
||||
|
||||
if (start < 0 || count <= 0) return;
|
||||
const s32 size = (s32)item->frames.size();
|
||||
if (size == 0 || start >= size) return;
|
||||
|
||||
const s32 end = std::min(start + count, size);
|
||||
|
||||
for (s32 i = start; i < end; ++i)
|
||||
{
|
||||
Anm2Frame& dest = item->frames[i];
|
||||
|
||||
// Booleans always just set if provided
|
||||
if (change.isVisible) dest.isVisible = *change.isVisible;
|
||||
if (change.isInterpolated) dest.isInterpolated = *change.isInterpolated;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ANM2_CHANGE_SET:
|
||||
if (change.rotation) dest.rotation = *change.rotation;
|
||||
if (change.delay) dest.delay = std::max(ANM2_FRAME_DELAY_MIN, *change.delay);
|
||||
if (change.crop) dest.crop = *change.crop;
|
||||
if (change.pivot) dest.pivot = *change.pivot;
|
||||
if (change.position) dest.position = *change.position;
|
||||
if (change.size) dest.size = *change.size;
|
||||
if (change.scale) dest.scale = *change.scale;
|
||||
if (change.offsetRGB) dest.offsetRGB = glm::clamp(*change.offsetRGB, 0.0f, 1.0f);
|
||||
if (change.tintRGBA) dest.tintRGBA = glm::clamp(*change.tintRGBA, 0.0f, 1.0f);
|
||||
break;
|
||||
|
||||
case ANM2_CHANGE_ADD:
|
||||
if (change.rotation) dest.rotation += *change.rotation;
|
||||
if (change.delay) dest.delay = std::max(ANM2_FRAME_DELAY_MIN, dest.delay + *change.delay);
|
||||
if (change.crop) dest.crop += *change.crop;
|
||||
if (change.pivot) dest.pivot += *change.pivot;
|
||||
if (change.position) dest.position += *change.position;
|
||||
if (change.size) dest.size += *change.size;
|
||||
if (change.scale) dest.scale += *change.scale;
|
||||
if (change.offsetRGB) dest.offsetRGB = glm::clamp(dest.offsetRGB + *change.offsetRGB, 0.0f, 1.0f);
|
||||
if (change.tintRGBA) dest.tintRGBA = glm::clamp(dest.tintRGBA + *change.tintRGBA, 0.0f, 1.0f);
|
||||
break;
|
||||
|
||||
case ANM2_CHANGE_SUBTRACT:
|
||||
if (change.rotation) dest.rotation -= *change.rotation;
|
||||
if (change.delay) dest.delay = std::max(ANM2_FRAME_DELAY_MIN, dest.delay - *change.delay);
|
||||
if (change.crop) dest.crop -= *change.crop;
|
||||
if (change.pivot) dest.pivot -= *change.pivot;
|
||||
if (change.position) dest.position -= *change.position;
|
||||
if (change.size) dest.size -= *change.size;
|
||||
if (change.scale) dest.scale -= *change.scale;
|
||||
if (change.offsetRGB) dest.offsetRGB = glm::clamp(dest.offsetRGB - *change.offsetRGB, 0.0f, 1.0f);
|
||||
if (change.tintRGBA) dest.tintRGBA = glm::clamp(dest.tintRGBA - *change.tintRGBA, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type)
|
||||
{
|
||||
Anm2Animation newAnimation = self->animations[animationID];
|
||||
|
||||
newAnimation.rootAnimation.frames.clear();
|
||||
|
||||
for (auto& [id, layerAnimation] : newAnimation.layerAnimations)
|
||||
layerAnimation.frames.clear();
|
||||
|
||||
for (auto& [id, nullAnimation] : newAnimation.nullAnimations)
|
||||
nullAnimation.frames.clear();
|
||||
|
||||
newAnimation.triggers.frames.clear();
|
||||
|
||||
auto merge_item = [&](Anm2Item& destinationItem, const Anm2Item& sourceItem)
|
||||
{
|
||||
switch (type)
|
||||
@@ -1017,6 +1061,8 @@ void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& m
|
||||
|
||||
for (auto mergeID : mergeIDs)
|
||||
{
|
||||
if (animationID == mergeID) continue;
|
||||
|
||||
const Anm2Animation& mergeAnimation = self->animations[mergeID];
|
||||
|
||||
merge_item(newAnimation.rootAnimation, mergeAnimation.rootAnimation);
|
||||
@@ -1047,7 +1093,7 @@ void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool is
|
||||
referenceNext.frameIndex = reference->frameIndex + 1;
|
||||
|
||||
Anm2Frame* frameNext = anm2_frame_from_reference(self, &referenceNext);
|
||||
if (!frameNext) return;
|
||||
if (!frameNext) frameNext = frame;
|
||||
|
||||
const Anm2Frame baseFrame = *frame;
|
||||
const Anm2Frame baseFrameNext = *frameNext;
|
||||
|
31
src/anm2.h
31
src/anm2.h
@@ -7,6 +7,7 @@
|
||||
#define ANM2_TINT_CONVERT(x) ((f32)x / 255.0f)
|
||||
|
||||
#define ANM2_FPS_MIN 0
|
||||
#define ANM2_FPS_DEFAULT 30
|
||||
#define ANM2_FPS_MAX 120
|
||||
#define ANM2_FRAME_NUM_MIN 1
|
||||
#define ANM2_FRAME_NUM_MAX 1000000
|
||||
@@ -148,8 +149,8 @@ struct Anm2Event
|
||||
|
||||
struct Anm2Frame
|
||||
{
|
||||
bool isInterpolated = false;
|
||||
bool isVisible = true;
|
||||
bool isInterpolated = false;
|
||||
f32 rotation = 1.0f;
|
||||
s32 delay = ANM2_FRAME_DELAY_MIN;
|
||||
s32 atFrame = INDEX_NONE;
|
||||
@@ -163,6 +164,21 @@ struct Anm2Frame
|
||||
vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
};
|
||||
|
||||
struct Anm2FrameChange
|
||||
{
|
||||
std::optional<bool> isVisible;
|
||||
std::optional<bool> isInterpolated;
|
||||
std::optional<f32> rotation;
|
||||
std::optional<s32> delay;
|
||||
std::optional<vec2> crop;
|
||||
std::optional<vec2> pivot;
|
||||
std::optional<vec2> position;
|
||||
std::optional<vec2> size;
|
||||
std::optional<vec2> scale;
|
||||
std::optional<vec3> offsetRGB;
|
||||
std::optional<vec4> tintRGBA;
|
||||
};
|
||||
|
||||
struct Anm2Item
|
||||
{
|
||||
bool isVisible = true;
|
||||
@@ -192,7 +208,7 @@ struct Anm2
|
||||
std::map<s32, Anm2Animation> animations;
|
||||
std::map<s32, s32> layerMap; // index, id
|
||||
s32 defaultAnimationID{};
|
||||
s32 fps = 30;
|
||||
s32 fps = ANM2_FPS_DEFAULT;
|
||||
s32 version{};
|
||||
};
|
||||
|
||||
@@ -231,6 +247,13 @@ enum Anm2MergeType
|
||||
ANM2_MERGE_IGNORE
|
||||
};
|
||||
|
||||
enum Anm2ChangeType
|
||||
{
|
||||
ANM2_CHANGE_ADD,
|
||||
ANM2_CHANGE_SUBTRACT,
|
||||
ANM2_CHANGE_SET
|
||||
};
|
||||
|
||||
void anm2_layer_add(Anm2* self);
|
||||
void anm2_layer_remove(Anm2* self, s32 id);
|
||||
void anm2_null_add(Anm2* self);
|
||||
@@ -246,7 +269,8 @@ Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* referenc
|
||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
|
||||
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time);
|
||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time);
|
||||
void anm2_frame_erase(Anm2* self, Anm2Reference* reference);
|
||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
|
||||
void anm2_reference_clear(Anm2Reference* self);
|
||||
void anm2_reference_item_clear(Anm2Reference* self);
|
||||
@@ -255,3 +279,4 @@ s32 anm2_animation_length_get(Anm2Animation* self);
|
||||
void anm2_animation_length_set(Anm2Animation* self);
|
||||
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
|
||||
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);
|
||||
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
|
@@ -1,5 +1,3 @@
|
||||
// Common rendering area
|
||||
|
||||
#include "canvas.h"
|
||||
|
||||
static void _canvas_texture_free(Canvas* self)
|
||||
@@ -9,7 +7,7 @@ static void _canvas_texture_free(Canvas* self)
|
||||
if (self->texture != 0) glDeleteTextures(1, &self->texture);
|
||||
}
|
||||
|
||||
static void _canvas_texture_init(Canvas* self, vec2& size)
|
||||
static void _canvas_texture_init(Canvas* self, const vec2& size)
|
||||
{
|
||||
_canvas_texture_free(self);
|
||||
|
||||
@@ -35,7 +33,7 @@ static void _canvas_texture_init(Canvas* self, vec2& size)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void canvas_init(Canvas* self)
|
||||
void canvas_init(Canvas* self, const vec2& size)
|
||||
{
|
||||
// Axis
|
||||
glGenVertexArrays(1, &self->axisVAO);
|
||||
@@ -91,6 +89,8 @@ void canvas_init(Canvas* self)
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(f32), (void*)(2 * sizeof(f32)));
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
_canvas_texture_init(self, size);
|
||||
}
|
||||
|
||||
mat4 canvas_transform_get(Canvas* self, vec2& pan, f32& zoom, OriginType origin)
|
||||
@@ -278,29 +278,3 @@ mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 r
|
||||
|
||||
return transform * model;
|
||||
}
|
||||
|
||||
/*
|
||||
mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale, vec2 pivotAlt, f32 rotationAlt)
|
||||
{
|
||||
vec2 scaleAbsolute = abs(scale);
|
||||
vec2 signScale = glm::sign(scale);
|
||||
vec2 pivotScaled = pivot * scaleAbsolute;
|
||||
vec2 pivotAltScaled = pivotAlt * scaleAbsolute;
|
||||
vec2 sizeScaled = size * scaleAbsolute;
|
||||
|
||||
mat4 model = glm::translate(mat4(1.0f), vec3(position - pivotScaled, 0.0f));
|
||||
|
||||
model = glm::translate(model, vec3(pivotScaled, 0.0f));
|
||||
model = glm::scale(model, vec3(signScale, 1.0f)); // Flip
|
||||
model = glm::rotate(model, radians(rotation), vec3(0,0,1));
|
||||
model = glm::translate(model, vec3(-pivotScaled, 0.0f));
|
||||
|
||||
model = glm::translate(model, vec3(pivotAltScaled, 0.0f));
|
||||
model = glm::rotate(model, radians(rotationAlt), vec3(0,0,1));
|
||||
model = glm::translate(model, vec3(-pivotAltScaled, 0.0f));
|
||||
|
||||
model = glm::scale(model, vec3(sizeScaled, 1.0f));
|
||||
|
||||
return transform * model;
|
||||
}
|
||||
*/
|
@@ -4,14 +4,16 @@
|
||||
|
||||
#define CANVAS_ZOOM_MIN 1.0f
|
||||
#define CANVAS_ZOOM_MAX 2000.0f
|
||||
#define CANVAS_ZOOM_DEFAULT 100.0f
|
||||
#define CANVAS_ZOOM_STEP 10.0f
|
||||
#define CANVAS_ZOOM_MOD 10.0f
|
||||
#define CANVAS_GRID_MIN 1
|
||||
#define CANVAS_GRID_MAX 1000
|
||||
#define CANVAS_GRID_DEFAULT 32
|
||||
#define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f)
|
||||
|
||||
static const vec2 CANVAS_GRID_SIZE = {3200, 1600};
|
||||
static const vec2 CANVAS_PIVOT_SIZE = {4, 4};
|
||||
static const vec2 CANVAS_PIVOT_SIZE = {8, 8};
|
||||
|
||||
const f32 CANVAS_AXIS_VERTICES[] =
|
||||
{
|
||||
@@ -38,7 +40,7 @@ struct Canvas
|
||||
vec2 size{};
|
||||
};
|
||||
|
||||
void canvas_init(Canvas* self);
|
||||
void canvas_init(Canvas* self, const vec2& size);
|
||||
mat4 canvas_transform_get(Canvas* self, vec2& pan, f32& zoom, OriginType origin);
|
||||
void canvas_clear(vec4& color);
|
||||
void canvas_bind(Canvas* self);
|
||||
|
@@ -6,44 +6,26 @@ static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
|
||||
{
|
||||
case CLIPBOARD_FRAME:
|
||||
{
|
||||
Anm2FrameWithReference frameWithReference = std::get<Anm2FrameWithReference>(self->data);
|
||||
Anm2Animation* animation = anm2_animation_from_reference(anm2, &frameWithReference.reference);
|
||||
Anm2FrameWithReference* frameWithReference = std::get_if<Anm2FrameWithReference>(&self->data);
|
||||
|
||||
if (!animation) break;
|
||||
|
||||
std::vector<Anm2Frame>* frames = nullptr;
|
||||
|
||||
switch (frameWithReference.reference.itemType)
|
||||
{
|
||||
case ANM2_ROOT:
|
||||
frames = &animation->rootAnimation.frames;
|
||||
break;
|
||||
case ANM2_LAYER:
|
||||
frames = &animation->layerAnimations[frameWithReference.reference.itemID].frames;
|
||||
break;
|
||||
case ANM2_NULL:
|
||||
frames = &animation->nullAnimations[frameWithReference.reference.itemID].frames;
|
||||
break;
|
||||
case ANM2_TRIGGERS:
|
||||
frames = &animation->triggers.frames;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (frames)
|
||||
frames->erase(frames->begin() + frameWithReference.reference.frameIndex);
|
||||
if (!frameWithReference) break;
|
||||
|
||||
anm2_frame_erase(anm2, &frameWithReference->reference);
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
Anm2AnimationWithID animationWithID = std::get<Anm2AnimationWithID>(self->data);
|
||||
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
|
||||
|
||||
if (!animationWithID) break;
|
||||
|
||||
for (auto & [id, animation] : anm2->animations)
|
||||
{
|
||||
if (id == animationWithID.id)
|
||||
anm2->animations.erase(animationWithID.id);
|
||||
if (id == animationWithID->id)
|
||||
{
|
||||
anm2->animations.erase(animationWithID->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -69,24 +51,26 @@ static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* locati
|
||||
|
||||
if (!animation || !anm2Item) break;
|
||||
|
||||
s32 insertIndex = (reference->itemType == ANM2_TRIGGERS) ?
|
||||
reference->frameIndex : std::max(reference->frameIndex, (s32)anm2Item->frames.size());
|
||||
anm2_frame_add(anm2, &frameWithReference->frame, reference, reference->frameIndex);
|
||||
|
||||
anm2Item->frames.insert(anm2Item->frames.begin() + insertIndex, frameWithReference->frame);
|
||||
|
||||
anm2_animation_length_set(animation);
|
||||
break;
|
||||
}
|
||||
case CLIPBOARD_ANIMATION:
|
||||
{
|
||||
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
|
||||
|
||||
if (!animationWithID) break;
|
||||
|
||||
s32 index = 0;
|
||||
|
||||
if (std::holds_alternative<s32>(*location))
|
||||
index = std::get<s32>(*location);
|
||||
else
|
||||
break;
|
||||
|
||||
index = std::clamp(index, 0, (s32)anm2->animations.size());
|
||||
|
||||
map_insert_shift(anm2->animations, index, std::get<Anm2AnimationWithID>(self->data).animation);
|
||||
map_insert_shift(anm2->animations, index, animationWithID->animation);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@@ -15,12 +15,8 @@ struct ClipboardItem
|
||||
ClipboardItemType type = CLIPBOARD_NONE;
|
||||
|
||||
ClipboardItem() = default;
|
||||
|
||||
ClipboardItem(const Anm2FrameWithReference& frame)
|
||||
: data(frame), type(CLIPBOARD_FRAME) {}
|
||||
|
||||
ClipboardItem(const Anm2AnimationWithID& anim)
|
||||
: data(anim), type(CLIPBOARD_ANIMATION) {}
|
||||
ClipboardItem(const Anm2FrameWithReference& frame) : data(frame), type(CLIPBOARD_FRAME) {}
|
||||
ClipboardItem(const Anm2AnimationWithID& animation) : data(animation), type(CLIPBOARD_ANIMATION) {}
|
||||
};
|
||||
|
||||
using ClipboardLocation = std::variant<std::monostate, Anm2Reference, s32>;
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// File dialog; saving/writing to files
|
||||
|
||||
#include "dialog.h"
|
||||
|
||||
static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter)
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// Handles the rendering of the spritesheet editor
|
||||
|
||||
#include "editor.h"
|
||||
|
||||
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
|
||||
@@ -9,7 +7,7 @@ void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources*
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
|
||||
canvas_init(&self->canvas);
|
||||
canvas_init(&self->canvas, vec2());
|
||||
}
|
||||
|
||||
void editor_draw(Editor* self)
|
||||
|
101
src/generate_preview.cpp
Normal file
101
src/generate_preview.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "generate_preview.h"
|
||||
|
||||
void generate_preview_init(GeneratePreview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
|
||||
{
|
||||
self->anm2 = anm2;
|
||||
self->reference = reference;
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
|
||||
canvas_init(&self->canvas, GENERATE_PREVIEW_SIZE);
|
||||
}
|
||||
|
||||
void generate_preview_draw(GeneratePreview* self)
|
||||
{
|
||||
|
||||
/* TODO
|
||||
f32& zoom = self->settings->previewZoom;
|
||||
GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
|
||||
GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
|
||||
mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
|
||||
|
||||
canvas_bind(&self->canvas);
|
||||
canvas_viewport_set(&self->canvas);
|
||||
canvas_clear(self->settings->previewBackgroundColor);
|
||||
|
||||
self->time = std::clamp(self->time, 0.0f, 1.0f);
|
||||
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
|
||||
s32& animationID = self->reference->animationID;
|
||||
|
||||
if (animation)
|
||||
{
|
||||
Anm2Frame root;
|
||||
mat4 rootModel = mat4(1.0f);
|
||||
|
||||
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
|
||||
|
||||
if (self->settings->previewIsRootTransform)
|
||||
rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
|
||||
|
||||
// Root
|
||||
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
|
||||
{
|
||||
mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, root.rotation, PERCENT_TO_UNIT(root.scale));
|
||||
mat4 rootTransform = transform * model;
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
|
||||
}
|
||||
|
||||
// Layers
|
||||
for (auto [i, id] : self->anm2->layerMap)
|
||||
{
|
||||
Anm2Frame frame;
|
||||
Anm2Item& layerAnimation = animation->layerAnimations[id];
|
||||
|
||||
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
|
||||
continue;
|
||||
|
||||
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, self->time);
|
||||
|
||||
if (!frame.isVisible)
|
||||
continue;
|
||||
|
||||
Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
|
||||
|
||||
if (!texture || texture->isInvalid)
|
||||
continue;
|
||||
|
||||
vec2 uvMin = frame.crop / vec2(texture->size);
|
||||
vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
|
||||
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
|
||||
|
||||
mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
mat4 layerTransform = transform * (rootModel * model);
|
||||
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
|
||||
|
||||
if (self->settings->previewIsBorder)
|
||||
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
|
||||
|
||||
if (self->settings->previewIsPivots)
|
||||
{
|
||||
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
|
||||
mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
|
||||
mat4 pivotTransform = transform * (rootModel * pivotModel);
|
||||
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s32& animationOverlayID = self->animationOverlayID;
|
||||
Anm2Animation* animationOverlay = map_find(self->anm2->animations, animationOverlayID);
|
||||
|
||||
canvas_unbind();
|
||||
*/
|
||||
}
|
||||
|
||||
void generate_preview_free(GeneratePreview* self)
|
||||
{
|
||||
canvas_free(&self->canvas);
|
||||
}
|
22
src/generate_preview.h
Normal file
22
src/generate_preview.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "anm2.h"
|
||||
#include "resources.h"
|
||||
#include "settings.h"
|
||||
#include "canvas.h"
|
||||
|
||||
const vec2 GENERATE_PREVIEW_SIZE = {325, 215};
|
||||
|
||||
struct GeneratePreview
|
||||
{
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2Reference* reference = nullptr;
|
||||
Resources* resources = nullptr;
|
||||
Settings* settings = nullptr;
|
||||
Canvas canvas;
|
||||
f32 time{};
|
||||
};
|
||||
|
||||
void generate_preview_init(GeneratePreview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
|
||||
void generate_preview_draw(GeneratePreview* self);
|
||||
void generate_preview_free(GeneratePreview* self);
|
2521
src/imgui.cpp
2521
src/imgui.cpp
File diff suppressed because it is too large
Load Diff
3268
src/imgui.h
3268
src/imgui.h
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,3 @@
|
||||
// Main function
|
||||
|
||||
#include "main.h"
|
||||
|
||||
s32
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// Handles the render of the animation preview
|
||||
|
||||
#include "preview.h"
|
||||
|
||||
static void _preview_render_textures_free(Preview* self)
|
||||
@@ -17,11 +15,12 @@ void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources
|
||||
self->resources = resources;
|
||||
self->settings = settings;
|
||||
|
||||
canvas_init(&self->canvas);
|
||||
canvas_init(&self->canvas, vec2());
|
||||
}
|
||||
|
||||
void preview_tick(Preview* self)
|
||||
{
|
||||
f32& time = self->time;
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
|
||||
|
||||
if (animation)
|
||||
@@ -45,29 +44,38 @@ void preview_tick(Preview* self)
|
||||
self->renderFrames.push_back(frameTexture);
|
||||
}
|
||||
|
||||
self->time += (f32)self->anm2->fps / TICK_RATE;
|
||||
time += (f32)self->anm2->fps / TICK_RATE;
|
||||
|
||||
if (self->time >= (f32)animation->frameNum - 1)
|
||||
if (time >= (f32)animation->frameNum - 1)
|
||||
{
|
||||
if (self->isRender)
|
||||
{
|
||||
self->isRender = false;
|
||||
self->isRenderFinished = true;
|
||||
self->time = 0.0f;
|
||||
time = 0.0f;
|
||||
self->isPlaying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->settings->playbackIsLoop)
|
||||
self->time = 0.0f;
|
||||
time = 0.0f;
|
||||
else
|
||||
{
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (f32)animation->frameNum - 1));
|
||||
self->isPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self->time = std::clamp(self->time, 0.0f, (f32)animation->frameNum - 1);
|
||||
if (self->settings->playbackIsClampPlayhead)
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (f32)animation->frameNum - 1));
|
||||
else
|
||||
time = std::max(time, 0.0f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void preview_draw(Preview* self)
|
||||
@@ -89,8 +97,8 @@ void preview_draw(Preview* self)
|
||||
if (self->settings->previewIsGrid)
|
||||
canvas_grid_draw(&self->canvas, shaderLine, transform, zoom, gridSize, gridOffset, gridColor);
|
||||
|
||||
if (self->settings->previewIsAxis)
|
||||
canvas_axes_draw(&self->canvas, shaderLine, transform, self->settings->previewAxisColor);
|
||||
if (self->settings->previewIsAxes)
|
||||
canvas_axes_draw(&self->canvas, shaderLine, transform, self->settings->previewAxesColor);
|
||||
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
|
||||
s32& animationID = self->reference->animationID;
|
||||
|
@@ -54,6 +54,13 @@ static void _settings_setting_load(Settings* self, const std::string& line)
|
||||
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
|
||||
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
|
||||
}
|
||||
else if (entry.type == TYPE_VEC3)
|
||||
{
|
||||
vec3* v = (vec3*)target;
|
||||
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
|
||||
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
|
||||
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
|
||||
}
|
||||
else if (entry.type == TYPE_VEC4)
|
||||
{
|
||||
vec4* v = (vec4*)target;
|
||||
@@ -105,6 +112,14 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
|
||||
out << entry.key << "Y=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC3:
|
||||
{
|
||||
vec3* data = (vec3*)(selfPointer + entry.offset);
|
||||
out << entry.key << "R=" << std::format(SETTINGS_FLOAT_FORMAT, data->r) << "\n";
|
||||
out << entry.key << "G=" << std::format(SETTINGS_FLOAT_FORMAT, data->g) << "\n";
|
||||
out << entry.key << "B=" << std::format(SETTINGS_FLOAT_FORMAT, data->b) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC4:
|
||||
{
|
||||
vec4* data = (vec4*)(selfPointer + entry.offset);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "anm2.h"
|
||||
#include "render.h"
|
||||
#include "tool.h"
|
||||
|
||||
@@ -22,20 +23,53 @@ struct Settings
|
||||
{
|
||||
ivec2 windowSize = {1080, 720};
|
||||
bool playbackIsLoop = true;
|
||||
bool previewIsAxis = true;
|
||||
bool playbackIsClampPlayhead = true;
|
||||
bool changeIsCrop = false;
|
||||
bool changeIsSize = false;
|
||||
bool changeIsPosition = false;
|
||||
bool changeIsPivot = false;
|
||||
bool changeIsScale = false;
|
||||
bool changeIsRotation = false;
|
||||
bool changeIsDelay = false;
|
||||
bool changeIsTint = false;
|
||||
bool changeIsColorOffset = false;
|
||||
bool changeIsVisibleSet = false;
|
||||
bool changeIsInterpolatedSet = false;
|
||||
bool changeIsFromSelectedFrame = false;
|
||||
vec2 changeCrop{};
|
||||
vec2 changeSize{};
|
||||
vec2 changePosition{};
|
||||
vec2 changePivot{};
|
||||
vec2 changeScale{};
|
||||
f32 changeRotation{};
|
||||
s32 changeDelay{};
|
||||
vec4 changeTint{};
|
||||
vec3 changeColorOffset{};
|
||||
bool changeIsVisible{};
|
||||
bool changeIsInterpolated{};
|
||||
s32 changeNumberFrames = 1;
|
||||
bool previewIsAxes = true;
|
||||
bool previewIsGrid = true;
|
||||
bool previewIsRootTransform = false;
|
||||
bool previewIsTriggers = true;
|
||||
bool previewIsPivots = false;
|
||||
bool previewIsTargets = true;
|
||||
bool previewIsBorder = false;
|
||||
f32 previewOverlayTransparency = 255.0f;
|
||||
f32 previewZoom = 200.0;
|
||||
vec2 previewPan = {0.0, 0.0};
|
||||
ivec2 previewGridSize = {32, 32};
|
||||
ivec2 previewGridSize = {32, 3};
|
||||
ivec2 previewGridOffset{};
|
||||
vec4 previewGridColor = {1.0, 1.0, 1.0, 0.125};
|
||||
vec4 previewAxisColor = {1.0, 1.0, 1.0, 0.125};
|
||||
vec4 previewAxesColor = {1.0, 1.0, 1.0, 0.125};
|
||||
vec4 previewBackgroundColor = {0.113, 0.184, 0.286, 1.0};
|
||||
ivec2 generateStartPosition = {0, 0};
|
||||
ivec2 generateFrameSize = {64, 64};
|
||||
ivec2 generatePivot = {32, 32};
|
||||
s32 generateRows = 4;
|
||||
s32 generateColumns = 4;
|
||||
s32 generateFrameCount = 16;
|
||||
s32 generateDelay = 1;
|
||||
bool editorIsGrid = true;
|
||||
bool editorIsGridSnap = true;
|
||||
bool editorIsBorder = true;
|
||||
@@ -45,9 +79,14 @@ struct Settings
|
||||
ivec2 editorGridOffset = {32, 32};
|
||||
vec4 editorGridColor = {1.0, 1.0, 1.0, 0.125};
|
||||
vec4 editorBackgroundColor = {0.113, 0.184, 0.286, 1.0};
|
||||
ToolType tool = TOOL_PAN;
|
||||
s32 mergeType = ANM2_MERGE_APPEND_FRAMES;
|
||||
bool mergeIsDeleteAnimationsAfter = false;
|
||||
s32 bakeInterval = 1;
|
||||
bool bakeIsRoundScale = true;
|
||||
bool bakeIsRoundRotation = true;
|
||||
s32 tool = TOOL_PAN;
|
||||
vec4 toolColor = {1.0, 1.0, 1.0, 1.0};
|
||||
RenderType renderType = RENDER_PNG;
|
||||
s32 renderType = RENDER_PNG;
|
||||
std::string renderPath = ".";
|
||||
std::string renderFormat = "{}.png";
|
||||
std::string ffmpegPath = "/usr/bin/ffmpeg";
|
||||
@@ -57,9 +96,35 @@ const SettingsEntry SETTINGS_ENTRIES[] =
|
||||
{
|
||||
{"window", TYPE_IVEC2, offsetof(Settings, windowSize)},
|
||||
{"playbackIsLoop", TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
|
||||
{"previewIsAxis", TYPE_BOOL, offsetof(Settings, previewIsAxis)},
|
||||
{"playbackIsClampPlayhead", TYPE_BOOL, offsetof(Settings, playbackIsClampPlayhead)},
|
||||
{"changeIsCrop", TYPE_BOOL, offsetof(Settings, changeIsCrop)},
|
||||
{"changeIsSize", TYPE_BOOL, offsetof(Settings, changeIsSize)},
|
||||
{"changeIsPosition", TYPE_BOOL, offsetof(Settings, changeIsPosition)},
|
||||
{"changeIsPivot", TYPE_BOOL, offsetof(Settings, changeIsPivot)},
|
||||
{"changeIsScale", TYPE_BOOL, offsetof(Settings, changeIsScale)},
|
||||
{"changeIsRotation", TYPE_BOOL, offsetof(Settings, changeIsRotation)},
|
||||
{"changeIsDelay", TYPE_BOOL, offsetof(Settings, changeIsDelay)},
|
||||
{"changeIsTint", TYPE_BOOL, offsetof(Settings, changeIsTint)},
|
||||
{"changeIsColorOffset", TYPE_BOOL, offsetof(Settings, changeIsColorOffset)},
|
||||
{"changeIsVisibleSet", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
|
||||
{"changeIsInterpolatedSet", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
|
||||
{"changeIsFromSelectedFrame", TYPE_BOOL, offsetof(Settings, changeIsFromSelectedFrame)},
|
||||
{"changeCrop", TYPE_VEC2, offsetof(Settings, changeCrop)},
|
||||
{"changeSize", TYPE_VEC2, offsetof(Settings, changeSize)},
|
||||
{"changePosition", TYPE_VEC2, offsetof(Settings, changePosition)},
|
||||
{"changePivot", TYPE_VEC2, offsetof(Settings, changePivot)},
|
||||
{"changeScale", TYPE_VEC2, offsetof(Settings, changeScale)},
|
||||
{"changeRotation", TYPE_FLOAT, offsetof(Settings, changeRotation)},
|
||||
{"changeDelay", TYPE_INT, offsetof(Settings, changeDelay)},
|
||||
{"changeTint", TYPE_VEC4, offsetof(Settings, changeTint)},
|
||||
{"changeColorOffset", TYPE_VEC3, offsetof(Settings, changeColorOffset)},
|
||||
{"changeIsVisible", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
|
||||
{"changeIsInterpolated", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
|
||||
{"changeNumberFrames", TYPE_INT, offsetof(Settings, changeNumberFrames)},
|
||||
{"previewIsAxes", TYPE_BOOL, offsetof(Settings, previewIsAxes)},
|
||||
{"previewIsGrid", TYPE_BOOL, offsetof(Settings, previewIsGrid)},
|
||||
{"previewIsRootTransform", TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
|
||||
{"previewIsTriggers", TYPE_BOOL, offsetof(Settings, previewIsTriggers)},
|
||||
{"previewIsPivots", TYPE_BOOL, offsetof(Settings, previewIsPivots)},
|
||||
{"previewIsTargets", TYPE_BOOL, offsetof(Settings, previewIsTargets)},
|
||||
{"previewIsBorder", TYPE_BOOL, offsetof(Settings, previewIsBorder)},
|
||||
@@ -69,8 +134,15 @@ const SettingsEntry SETTINGS_ENTRIES[] =
|
||||
{"previewGridSize", TYPE_IVEC2, offsetof(Settings, previewGridSize)},
|
||||
{"previewGridOffset", TYPE_IVEC2, offsetof(Settings, previewGridOffset)},
|
||||
{"previewGridColor", TYPE_VEC4, offsetof(Settings, previewGridColor)},
|
||||
{"previewAxisColor", TYPE_VEC4, offsetof(Settings, previewAxisColor)},
|
||||
{"previewAxesColor", TYPE_VEC4, offsetof(Settings, previewAxesColor)},
|
||||
{"previewBackgroundColor", TYPE_VEC4, offsetof(Settings, previewBackgroundColor)},
|
||||
{"generateStartPosition", TYPE_VEC2, offsetof(Settings, generateStartPosition)},
|
||||
{"generateFrameSize", TYPE_VEC2, offsetof(Settings, generateFrameSize)},
|
||||
{"generatePivot", TYPE_VEC2, offsetof(Settings, generatePivot)},
|
||||
{"generateRows", TYPE_INT, offsetof(Settings, generateRows)},
|
||||
{"generateColumns", TYPE_INT, offsetof(Settings, generateColumns)},
|
||||
{"generateFrameCount", TYPE_INT, offsetof(Settings, generateFrameCount)},
|
||||
{"generateDelay", TYPE_INT, offsetof(Settings, generateDelay)},
|
||||
{"editorIsGrid", TYPE_BOOL, offsetof(Settings, editorIsGrid)},
|
||||
{"editorIsGridSnap", TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
|
||||
{"editorIsBorder", TYPE_BOOL, offsetof(Settings, editorIsBorder)},
|
||||
@@ -80,6 +152,11 @@ const SettingsEntry SETTINGS_ENTRIES[] =
|
||||
{"editorGridOffset", TYPE_IVEC2, offsetof(Settings, editorGridOffset)},
|
||||
{"editorGridColor", TYPE_VEC4, offsetof(Settings, editorGridColor)},
|
||||
{"editorBackgroundColor", TYPE_VEC4, offsetof(Settings, editorBackgroundColor)},
|
||||
{"mergeType", TYPE_INT, offsetof(Settings, mergeType)},
|
||||
{"mergeIsDeleteAnimationsAfter", TYPE_BOOL, offsetof(Settings, mergeIsDeleteAnimationsAfter)},
|
||||
{"bakeInterval", TYPE_INT, offsetof(Settings, bakeInterval)},
|
||||
{"bakeRoundScale", TYPE_BOOL, offsetof(Settings, bakeIsRoundScale)},
|
||||
{"bakeRoundRotation", TYPE_BOOL, offsetof(Settings, bakeIsRoundRotation)},
|
||||
{"tool", TYPE_INT, offsetof(Settings, tool)},
|
||||
{"toolColor", TYPE_VEC4, offsetof(Settings, toolColor)},
|
||||
{"renderType", TYPE_INT, offsetof(Settings, renderType)},
|
||||
|
@@ -41,7 +41,7 @@ void snapshots_reset(Snapshots* self)
|
||||
self->action.clear();
|
||||
}
|
||||
|
||||
void snapshots_undo_stack_push(Snapshots* self, const Snapshot* snapshot)
|
||||
void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot)
|
||||
{
|
||||
_snapshot_stack_push(&self->undoStack, snapshot);
|
||||
self->redoStack.top = 0;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "anm2.h"
|
||||
#include "preview.h"
|
||||
#include "texture.h"
|
||||
|
||||
#define SNAPSHOT_STACK_MAX 1000
|
||||
#define SNAPSHOT_ACTION "Action"
|
||||
@@ -18,6 +19,8 @@ struct SnapshotStack
|
||||
{
|
||||
Snapshot snapshots[SNAPSHOT_STACK_MAX];
|
||||
s32 top = 0;
|
||||
|
||||
bool is_empty() const { return top == 0; }
|
||||
};
|
||||
|
||||
struct Snapshots
|
||||
@@ -30,7 +33,7 @@ struct Snapshots
|
||||
SnapshotStack redoStack;
|
||||
};
|
||||
|
||||
void snapshots_undo_stack_push(Snapshots* self, const Snapshot* snapshot);
|
||||
void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot);
|
||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview);
|
||||
void snapshots_undo(Snapshots* self);
|
||||
void snapshots_redo(Snapshots* self);
|
||||
|
@@ -17,8 +17,6 @@ static void _update(State* self)
|
||||
|
||||
static void _draw(State* self)
|
||||
{
|
||||
editor_draw(&self->editor);
|
||||
preview_draw(&self->preview);
|
||||
imgui_draw();
|
||||
|
||||
SDL_GL_SwapWindow(self->window);
|
||||
@@ -147,6 +145,8 @@ void loop(State* self)
|
||||
|
||||
_update(self);
|
||||
_draw(self);
|
||||
|
||||
SDL_Delay(STATE_DELAY_MIN);
|
||||
}
|
||||
|
||||
void quit(State* self)
|
||||
|
@@ -13,6 +13,8 @@
|
||||
#define STATE_QUIT_INFO "Exiting..."
|
||||
#define STATE_GL_LINE_WIDTH 2.0f
|
||||
|
||||
#define STATE_DELAY_MIN 1
|
||||
|
||||
#define STATE_MIX_FLAGS (MIX_INIT_MP3 | MIX_INIT_OGG | MIX_INIT_WAV)
|
||||
#define STATE_MIX_SAMPLE_RATE 44100
|
||||
#define STATE_MIX_FORMAT MIX_DEFAULT_FORMAT
|
||||
|
@@ -114,3 +114,33 @@ bool texture_pixel_set(Texture* self, ivec2 position, vec4 color)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Texture texture_copy(Texture* self)
|
||||
{
|
||||
Texture copy = *self;
|
||||
_texture_gl_set(©, nullptr);
|
||||
|
||||
GLuint fboSource;
|
||||
GLuint fboDestination;
|
||||
glGenFramebuffers(1, &fboSource);
|
||||
glGenFramebuffers(1, &fboDestination);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSource);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->id, 0);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboDestination);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copy.id, 0);
|
||||
|
||||
glBlitFramebuffer
|
||||
(
|
||||
0, 0, self->size.x, self->size.y,
|
||||
0, 0, self->size.x, self->size.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
||||
);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fboSource);
|
||||
glDeleteFramebuffers(1, &fboDestination);
|
||||
|
||||
return copy;
|
||||
}
|
@@ -23,3 +23,4 @@ bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size
|
||||
bool texture_pixel_set(Texture* self, ivec2 position, vec4 color);
|
||||
void texture_free(Texture* self);
|
||||
std::vector<u8> texture_download(const Texture* self);
|
||||
Texture texture_copy(Texture* self);
|
19
src/tool.h
19
src/tool.h
@@ -21,8 +21,8 @@ enum ToolType
|
||||
TOOL_COUNT,
|
||||
};
|
||||
|
||||
const SDL_SystemCursor MOUSE_CURSOR_DEFAULT = SDL_SYSTEM_CURSOR_DEFAULT;
|
||||
const SDL_SystemCursor TOOL_MOUSE_CURSORS[TOOL_COUNT] =
|
||||
const SDL_SystemCursor CURSOR_DEFAULT = SDL_SYSTEM_CURSOR_DEFAULT;
|
||||
const SDL_SystemCursor TOOL_CURSORS[TOOL_COUNT] =
|
||||
{
|
||||
SDL_SYSTEM_CURSOR_POINTER,
|
||||
SDL_SYSTEM_CURSOR_MOVE,
|
||||
@@ -35,18 +35,3 @@ const SDL_SystemCursor TOOL_MOUSE_CURSORS[TOOL_COUNT] =
|
||||
SDL_SYSTEM_CURSOR_DEFAULT,
|
||||
SDL_SYSTEM_CURSOR_DEFAULT
|
||||
};
|
||||
|
||||
const bool TOOL_MOUSE_CURSOR_IS_CONSTANT[TOOL_COUNT] =
|
||||
{
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
};
|
||||
|
Reference in New Issue
Block a user