it's kind of finished

This commit is contained in:
2025-06-28 23:33:15 -04:00
parent d7913c01db
commit 0e8d1ae6b4
19 changed files with 1598 additions and 1133 deletions

View File

@@ -108,17 +108,35 @@ static inline s32 map_next_id_get(const std::map<s32, T>& map) {
s32 id = 0; for (const auto& [key, _] : map) if (key != id) break; else ++id; return id;
}
/* Swaps elements in a map */
/* If neither key exists, do nothing */
/* If one key exists, change its ID */
/* If both keys exist, swap */
template<typename Map, typename Key>
static inline void map_swap(Map& map, const Key& key1, const Key& key2)
{
if (key1 == key2)
return;
auto it1 = map.find(key1);
auto it2 = map.find(key2);
if (it1 == map.end() || it2 == map.end())
return;
using std::swap;
swap(it1->second, it2->second);
}
if (it1 != map.end() && it2 != map.end())
{
using std::swap;
swap(it1->second, it2->second);
}
else if (it1 != map.end())
{
map[key2] = std::move(it1->second);
map.erase(it1);
}
else if (it2 != map.end())
{
map[key1] = std::move(it2->second);
map.erase(it2);
}
};
#define DEFINE_ENUM_TO_STRING_FN(fn_name, arr, count) \
static inline const char* fn_name(s32 index) { \

View File

@@ -62,13 +62,15 @@
#define STRING_IMGUI_ANIMATIONS_REMOVE "Remove"
#define STRING_IMGUI_ANIMATIONS_DUPLICATE "Duplicate"
#define STRING_IMGUI_ANIMATIONS_SET_AS_DEFAULT "Set as Default"
#define STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT "%s (*)"
#define STRING_IMGUI_ANIMATIONS_DEFAULT_ANIMATION_FORMAT "(*) %s "
#define STRING_IMGUI_ANIMATIONS_DRAG_DROP "Animation Drag/Drop"
#define STRING_IMGUI_EVENTS "Events"
#define STRING_IMGUI_EVENTS_EVENT_LABEL "##Event"
#define STRING_IMGUI_EVENTS_ADD "Add"
#define STRING_IMGUI_EVENTS_REMOVE "Remove"
#define STRING_IMGUI_EVENT_FORMAT "#%i %s"
#define STRING_IMGUI_EVENTS_DRAG_DROP "Event Drag/Drop"
#define STRING_IMGUI_SPRITESHEETS "Spritesheets"
#define STRING_IMGUI_SPRITESHEETS_ADD "Add"
@@ -76,6 +78,7 @@
#define STRING_IMGUI_SPRITESHEETS_RELOAD "Reload"
#define STRING_IMGUI_SPRITESHEETS_REPLACE "Replace"
#define STRING_IMGUI_SPRITESHEET_FORMAT "#%i %s"
#define STRING_IMGUI_SPRITESHEETS_DRAG_DROP "Spritesheet Drag/Drop"
#define STRING_IMGUI_FRAME_PROPERTIES "Frame Properties"
#define STRING_IMGUI_FRAME_PROPERTIES_CROP_POSITION "Crop Position"
@@ -116,12 +119,15 @@
#define STRING_IMGUI_ANIMATION_PREVIEW_AXIS_COLOR "Color"
#define STRING_IMGUI_ANIMATION_PREVIEW_ROOT_TRANSFORM "Root Transform"
#define STRING_IMGUI_ANIMATION_PREVIEW_SHOW_PIVOT "Show Pivot"
#define STRING_IMGUI_ANIMATION_PREVIEW_POSITION "##Position"
#define STRING_IMGUI_ANIMATION_PREVIEW_POSITION_FORMAT "Position: {%5.0f, %5.0f}"
#define STRING_IMGUI_SPRITESHEET_EDITOR "Spritesheet Editor"
#define STRING_IMGUI_SPRITESHEET_EDITOR_LABEL "##Animation Preview"
#define STRING_IMGUI_SPRITESHEET_EDITOR_SETTINGS "##Animation Preview Settings"
#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SETTINGS "##Grid Settings"
#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID "Grid"
#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SNAP "Snap"
#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_SIZE "Size"
#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_OFFSET "Offset"
#define STRING_IMGUI_SPRITESHEET_EDITOR_GRID_COLOR "Color"
@@ -131,6 +137,8 @@
#define STRING_IMGUI_SPRITESHEET_EDITOR_BACKGROUND_COLOR "Background Color"
#define STRING_IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW "Center View"
#define STRING_IMGUI_SPRITESHEET_EDITOR_BORDER "Border"
#define STRING_IMGUI_SPRITESHEET_EDITOR_POSITION "##Position"
#define STRING_IMGUI_SPRITESHEET_EDITOR_POSITION_FORMAT "Position: {%5.0f, %5.0f}"
#define STRING_IMGUI_TIMELINE "Timeline"
#define STRING_IMGUI_TIMELINE_HEADER "##Header"
@@ -178,6 +186,8 @@
#define STRING_IMGUI_TIMELINE_VIEWER "Viewer"
#define STRING_IMGUI_TIMELINE_CHILD "Timeline"
#define STRING_IMGUI_TIMELINE_MAIN "Main"
#define STRING_IMGUI_TIMELINE_FRAME_DRAG_DROP "Frame Drag/Drop"
#define STRING_IMGUI_TIMELINE_ITEM_DRAG_DROP "Item Drag/Drop"
#define STRING_IMGUI_TOOLS "Tools"
#define STRING_IMGUI_TOOLS_PAN "##Pan"
@@ -190,7 +200,7 @@
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE "Duplicates the selected animation."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE "Removes the selected animation."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT "Select the animation to edit.\nYou can also click the name to edit it."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT "Sets the selected animation as the default.\nDefault animations are marked with \"(*)\"."
#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT "Sets the selected animation as the default.\nThe default animation is marked with \"(*)\"."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS "Toggles the display of the X/Y axes on the animation preview."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS_COLOR "Changes the color of the X/Y axes on the animation preview."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR "Changes the background color of the animation preview."
@@ -200,7 +210,7 @@
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_OFFSET "Changes the animation preview grid's offset, in pixels."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE "Changes the animation preview grid's size, in pixels."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM "Changes the animation preview zoom level."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ROOT_TRANSFORM "Toggle the properties of the Root element of an animation being able to change the properties (such as position or scale) of all of the animation's elements."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ROOT_TRANSFORM "Toggle the properties of the Root element of an animation being able to change the properties (such as position or scale) of all of the animation's elements.\nNOTE: Scale/rotation currently not implemented. Matrix math is hard."
#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_SHOW_PIVOT "Toggle the pivots of layer animation elements being visible on the animation preview."
#define STRING_IMGUI_TOOLTIP_EVENTS_ADD "Add a new event."
#define STRING_IMGUI_TOOLTIP_EVENTS_REMOVE "Removes the selected event."
@@ -251,16 +261,17 @@
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_BACKGROUND_COLOR "Changes the background color of the spritesheet editor."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_CENTER_VIEW "Centers the spritesheet editor's pan."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID "Toggles grid visibility on the spritesheet editor."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_SNAP "When using the crop tool, will snap points to the grid."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_COLOR "Changes the spritesheet editor grid color."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_OFFSET "Changes the spritesheet editor grid's offset, in pixels."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_GRID_SIZE "Changes the spritesheet editor grid's size, in pixels."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_ZOOM "Changes the spritesheet editor zoom level."
#define STRING_IMGUI_TOOLTIP_SPRITESHEET_EDITOR_BORDER "Toggles a border appearing around the confines of the spritesheet."
#define STRING_IMGUI_TOOLTIP_TOOLS_PAN "Use the pan tool.\nWill shift the view as the cursor is dragged."
#define STRING_IMGUI_TOOLTIP_TOOLS_MOVE "Use the move tool.\nWill move selected elements as the cursor is dragged."
#define STRING_IMGUI_TOOLTIP_TOOLS_ROTATE "Use the rotate tool.\nWill rotate selected elements as the cursor is dragged."
#define STRING_IMGUI_TOOLTIP_TOOLS_SCALE "Use the scale tool.\nWill scale the selected elements as the cursor is dragged."
#define STRING_IMGUI_TOOLTIP_TOOLS_CROP "Use the crop tool.\nWill set the crop of the selected elements as the cursor is dragged."
#define STRING_IMGUI_TOOLTIP_TOOLS_MOVE "Use the move tool.\nWill move selected elements as the cursor is dragged.\n(Animation Preview only.)"
#define STRING_IMGUI_TOOLTIP_TOOLS_ROTATE "Use the rotate tool.\nWill rotate selected elements as the cursor is dragged.\n(Animation Preview only.)"
#define STRING_IMGUI_TOOLTIP_TOOLS_SCALE "Use the scale tool.\nWill scale the selected elements as the cursor is dragged.\n(Animation Preview only.)"
#define STRING_IMGUI_TOOLTIP_TOOLS_CROP "Use the crop tool.\nWill set the crop of the selected elements as the cursor is dragged.\n(Spritesheet Editor only.)"
#define STRING_OPENGL_VERSION "#version 330"

View File

@@ -37,7 +37,6 @@ anm2_serialize(Anm2* self, const char* path)
if (!self || !path)
return false;
/* Update creation date on first version */
if (self->version == 0)
anm2_created_on_set(self);
@@ -45,8 +44,6 @@ anm2_serialize(Anm2* self, const char* path)
/* Increment anm2's version */
self->version++;
/* Set the anm2's date to the system time */
/* AnimatedActor */
animatedActorElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
document.InsertFirstChild(animatedActorElement);
@@ -283,14 +280,14 @@ anm2_serialize(Anm2* self, const char* path)
/* Triggers */
triggersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]);
for (auto & trigger : animation.triggers.items)
for (auto & frame : animation.triggers.frames)
{
XMLElement* triggerElement;
/* Trigger */
triggerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]);
triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], trigger.eventID); /* EventID */
triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_AT_FRAME], trigger.atFrame); /* AtFrame */
triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], frame.eventID); /* EventID */
triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_AT_FRAME], frame.atFrame); /* AtFrame */
triggersElement->InsertEndChild(triggerElement);
}
@@ -321,35 +318,27 @@ anm2_serialize(Anm2* self, const char* path)
bool
anm2_deserialize(Anm2* self, Resources* resources, const char* path)
{
XMLDocument document;
XMLError error;
const XMLElement* element;
const XMLElement* root;
Anm2Spritesheet* lastSpritesheet = NULL;
Anm2Layer* lastLayer = NULL;
Anm2Null* lastNull = NULL;
Anm2Event* lastEvent = NULL;
Anm2Animation* lastAnimation = NULL;
Anm2LayerAnimation* lastLayerAnimation = NULL;
Anm2NullAnimation* lastNullAnimation = NULL;
Anm2Frame* lastFrame = NULL;
Anm2Trigger* lastTrigger = NULL;
XMLDocument xmlDocument;
XMLError xmlError;
const XMLElement* xmlElement;
const XMLElement* xmlRoot;
Anm2Animation* animation = NULL;
Anm2Layer* layer = NULL;
Anm2Null* null = NULL;
Anm2Item* item = NULL;
Anm2Event* event = NULL;
Anm2Frame* frame = NULL;
Anm2Spritesheet* spritesheet = NULL;
Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR;
Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID;
Anm2AnimationType animationType = ANM2_ROOT_ANIMATION;
Anm2Null tempNull;
Anm2Layer tempLayer;
Anm2Spritesheet tempSpritesheet;
Anm2Event tempEvent;
char lastSpritesheetPath[PATH_MAX];
*self = Anm2{};
error = document.LoadFile(path);
xmlError = xmlDocument.LoadFile(path);
if (error != XML_SUCCESS)
if (xmlError != XML_SUCCESS)
{
printf(STRING_ERROR_ANM2_READ, path, document.ErrorStr());
printf(STRING_ERROR_ANM2_READ, path, xmlDocument.ErrorStr());
return false;
}
@@ -357,297 +346,252 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
strncpy(self->path, path, PATH_MAX - 1);
working_directory_from_path_set(path);
root = document.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
element = root;
xmlRoot = xmlDocument.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
xmlElement = xmlRoot;
while (element)
while (xmlElement)
{
const XMLAttribute* attribute;
const XMLElement* child;
s32 id;
const XMLAttribute* xmlAttribute = NULL;
const XMLElement* xmlChild = NULL;
s32 id = 0;
/* Elements */
anm2Element = anm2_element_from_string(element->Name());
anm2Element = anm2_element_from_string(xmlElement->Name());
switch (anm2Element)
{
case ANM2_ELEMENT_SPRITESHEET:
lastSpritesheet = &tempSpritesheet;
id = map_next_id_get(self->spritesheets);
self->spritesheets[id] = Anm2Spritesheet{};
spritesheet = &self->spritesheets[id];
break;
case ANM2_ELEMENT_LAYER:
lastLayer = &tempLayer;
id = map_next_id_get(self->layers);
self->layers[id] = Anm2Layer{};
layer = &self->layers[id];
break;
case ANM2_ELEMENT_NULL:
lastNull = &tempNull;
id = map_next_id_get(self->nulls);
self->nulls[id] = Anm2Null{};
null = &self->nulls[id];
break;
case ANM2_ELEMENT_EVENT:
lastEvent = &tempEvent;
id = map_next_id_get(self->events);
self->events[id] = Anm2Event{};
event = &self->events[id];
break;
case ANM2_ELEMENT_ANIMATION:
id = map_next_id_get(self->animations);
self->animations[id] = Anm2Animation{};
lastAnimation = &self->animations[id];
animation = &self->animations[id];
break;
case ANM2_ELEMENT_ROOT_ANIMATION:
animationType = ANM2_ROOT_ANIMATION;
item = &animation->rootAnimation;
break;
case ANM2_ELEMENT_LAYER_ANIMATION:
animationType = ANM2_LAYER_ANIMATION;
lastLayerAnimation = NULL;
id = map_next_id_get(animation->layerAnimations);
animation->layerAnimations[id] = Anm2Item{};
item = &animation->layerAnimations[id];
break;
case ANM2_ELEMENT_NULL_ANIMATION:
animationType = ANM2_NULL_ANIMATION;
lastNullAnimation = NULL;
id = map_next_id_get(animation->nullAnimations);
animation->nullAnimations[id] = Anm2Item{};
item = &animation->nullAnimations[id];
break;
case ANM2_ELEMENT_TRIGGERS:
item = &animation->triggers;
break;
case ANM2_ELEMENT_FRAME:
switch (animationType)
{
case ANM2_ROOT_ANIMATION:
lastAnimation->rootAnimation.frames.push_back(Anm2Frame{});
lastFrame = &lastAnimation->rootAnimation.frames.back();
break;
case ANM2_LAYER_ANIMATION:
if (!lastLayerAnimation) break;
lastLayerAnimation->frames.push_back(Anm2Frame{});
lastFrame = &lastLayerAnimation->frames.back();
break;
case ANM2_NULL_ANIMATION:
if (!lastNullAnimation) break;
lastNullAnimation->frames.push_back(Anm2Frame{});
lastFrame = &lastNullAnimation->frames.back();
break;
default:
break;
}
break;
case ANM2_ELEMENT_TRIGGER:
lastAnimation->triggers.items.push_back(Anm2Trigger{});
lastTrigger = &lastAnimation->triggers.items.back();
break;
item->frames.push_back(Anm2Frame{});
frame = &item->frames.back();
default:
break;
}
/* Attributes */
attribute = element->FirstAttribute();
xmlAttribute = xmlElement->FirstAttribute();
while (attribute)
while (xmlAttribute)
{
anm2Attribute = anm2_attribute_from_string(attribute->Name());
anm2Attribute = anm2_attribute_from_string(xmlAttribute->Name());
switch (anm2Attribute)
{
case ANM2_ATTRIBUTE_CREATED_BY:
strncpy(self->createdBy, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(self->createdBy, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_CREATED_ON:
strncpy(self->createdOn, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(self->createdOn, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_VERSION:
self->version = atoi(attribute->Value());
self->version = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_FPS:
self->fps = atoi(attribute->Value());
self->fps = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_ID:
id = atoi(attribute->Value());
switch (anm2Element)
{
case ANM2_ELEMENT_SPRITESHEET:
self->spritesheets[id] = tempSpritesheet;
lastSpritesheet = &self->spritesheets[id];
break;
case ANM2_ELEMENT_LAYER:
self->layers[id] = tempLayer;
lastLayer = &self->layers[id];
break;
case ANM2_ELEMENT_NULL:
self->nulls[id] = tempNull;
lastNull = &self->nulls[id];
break;
case ANM2_ELEMENT_EVENT:
self->events[id] = tempEvent;
lastEvent = &self->events[id];
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_LAYER_ID:
map_swap(animation->layerAnimations, id, atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_NULL_ID:
map_swap(animation->nullAnimations, id, atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_PATH:
/* Make path lowercase */
strncpy(lastSpritesheetPath, attribute->Value(), PATH_MAX - 1);
strncpy(spritesheet->path, xmlAttribute->Value(), PATH_MAX - 1);
break;
case ANM2_ATTRIBUTE_NAME:
switch (anm2Element)
{
case ANM2_ELEMENT_LAYER:
strncpy(lastLayer->name, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(layer->name, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ELEMENT_NULL:
strncpy(lastNull->name, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(null->name, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ELEMENT_ANIMATION:
strncpy(lastAnimation->name, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(animation->name, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ELEMENT_EVENT:
strncpy(lastEvent->name, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(event->name, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_SPRITESHEET_ID:
lastLayer->spritesheetID = atoi(attribute->Value());
layer->spritesheetID = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_SHOW_RECT:
switch (anm2Element)
{
case ANM2_ELEMENT_NULL:
lastNull->isShowRect = string_to_bool(attribute->Value());
break;
default:
break;
}
null->isShowRect = string_to_bool(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_DEFAULT_ANIMATION:
strncpy(self->defaultAnimation, attribute->Value(), ANM2_STRING_MAX - 1);
strncpy(self->defaultAnimation, xmlAttribute->Value(), ANM2_STRING_MAX - 1);
break;
case ANM2_ATTRIBUTE_FRAME_NUM:
lastAnimation->frameNum = atoi(attribute->Value());
animation->frameNum = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_LOOP:
lastAnimation->isLoop = string_to_bool(attribute->Value());
animation->isLoop = string_to_bool(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_X_POSITION:
lastFrame->position.x = atof(attribute->Value());
frame->position.x = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_Y_POSITION:
lastFrame->position.y = atof(attribute->Value());
frame->position.y = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_X_PIVOT:
lastFrame->pivot.x = atof(attribute->Value());
frame->pivot.x = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_Y_PIVOT:
lastFrame->pivot.y = atof(attribute->Value());
frame->pivot.y = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_X_CROP:
lastFrame->crop.x = atof(attribute->Value());
frame->crop.x = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_Y_CROP:
lastFrame->crop.y = atof(attribute->Value());
frame->crop.y = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_WIDTH:
lastFrame->size.x = atof(attribute->Value());
frame->size.x = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_HEIGHT:
lastFrame->size.y = atof(attribute->Value());
frame->size.y = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_X_SCALE:
lastFrame->scale.x = atof(attribute->Value());
frame->scale.x = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_Y_SCALE:
lastFrame->scale.y = atof(attribute->Value());
frame->scale.y = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_DELAY:
lastFrame->delay = atoi(attribute->Value());
frame->delay = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_VISIBLE:
switch (anm2Element)
{
case ANM2_ELEMENT_FRAME:
lastFrame->isVisible = string_to_bool(attribute->Value());
frame->isVisible = string_to_bool(xmlAttribute->Value());
break;
case ANM2_LAYER_ANIMATION:
lastLayerAnimation->isVisible = string_to_bool(attribute->Value());
break;
case ANM2_NULL_ANIMATION:
lastNullAnimation->isVisible = string_to_bool(attribute->Value());
case ANM2_ELEMENT_ROOT_ANIMATION:
case ANM2_ELEMENT_LAYER_ANIMATION:
case ANM2_ELEMENT_NULL_ANIMATION:
item->isVisible = string_to_bool(xmlAttribute->Value());
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_RED_TINT:
lastFrame->tintRGBA.r = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->tintRGBA.r = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_GREEN_TINT:
lastFrame->tintRGBA.g = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->tintRGBA.g = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_BLUE_TINT:
lastFrame->tintRGBA.b = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->tintRGBA.b = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_ALPHA_TINT:
lastFrame->tintRGBA.a = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->tintRGBA.a = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_RED_OFFSET:
lastFrame->offsetRGB.r = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->offsetRGB.r = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_GREEN_OFFSET:
lastFrame->offsetRGB.g = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->offsetRGB.g = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_BLUE_OFFSET:
lastFrame->offsetRGB.b = COLOR_INT_TO_FLOAT(atoi(attribute->Value()));
frame->offsetRGB.b = COLOR_INT_TO_FLOAT(atoi(xmlAttribute->Value()));
break;
case ANM2_ATTRIBUTE_ROTATION:
lastFrame->rotation = atof(attribute->Value());
frame->rotation = atof(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_INTERPOLATED:
lastFrame->isInterpolated = string_to_bool(attribute->Value());
break;
case ANM2_ATTRIBUTE_LAYER_ID:
id = atoi(attribute->Value());
lastAnimation->layerAnimations[id] = Anm2LayerAnimation{};
lastLayerAnimation = &lastAnimation->layerAnimations[id];
break;
case ANM2_ATTRIBUTE_NULL_ID:
id = atoi(attribute->Value());
lastAnimation->nullAnimations[id] = Anm2NullAnimation{};
lastNullAnimation = &lastAnimation->nullAnimations[id];
frame->isInterpolated = string_to_bool(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_EVENT_ID:
lastTrigger->eventID = atoi(attribute->Value());
frame->eventID = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_AT_FRAME:
lastTrigger->atFrame = atoi(attribute->Value());
frame->atFrame = atoi(xmlAttribute->Value());
break;
default:
break;
}
attribute = attribute->Next();
xmlAttribute = xmlAttribute->Next();
}
/* Load spritesheet textures */
if (anm2Element == ANM2_ELEMENT_SPRITESHEET)
{
strncpy(lastSpritesheet->path, lastSpritesheetPath, PATH_MAX);
anm2_spritesheet_texture_load(self, resources, lastSpritesheetPath , id);
}
anm2_spritesheet_texture_load(self, resources, spritesheet->path , id);
/* Iterate through children */
child = element->FirstChildElement();
xmlChild = xmlElement->FirstChildElement();
if (child)
if (xmlChild)
{
element = child;
xmlElement = xmlChild;
continue;
}
/* Iterate through siblings */
while (element)
while (xmlElement)
{
const XMLElement* next;
const XMLElement* xmlNext;
next = element->NextSiblingElement();
xmlNext = xmlElement->NextSiblingElement();
if (next)
if (xmlNext)
{
element = next;
xmlElement = xmlNext;
break;
}
/* If no siblings, return to parent. If no parent, end parsing */
element = element->Parent() ? element->Parent()->ToElement() : NULL;
xmlElement = xmlElement->Parent() ? xmlElement->Parent()->ToElement() : NULL;
}
}
@@ -665,7 +609,7 @@ anm2_layer_add(Anm2* self)
self->layers[id] = Anm2Layer{};
for (auto & [animationID, animation] : self->animations)
animation.layerAnimations[id] = Anm2LayerAnimation{};
animation.layerAnimations[id] = Anm2Item{};
}
/* Removes a layer from the anm2 given the index/id */
@@ -674,8 +618,8 @@ anm2_layer_remove(Anm2* self, s32 id)
{
self->layers.erase(id);
for (auto& animationPair : self->animations)
animationPair.second.layerAnimations.erase(id);
for (auto & [animationID, animation] : self->animations)
animation.layerAnimations.erase(id);
}
/* Adds a new null to the anm2 */
@@ -687,7 +631,7 @@ anm2_null_add(Anm2* self)
self->nulls[id] = Anm2Null{};
for (auto & [animationID, animation] : self->animations)
animation.nullAnimations[id] = Anm2NullAnimation{};
animation.nullAnimations[id] = Anm2Item{};
}
/* Removes a null from the anm2 given the index/id */
@@ -696,8 +640,8 @@ anm2_null_remove(Anm2* self, s32 id)
{
self->nulls.erase(id);
for (auto& animationPair : self->animations)
animationPair.second.nullAnimations.erase(id);
for (auto & [animationID, animation] : self->animations)
animation.nullAnimations.erase(id);
}
/* Adds a new animation to the anm2, makes sure to keep the layeranimations/nullsanimation check */
@@ -710,13 +654,13 @@ anm2_animation_add(Anm2* self)
/* match layers */
for (auto & [layerID, layer] : self->layers)
{
animation.layerAnimations[layerID] = Anm2LayerAnimation{};
animation.layerAnimations[layerID] = Anm2Item{};
}
/* match nulls */
for (auto & [nullID, null] : self->nulls)
{
animation.nullAnimations[nullID] = Anm2NullAnimation{};
animation.nullAnimations[nullID] = Anm2Item{};
}
/* add a root frame */
@@ -757,69 +701,104 @@ anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path
resources->textures[id] = texture;
}
Anm2Animation*
anm2_animation_from_id(Anm2* self, s32 animationID)
{
auto it = self->animations.find(animationID);
if (it == self->animations.end())
return NULL;
return &it->second;
}
/* Returns the item from a anm2 reference. */
Anm2Item*
anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
{
Anm2Animation* animation = anm2_animation_from_id(self, animationID);
if (!animation)
return NULL;
switch (reference->type)
{
case ANM2_ROOT:
return &animation->rootAnimation;
case ANM2_LAYER:
{
auto it = animation->layerAnimations.find(reference->id);
if (it == animation->layerAnimations.end())
return NULL;
return &it->second;
}
case ANM2_NULL:
{
auto it = animation->nullAnimations.find(reference->id);
if (it == animation->nullAnimations.end())
return NULL;
return &it->second;
}
case ANM2_TRIGGERS:
return &animation->triggers;
default:
return NULL;
}
}
/* Gets the frame from the reference's properties */
Anm2Frame*
anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
{
Anm2Item* item = anm2_item_from_reference(self, reference, animationID);
if (!item)
return NULL;
if (reference->index < 0 || reference->index >= (s32)item->frames.size())
return NULL;
return &item->frames[reference->index];
}
/* Creates/fetches a frame from a given time. */
/* Returns true/false if frame will be valid or not. */
bool
anm2_frame_from_time(Anm2* self, Anm2Animation* animation, Anm2Frame* frame, Anm2AnimationType type, s32 id, f32 time)
void
anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 time)
{
Anm2Animation* animation = anm2_animation_from_id(self, animationID);
/* Out of range */
if (time < 0 || time > animation->frameNum)
return false;
return;
Anm2Item* item = anm2_item_from_reference(self, &reference, animationID);
if (!item)
return;
Anm2RootAnimation* rootAnimation;
Anm2LayerAnimation* layerAnimation;
Anm2NullAnimation* nullAnimation;
Anm2Frame* nextFrame = NULL;
std::vector<Anm2Frame>* frames = NULL;
f32 delayCurrent = 0;
f32 delayNext = 0;
bool isTimeMatchedFrame = false;
s32 delayCurrent = 0;
s32 delayNext = 0;
switch (type)
for (s32 i = 0; i < (s32)item->frames.size(); i++)
{
case ANM2_ROOT_ANIMATION:
frames = &animation->rootAnimation.frames;
break;
case ANM2_LAYER_ANIMATION:
if (id < 0 || id >= (s32)animation->layerAnimations.size())
return false;
frames = &animation->layerAnimations[id].frames;
break;
case ANM2_NULL_ANIMATION:
if (id < 0 || id >= (s32)animation->nullAnimations.size())
return false;
frames = &animation->nullAnimations[id].frames;
break;
default:
return false;
}
for (s32 i = 0; i < (s32)frames->size(); i++)
{
*frame = (*frames)[i];
*frame = item->frames[i];
delayNext += frame->delay;
/* If a frame is within the time constraints, it's a time matched frame, break */
/* Otherwise, the last found frame parsed will be used. */
if (time >= delayCurrent && time < delayNext)
{
if (i + 1 < (s32)frames->size())
nextFrame = &(*frames)[i + 1];
if (i + 1 < (s32)item->frames.size())
nextFrame = &item->frames[i + 1];
else
nextFrame = NULL;
isTimeMatchedFrame = true;
break;
}
delayCurrent += frame->delay;
}
/* No valid frame found */
if (!isTimeMatchedFrame)
return false;
/* interpolate only if there's a frame following */
/* Interpolate only if there's a frame following */
if (frame->isInterpolated && nextFrame)
{
f32 interpolationTime = (time - delayCurrent) / (delayNext - delayCurrent);
@@ -830,6 +809,67 @@ anm2_frame_from_time(Anm2* self, Anm2Animation* animation, Anm2Frame* frame, Anm
frame->offsetRGB = glm::mix(frame->offsetRGB, nextFrame->offsetRGB, interpolationTime);;
frame->tintRGBA = glm::mix(frame->tintRGBA, nextFrame->tintRGBA, interpolationTime);;
}
}
return true;
/* Will try adding a frame to the anm2 given the specified reference */
Anm2Frame*
anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
{
Anm2Animation* animation = anm2_animation_from_id(self, animationID);
Anm2Item* item = anm2_item_from_reference(self, reference, animationID);
if (!animation || !item)
return NULL;
if (item)
{
Anm2Frame frame = Anm2Frame{};
s32 index = -1;
if (reference->type == ANM2_TRIGGERS)
{
/* don't add redudant triggers (i.e. at same time) */
for (auto & frameCheck : item->frames)
{
if (frameCheck.atFrame == time)
return NULL;
}
frame.atFrame = time;
index = item->frames.size();
}
else
{
s32 delay = 0;
s32 frameDelayCount = 0;
/* Add up all delay to see where this new frame might lie */
for (auto & frameCheck : item->frames)
frameDelayCount += frameCheck.delay;
/* If adding the smallest frame would be over the length, don't bother */
if (frameDelayCount + ANM2_FRAME_DELAY_MIN > animation->frameNum)
return NULL;
/* Will insert next to frame if frame exists */
Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference, animationID);
if (checkFrame)
{
/* Will shrink frame delay to fit */
if (frameDelayCount + checkFrame->delay > animation->frameNum)
frame.delay = animation->frameNum - frameDelayCount;
index = reference->index + 1;
}
else
index = (s32)item->frames.size();
}
item->frames.insert(item->frames.begin() + index, frame);
return &item->frames[index];
}
return NULL;
}

View File

@@ -14,6 +14,7 @@
#define ANM2_FPS_MAX 120
#define ANM2_FRAME_NUM_MIN 1
#define ANM2_FRAME_NUM_MAX 1000000
#define ANM2_FRAME_DELAY_MIN 1
/* Elements */
#define ANM2_ELEMENT_LIST \
@@ -108,14 +109,14 @@ static const char* ANM2_ATTRIBUTE_STRINGS[] = {
DEFINE_STRING_TO_ENUM_FN(anm2_attribute_from_string, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT)
#define ANM2_ANIMATION_TYPE_COUNT (ANM2_ANIMATION_TRIGGERS + 1)
enum Anm2AnimationType
#define ANM2_COUNT (ANM2_TRIGGER + 1)
enum Anm2Type
{
ANM2_NONE,
ANM2_ROOT_ANIMATION,
ANM2_LAYER_ANIMATION,
ANM2_NULL_ANIMATION,
ANM2_TRIGGER
ANM2_ROOT,
ANM2_LAYER,
ANM2_NULL,
ANM2_TRIGGERS
};
struct Anm2Spritesheet
@@ -140,18 +141,14 @@ struct Anm2Event
char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_EVENT;
};
struct Anm2Trigger
{
s32 eventID = -1;
s32 atFrame = -1;
};
struct Anm2Frame
{
bool isInterpolated = false;
bool isVisible = true;
f32 rotation = 1.0f;
s32 delay = 1;
s32 delay = ANM2_FRAME_DELAY_MIN;
s32 atFrame = -1;
s32 eventID = -1;
vec2 crop = {0.0f, 0.0f};
vec2 pivot = {0.0f, 0.0f};
vec2 position = {0.0f, 0.0f};
@@ -161,39 +158,21 @@ struct Anm2Frame
vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
};
struct Anm2LayerAnimation
{
bool isVisible = true;
std::vector<Anm2Frame> frames;
};
struct Anm2NullAnimation
{
bool isVisible = true;
std::vector<Anm2Frame> frames;
};
struct Anm2RootAnimation
struct Anm2Item
{
bool isVisible = true;
std::vector<Anm2Frame> frames;
};
struct Anm2Triggers
{
bool isVisible = true;
std::vector<Anm2Trigger> items;
};
struct Anm2Animation
{
s32 frameNum = ANM2_FRAME_NUM_MIN;
char name[ANM2_STRING_MAX] = STRING_ANM2_NEW_ANIMATION;
bool isLoop = true;
Anm2RootAnimation rootAnimation;
std::map<s32, Anm2LayerAnimation> layerAnimations;
std::map<s32, Anm2NullAnimation> nullAnimations;
Anm2Triggers triggers;
Anm2Item rootAnimation;
std::map<s32, Anm2Item> layerAnimations;
std::map<s32, Anm2Item> nullAnimations;
Anm2Item triggers;
};
struct Anm2
@@ -211,6 +190,15 @@ struct Anm2
std::map<s32, Anm2Animation> animations;
};
struct Anm2Reference
{
Anm2Type type = ANM2_NONE;
s32 id = -1;
s32 index = -1;
auto operator<=>(const Anm2Reference&) const = default;
};
void anm2_layer_add(Anm2* self);
void anm2_layer_remove(Anm2* self, s32 id);
void anm2_null_add(Anm2* self);
@@ -222,4 +210,8 @@ void anm2_created_on_set(Anm2* self);
s32 anm2_animation_add(Anm2* self);
void anm2_animation_remove(Anm2* self, s32 id);
void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path, s32 id);
bool anm2_frame_from_time(Anm2* self, Anm2Animation* animation, Anm2Frame* frame, Anm2AnimationType type, s32 id, f32 time);
Anm2Animation* anm2_animation_from_id(Anm2* self, s32 animationID);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID);
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 time);

View File

@@ -19,9 +19,10 @@ _dialog_callback(void* userdata, const char* const* filelist, s32 filter)
}
void
dialog_init(Dialog* self, Anm2* anm2, Resources* resources, SDL_Window* window)
dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window)
{
self->anm2 = anm2;
self->reference = reference;
self->resources = resources;
self->window = window;
}
@@ -76,6 +77,7 @@ dialog_tick(Dialog* self)
switch (self->type)
{
case DIALOG_ANM2_OPEN:
*self->reference = Anm2Reference{};
resources_textures_free(self->resources);
anm2_deserialize(self->anm2, self->resources, self->path);
window_title_from_anm2_set(self->window, self->anm2);

View File

@@ -26,6 +26,7 @@ enum DialogType
struct Dialog
{
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
Resources* resources = NULL;
SDL_Window* window = NULL;
s32 replaceID = -1;
@@ -34,7 +35,7 @@ struct Dialog
bool isSelected = false;
};
void dialog_init(Dialog* self, Anm2* anm2, Resources* resources, SDL_Window* window);
void dialog_init(Dialog* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, SDL_Window* window);
void dialog_anm2_open(Dialog* self);
void dialog_png_open(Dialog* self);
void dialog_png_replace(Dialog* self);

View File

@@ -1,8 +1,8 @@
#include "editor.h"
static void _editor_grid_set(Editor* self);
static s32 _editor_grid_set(Editor* self);
static void
static s32
_editor_grid_set(Editor* self)
{
std::vector<f32> vertices;
@@ -34,19 +34,32 @@ _editor_grid_set(Editor* self)
vertices.push_back(normY);
}
self->gridVertexCount = (s32)vertices.size();
glBindVertexArray(self->gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(f32), vertices.data(), GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
return (s32)vertices.size();
}
void
editor_init(Editor* self, Resources* resources, Settings* settings)
editor_init
(
Editor* self,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Resources* resources,
Settings* settings
)
{
self->anm2 = anm2;
self->reference = reference;
self->animationID = animationID;
self->spritesheetID = spritesheetID;
self->resources = resources;
self->settings = settings;
@@ -140,9 +153,9 @@ editor_draw(Editor* self)
glClear(GL_COLOR_BUFFER_BIT);
if (self->spritesheetID > -1)
if (*self->spritesheetID > -1)
{
Texture* texture = &self->resources->textures[self->spritesheetID];
Texture* texture = &self->resources->textures[*self->spritesheetID];
glm::mat4 spritesheetTransform = editorTransform;
glm::vec2 ndcScale = glm::vec2(texture->size.x, texture->size.y) / (EDITOR_SIZE * 0.5f);
@@ -186,12 +199,16 @@ editor_draw(Editor* self)
glUseProgram(0);
}
if (self->isFrame)
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference, *self->animationID);
/* Draw the layer frame's crop and pivot */
if (frame && self->reference->type == ANM2_LAYER)
{
/* Rect */
glm::mat4 rectTransform = editorTransform;
glm::vec2 rectNDCPos = self->frame.crop / (EDITOR_SIZE / 2.0f);
glm::vec2 rectNDCScale = self->frame.size / (EDITOR_SIZE * 0.5f);
glm::vec2 rectNDCPos = frame->crop / (EDITOR_SIZE / 2.0f);
glm::vec2 rectNDCScale = frame->size / (EDITOR_SIZE / 2.0f);
rectTransform = glm::translate(rectTransform, glm::vec3(rectNDCPos, 0.0f));
rectTransform = glm::scale(rectTransform, glm::vec3(rectNDCScale, 1.0f));
@@ -208,9 +225,10 @@ editor_draw(Editor* self)
glBindVertexArray(0);
glUseProgram(0);
/* Pivot */
glm::mat4 pivotTransform = editorTransform;
glm::vec2 pivotNDCPos = self->frame.pivot / (EDITOR_SIZE / 2.0f);
glm::vec2 pivotNDCScale = ATLAS_SIZES[TEXTURE_PIVOT] / (EDITOR_SIZE * 0.5f);
glm::vec2 pivotNDCPos = ((frame->crop + frame->pivot) - (EDITOR_PIVOT_SIZE / 2.0f)) / (EDITOR_SIZE / 2.0f);
glm::vec2 pivotNDCScale = EDITOR_PIVOT_SIZE / (EDITOR_SIZE / 2.0f);
pivotTransform = glm::translate(pivotTransform, glm::vec3(pivotNDCPos, 0.0f));
pivotTransform = glm::scale(pivotTransform, glm::vec3(pivotNDCScale, 1.0f));
@@ -244,16 +262,21 @@ editor_draw(Editor* self)
if (self->settings->editorIsGrid)
{
if
(
(ivec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY) != self->oldGridSize) ||
(ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY) != self->oldGridOffset)
)
_editor_grid_set(self);
static ivec2 previousGridSize = {-1, -1};
static ivec2 previousGridOffset = {-1, -1};
static s32 gridVertexCount = -1;
ivec2 gridSize = ivec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY);
ivec2 gridOffset = ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY);
if (previousGridSize != gridSize || previousGridOffset != gridOffset)
{
gridVertexCount = _editor_grid_set(self);
previousGridSize = gridSize;
previousGridOffset = gridOffset;
}
glUseProgram(shaderLine);
glBindVertexArray(self->gridVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(editorTransform));
glUniform4f
@@ -262,7 +285,7 @@ editor_draw(Editor* self)
self->settings->editorGridColorR, self->settings->editorGridColorG, self->settings->editorGridColorB, self->settings->editorGridColorA
);
glDrawArrays(GL_LINES, 0, self->gridVertexCount);
glDrawArrays(GL_LINES, 0, gridVertexCount);
glBindVertexArray(0);
glUseProgram(0);
@@ -275,8 +298,6 @@ void
editor_tick(Editor* self)
{
self->settings->editorZoom = CLAMP(self->settings->editorZoom, EDITOR_ZOOM_MIN, EDITOR_ZOOM_MAX);
self->oldGridSize = glm::vec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY);
self->oldGridOffset = glm::vec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY);
}
void

View File

@@ -13,12 +13,17 @@
#define EDITOR_GRID_OFFSET_MAX 100
static const vec2 EDITOR_SIZE = {5000, 5000};
static const vec2 EDITOR_PIVOT_SIZE = {4, 4};
static const vec4 EDITOR_TEXTURE_TINT = COLOR_OPAQUE;
static const vec4 EDITOR_BORDER_TINT = COLOR_OPAQUE;
static const vec4 EDITOR_FRAME_TINT = COLOR_RED;
struct Editor
{
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
s32* animationID = NULL;
s32* spritesheetID = NULL;
Resources* resources = NULL;
Settings* settings = NULL;
GLuint fbo;
@@ -31,16 +36,19 @@ struct Editor
GLuint textureVBO;
GLuint borderVAO;
GLuint borderVBO;
s32 gridVertexCount = -1;
s32 spritesheetID = -1;
s32 oldSpritesheetID = -1;
ivec2 oldGridSize = {-1, -1};
ivec2 oldGridOffset = {-1, -1};
Anm2Frame frame;
bool isFrame = false;
};
void editor_init(Editor* self, Resources* resources, Settings* settings);
void editor_init
(
Editor* self,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Resources* resources,
Settings* settings
);
void editor_draw(Editor* self);
void editor_tick(Editor* self);
void editor_free(Editor* self);

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@
#include "window.h"
#include "input.h"
#include "settings.h"
#include "tool.h"
#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#define IMGUI_ENABLE_DOCKING
@@ -15,8 +16,6 @@
#include <imgui/backends/imgui_impl_sdl3.h>
#include <imgui/backends/imgui_impl_opengl3.h>
#define IMGUI_TIMELINE_ELEMENT_WIDTH 300
#define IMGUI_TIMELINE_ELEMENT_WIDTH 300
#define IMGUI_DRAG_SPEED 1.0
@@ -32,15 +31,15 @@
#define IMGUI_PICKER_LINE_COLOR IM_COL32(255, 255, 255, 255)
#define IMGUI_TOOLS_WIDTH_INCREMENT -2
#define IMGUI_POSITION_STRING_MAX 0xFF
static const vec2 IMGUI_TASKBAR_MARGINS = {8, 4};
static const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 105};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {200, 85};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 135};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {95, 20};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_SIZE = {45, 20};
static const ImVec2 IMGUI_SPRITESHEET_EDITOR_SETTINGS_CHILD_SIZE = {200, 85};
static const ImVec2 IMGUI_SPRITESHEET_EDITOR_SETTINGS_SIZE = {1280, 105};
@@ -52,15 +51,13 @@ static const ImVec2 IMGUI_TIMELINE_VIEWER_SIZE = {0, 40};
static const ImVec2 IMGUI_TIMELINE_ELEMENTS_TIMELINE_SIZE = {0, 40};
static const ImVec2 IMGUI_TIMELINE_FRAME_INDICES_SIZE = {0, 40};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_SIZE = {300, 40};
static const ImVec2 IMGUI_TIMELINE_SHIFT_ARROWS_SIZE = {64, 40};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {150, 20};
static const ImVec2 IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_SIZE = {60, 20};
static const ImVec2 IMGUI_SPRITESHEET_SIZE = {0, 150};
static const ImVec2 IMGUI_SPRITESHEET_PREVIEW_SIZE = {100, 100};
static const ImVec2 IMGUI_ICON_SIZE = {16, 16};
static const ImVec2 IMGUI_ICON_SMALL_SIZE = {8, 8};
static const ImVec2 IMGUI_ICON_DUMMY_SIZE = {20, 16};
static const ImVec2 IMGUI_ICON_BUTTON_SIZE = {24, 24};
static const ImVec2 IMGUI_IMAGE_TARGET_SIZE = {125, 125};
static const ImVec2 IMGUI_ICON_BUTTON_SIZE = {24, 24};
static const ImVec2 IMGUI_DUMMY_SIZE = {1, 1};
static const ImVec4 IMGUI_TIMELINE_HEADER_COLOR = {0.04, 0.04, 0.04, 1.0f};
@@ -89,15 +86,6 @@ static const ImVec4 IMGUI_TIMELINE_LAYER_ACTIVE_COLOR = {1.000, 0.618, 0.324, 0.
static const ImVec4 IMGUI_TIMELINE_NULL_ACTIVE_COLOR = {0.646, 0.971, 0.441, 0.75};
static const ImVec4 IMGUI_TIMELINE_TRIGGERS_ACTIVE_COLOR = {1.000, 0.618, 0.735, 0.75};
#define TOOL_COUNT (TOOL_CROP + 1)
enum ToolType
{
TOOL_PAN,
TOOL_MOVE,
TOOL_ROTATE,
TOOL_SCALE,
TOOL_CROP
};
struct Imgui
{
@@ -105,36 +93,38 @@ struct Imgui
Resources* resources = NULL;
Input* input = NULL;
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
s32* animationID = NULL;
s32* spritesheetID = NULL;
Editor* editor = NULL;
Preview* preview = NULL;
Settings* settings = NULL;
Tool* tool = NULL;
SDL_Window* window = NULL;
SDL_GLContext* glContext = NULL;
Settings* settings = NULL;
s32 animationID = -1;
s32 timelineElementID = -1;
s32 eventID = -1;
s32 spritesheetID = -1;
s32 timelineElementIndex = -1;
Anm2AnimationType animationType = ANM2_NONE;
ToolType tool = TOOL_PAN;
void* frameVector = NULL;
s32 frameIndex = -1;
bool isSwap = false;
Anm2Reference swapReference;
};
void imgui_init
void
imgui_init
(
Imgui* self,
Dialog* dialog,
Resources* resources,
Input* input,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Editor* editor,
Preview* preview,
Settings* settings,
Tool* tool,
SDL_Window* window,
SDL_GLContext* glContext
);
void imgui_tick(Imgui* self);
void imgui_draw(Imgui* self);
void imgui_free(Imgui* self);
void imgui_free(Imgui* self);

View File

@@ -29,6 +29,19 @@ _mouse_tick(Mouse* self)
self->oldPosition = self->position;
}
static void
_keyboard_tick(Keyboard* self)
{
const bool* state;
memcpy(&self->previous, &self->current, sizeof(self->previous));
memset(&self->current, '\0', sizeof(self->current));
state = SDL_GetKeyboardState(NULL);
memcpy(&self->current, state, KEY_COUNT);
}
bool
mouse_press(Mouse* self, MouseType type)
{
@@ -47,8 +60,27 @@ mouse_release(Mouse* self, MouseType type)
return (!self->current[type] && self->previous[type]);
}
bool
key_press(Keyboard* self, KeyType type)
{
return (self->current[type] && !self->previous[type]);
}
bool
key_held(Keyboard* self, KeyType type)
{
return (self->current[type] && self->previous[type]);
}
bool
key_release(Keyboard* self, KeyType type)
{
return (!self->current[type] && self->previous[type]);
}
void
input_tick(Input* self)
{
_mouse_tick(&self->mouse);
_keyboard_tick(&self->keyboard);
}

View File

@@ -9,22 +9,267 @@ enum MouseType
MOUSE_RIGHT
};
#define KEY_COUNT (KEY_DELETE + 1)
#define KEY_COUNT (255)
enum KeyType
{
KEY_DELETE
KEY_UNKNOWN = 0,
KEY_UNKNOWN_TWO = 1,
KEY_UNKNOWN_THREE = 2,
KEY_UNKNOWN_FOUR = 3,
KEY_A = 4,
KEY_B = 5,
KEY_C = 6,
KEY_D = 7,
KEY_E = 8,
KEY_F = 9,
KEY_G = 10,
KEY_H = 11,
KEY_I = 12,
KEY_J = 13,
KEY_K = 14,
KEY_L = 15,
KEY_M = 16,
KEY_N = 17,
KEY_O = 18,
KEY_P = 19,
KEY_Q = 20,
KEY_R = 21,
KEY_S = 22,
KEY_T = 23,
KEY_U = 24,
KEY_V = 25,
KEY_W = 26,
KEY_X = 27,
KEY_Y = 28,
KEY_Z = 29,
KEY_1 = 30,
KEY_2 = 31,
KEY_3 = 32,
KEY_4 = 33,
KEY_5 = 34,
KEY_6 = 35,
KEY_7 = 36,
KEY_8 = 37,
KEY_9 = 38,
KEY_0 = 39,
KEY_RETURN = 40,
KEY_ESCAPE = 41,
KEY_BACKSPACE = 42,
KEY_TAB = 43,
KEY_SPACE = 44,
KEY_MINUS = 45,
KEY_EQUALS = 46,
KEY_LEFTBRACKET = 47,
KEY_RIGHTBRACKET = 48,
KEY_BACKSLASH = 49,
KEY_NONUSHASH = 50,
KEY_SEMICOLON = 51,
KEY_APOSTROPHE = 52,
KEY_GRAVE = 53,
KEY_COMMA = 54,
KEY_PERIOD = 55,
KEY_SLASH = 56,
KEY_CAPSLOCK = 57,
KEY_F1 = 58,
KEY_F2 = 59,
KEY_F3 = 60,
KEY_F4 = 61,
KEY_F5 = 62,
KEY_F6 = 63,
KEY_F7 = 64,
KEY_F8 = 65,
KEY_F9 = 66,
KEY_F10 = 67,
KEY_F11 = 68,
KEY_F12 = 69,
KEY_PRINTSCREEN = 70,
KEY_SCROLLLOCK = 71,
KEY_PAUSE = 72,
KEY_INSERT = 73,
KEY_HOME = 74,
KEY_PAGEUP = 75,
KEY_DELETE = 76,
KEY_END = 77,
KEY_PAGEDOWN = 78,
KEY_RIGHT = 79,
KEY_LEFT = 80,
KEY_DOWN = 81,
KEY_UP = 82,
KEY_NUMLOCKCLEAR = 83,
KEY_KP_DIVIDE = 84,
KEY_KP_MULTIPLY = 85,
KEY_KP_MINUS = 86,
KEY_KP_PLUS = 87,
KEY_KP_ENTER = 88,
KEY_KP_1 = 89,
KEY_KP_2 = 90,
KEY_KP_3 = 91,
KEY_KP_4 = 92,
KEY_KP_5 = 93,
KEY_KP_6 = 94,
KEY_KP_7 = 95,
KEY_KP_8 = 96,
KEY_KP_9 = 97,
KEY_KP_0 = 98,
KEY_KP_PERIOD = 99,
KEY_NONUSBACKSLASH = 100,
KEY_APPLICATION = 101,
KEY_POWER = 102,
KEY_KP_EQUALS = 103,
KEY_F13 = 104,
KEY_F14 = 105,
KEY_F15 = 106,
KEY_F16 = 107,
KEY_F17 = 108,
KEY_F18 = 109,
KEY_F19 = 110,
KEY_F20 = 111,
KEY_F21 = 112,
KEY_F22 = 113,
KEY_F23 = 114,
KEY_F24 = 115,
KEY_EXECUTE = 116,
KEY_HELP = 117,
KEY_MENU = 118,
KEY_SELECT = 119,
KEY_STOP = 120,
KEY_AGAIN = 121,
KEY_UNDO = 122,
KEY_CUT = 123,
KEY_COPY = 124,
KEY_PASTE = 125,
KEY_FIND = 126,
KEY_MUTE = 127,
KEY_VOLUMEUP = 128,
KEY_VOLUMEDOWN = 129,
KEY_LOCKINGCAPSLOCK = 130,
KEY_LOCKINGNUMLOCK = 131,
KEY_LOCKINGSCROLLLOCK = 132,
KEY_KP_COMMA = 133,
KEY_KP_EQUALSAS400 = 134,
KEY_INTERNATIONAL1 = 135,
KEY_INTERNATIONAL2 = 136,
KEY_INTERNATIONAL3 = 137,
KEY_INTERNATIONAL4 = 138,
KEY_INTERNATIONAL5 = 139,
KEY_INTERNATIONAL6 = 140,
KEY_INTERNATIONAL7 = 141,
KEY_INTERNATIONAL8 = 142,
KEY_INTERNATIONAL9 = 143,
KEY_LANG1 = 144,
KEY_LANG2 = 145,
KEY_LANG3 = 146,
KEY_LANG4 = 147,
KEY_LANG5 = 148,
KEY_LANG6 = 149,
KEY_LANG7 = 150,
KEY_LANG8 = 151,
KEY_LANG9 = 152,
KEY_ALTERASE = 153,
KEY_SYSREQ = 154,
KEY_CANCEL = 155,
KEY_CLEAR = 156,
KEY_PRIOR = 157,
KEY_RETURN2 = 158,
KEY_SEPARATOR = 159,
KEY_OUT = 160,
KEY_OPER = 161,
KEY_CLEARAGAIN = 162,
KEY_CRSEL = 163,
KEY_EXSEL = 164,
KEY_KP_00 = 176,
KEY_KP_000 = 177,
KEY_THOUSANDSSEPARATOR = 178,
KEY_DECIMALSEPARATOR = 179,
KEY_CURRENCYUNIT = 180,
KEY_CURRENCYSUBUNIT = 181,
KEY_KP_LEFTPAREN = 182,
KEY_KP_RIGHTPAREN = 183,
KEY_KP_LEFTBRACE = 184,
KEY_KP_RIGHTBRACE = 185,
KEY_KP_TAB = 186,
KEY_KP_BACKSPACE = 187,
KEY_KP_A = 188,
KEY_KP_B = 189,
KEY_KP_C = 190,
KEY_KP_D = 191,
KEY_KP_E = 192,
KEY_KP_F = 193,
KEY_KP_XOR = 194,
KEY_KP_POWER = 195,
KEY_KP_PERCENT = 196,
KEY_KP_LESS = 197,
KEY_KP_GREATER = 198,
KEY_KP_AMPERSAND = 199,
KEY_KP_DBLAMPERSAND = 200,
KEY_KP_VERTICALBAR = 201,
KEY_KP_DBLVERTICALBAR = 202,
KEY_KP_COLON = 203,
KEY_KP_HASH = 204,
KEY_KP_SPACE = 205,
KEY_KP_AT = 206,
KEY_KP_EXCLAM = 207,
KEY_KP_MEMSTORE = 208,
KEY_KP_MEMRECALL = 209,
KEY_KP_MEMCLEAR = 210,
KEY_KP_MEMADD = 211,
KEY_KP_MEMSUBTRACT = 212,
KEY_KP_MEMMULTIPLY = 213,
KEY_KP_MEMDIVIDE = 214,
KEY_KP_PLUSMINUS = 215,
KEY_KP_CLEAR = 216,
KEY_KP_CLEARENTRY = 217,
KEY_KP_BINARY = 218,
KEY_KP_OCTAL = 219,
KEY_KP_DECIMAL = 220,
KEY_KP_HEXADECIMAL = 221,
KEY_LCTRL = 224,
KEY_LSHIFT = 225,
KEY_LALT = 226,
KEY_LGUI = 227,
KEY_RCTRL = 228,
KEY_RSHIFT = 229,
KEY_RALT = 230,
KEY_RGUI = 231
};
#define INPUT_COUNT (INPUT_MOUSE_CLICK + 1)
#define INPUT_COUNT (INPUT_ZOOM_OUT + 1)
enum InputType
{
INPUT_MOUSE_CLICK
INPUT_PAN,
INPUT_MOVE,
INPUT_SCALE,
INPUT_CROP,
INPUT_LEFT,
INPUT_RIGHT,
INPUT_UP,
INPUT_DOWN,
INPUT_ROTATE_LEFT,
INPUT_ROTATE_RIGHT,
INPUT_ZOOM_IN,
INPUT_ZOOM_OUT
};
static const KeyType INPUT_KEYS[INPUT_COUNT]
{
KEY_SPACE,
KEY_T,
KEY_S,
KEY_C,
KEY_LEFT,
KEY_RIGHT,
KEY_UP,
KEY_DOWN,
KEY_Q,
KEY_W,
KEY_1,
KEY_2
};
struct Keyboard
{
bool current[MOUSE_COUNT];
bool previous[MOUSE_COUNT];
bool current[KEY_COUNT];
bool previous[KEY_COUNT];
};
struct Mouse
@@ -39,11 +284,14 @@ struct Mouse
struct Input
{
Keyboard keyboard;
Mouse mouse;
Keyboard keyboard;
};
bool mouse_press(Mouse* self, MouseType type);
bool mouse_held(Mouse* self, MouseType type);
bool mouse_release(Mouse* self, MouseType type);
bool key_press(Keyboard* self, KeyType type);
bool key_held(Keyboard* self, KeyType type);
bool key_release(Keyboard* self, KeyType type);
void input_tick(Input* self);

View File

@@ -1,7 +1,7 @@
#include "preview.h"
static void _preview_axis_set(Preview* self);
static void _preview_grid_set(Preview* self);
static s32 _preview_grid_set(Preview* self);
static void
_preview_axis_set(Preview* self)
@@ -16,13 +16,14 @@ _preview_axis_set(Preview* self)
glBindVertexArray(0);
}
static void
/* Sets and returns the grid's vertices */
static s32
_preview_grid_set(Preview* self)
{
std::vector<f32> vertices;
s32 verticalLineCount = PREVIEW_SIZE.x / MIN(self->settings->previewGridSizeX, PREVIEW_GRID_MIN);
s32 horizontalLineCount = PREVIEW_SIZE.y / MIN(self->settings->previewGridSizeY, PREVIEW_GRID_MIN);
s32 verticalLineCount = (s32)(PREVIEW_SIZE.x / MIN(self->settings->previewGridSizeX, PREVIEW_GRID_MIN));
s32 horizontalLineCount = (s32)(PREVIEW_SIZE.y / MIN(self->settings->previewGridSizeY, PREVIEW_GRID_MIN));
/* Vertical */
for (s32 i = 0; i <= verticalLineCount; i++)
@@ -48,22 +49,23 @@ _preview_grid_set(Preview* self)
vertices.push_back(normY);
}
self->gridVertexCount = (s32)vertices.size();
glBindVertexArray(self->gridVAO);
glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(f32), vertices.data(), GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
return (s32)vertices.size();
}
void
preview_init(Preview* self, Anm2* anm2, Resources* resources, Input* input, Settings* settings)
preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, s32* animationID, Resources* resources, Settings* settings)
{
self->anm2 = anm2;
self->reference = reference;
self->animationID = animationID;
self->resources = resources;
self->input = input;
self->settings = settings;
/* Framebuffer + texture */
@@ -137,14 +139,11 @@ void
preview_tick(Preview* self)
{
self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX);
self->oldGridSize = glm::vec2(self->settings->previewGridSizeX, self->settings->previewGridSizeY);
self->oldGridOffset = glm::vec2(self->settings->previewGridOffsetX, self->settings->previewGridOffsetY);
Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID);
if (self->animationID > -1)
if (animation)
{
Anm2Animation* animation = &self->anm2->animations[self->animationID];
if (self->isPlaying)
{
self->time += (f32)self->anm2->fps / TICK_RATE;
@@ -162,7 +161,7 @@ preview_draw(Preview* self)
{
GLuint shaderLine = self->resources->shaders[SHADER_LINE];
GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE];
f32 zoomFactor = self->settings->previewZoom / 100.0f;
glm::vec2 ndcPan = glm::vec2(-self->settings->previewPanX / (PREVIEW_SIZE.x / 2.0f), -self->settings->previewPanY / (PREVIEW_SIZE.y / 2.0f));
glm::mat4 previewTransform = glm::translate(glm::mat4(1.0f), glm::vec3(ndcPan, 0.0f));
@@ -183,12 +182,18 @@ preview_draw(Preview* self)
/* Grid */
if (self->settings->previewIsGrid)
{
if
(
(ivec2(self->settings->previewGridSizeX, self->settings->previewGridSizeY) != self->oldGridSize) ||
(ivec2(self->settings->previewGridOffsetX, self->settings->previewGridOffsetY) != self->oldGridOffset)
)
_preview_grid_set(self);
static ivec2 previousGridSize = {-1, -1};
static ivec2 previousGridOffset = {-1, -1};
static s32 gridVertexCount = -1;
ivec2 gridSize = ivec2(self->settings->previewGridSizeX, self->settings->previewGridSizeY);
ivec2 gridOffset = ivec2(self->settings->previewGridOffsetX, self->settings->previewGridOffsetY);
if (previousGridSize != gridSize || previousGridOffset != gridOffset)
{
gridVertexCount = _preview_grid_set(self);
previousGridSize = gridSize;
previousGridOffset = gridOffset;
}
glUseProgram(shaderLine);
glBindVertexArray(self->gridVAO);
@@ -200,7 +205,7 @@ preview_draw(Preview* self)
self->settings->previewGridColorR, self->settings->previewGridColorG, self->settings->previewGridColorB, self->settings->previewGridColorA
);
glDrawArrays(GL_LINES, 0, self->gridVertexCount);
glDrawArrays(GL_LINES, 0, gridVertexCount);
glBindVertexArray(0);
glUseProgram(0);
@@ -230,33 +235,32 @@ preview_draw(Preview* self)
glDrawArrays(GL_LINES, 2, 2);
glBindVertexArray(0);
glUseProgram(0);
}
Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID);
/* Animation */
if (self->animationID > -1)
if (animation)
{
Anm2Frame rootFrame = Anm2Frame{};
Anm2Animation* animation = &self->anm2->animations[self->animationID];
bool isRootFrame = anm2_frame_from_time(self->anm2, animation, &rootFrame, ANM2_ROOT_ANIMATION, 0, self->time);
Anm2Frame rootFrame;
anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{ANM2_ROOT, 0, 0}, *self->animationID, self->time);
/* Layers (Reversed) */
/* Layers */
for (auto & [id, layerAnimation] : animation->layerAnimations)
{
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
Anm2Layer* layer = &self->anm2->layers[id];
Anm2Frame frame = layerAnimation.frames[0];
Anm2Frame frame;
anm2_frame_from_time(self->anm2, animation, &frame, ANM2_LAYER_ANIMATION, id, self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
if (!frame.isVisible)
continue;
Texture* texture = &self->resources->textures[layer->spritesheetID];
Texture* texture = &self->resources->textures[self->anm2->layers[id].spritesheetID];
if (texture->isInvalid)
continue;
@@ -264,7 +268,7 @@ preview_draw(Preview* self)
glm::mat4 layerTransform = previewTransform;
glm::vec2 position = self->settings->previewIsRootTransform ? (frame.position + rootFrame.position) : frame.position;
glm::vec2 scale = self->settings->previewIsRootTransform ? (frame.scale / 100.0f) * (rootFrame.scale / 100.0f) : (frame.scale / 100.0f);
glm::vec2 scale = frame.scale / 100.0f;
glm::vec2 ndcPos = position / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcPivotOffset = (frame.pivot * scale) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = (frame.size * scale) / (PREVIEW_SIZE / 2.0f);
@@ -276,7 +280,6 @@ preview_draw(Preview* self)
layerTransform = glm::translate(layerTransform, glm::vec3(-ndcPivotOffset, 0.0f));
layerTransform = glm::scale(layerTransform, glm::vec3(ndcScale, 1.0f));
glm::vec2 uvMin = frame.crop / glm::vec2(texture->size);
glm::vec2 uvMax = (frame.crop + frame.size) / glm::vec2(texture->size);
@@ -305,17 +308,13 @@ preview_draw(Preview* self)
glUseProgram(0);
}
/* Root */
if
(isRootFrame && animation->rootAnimation.isVisible && rootFrame.isVisible)
if (animation->rootAnimation.isVisible && rootFrame.isVisible)
{
glm::mat4 rootTransform = previewTransform;
glm::vec2 ndcPos = (rootFrame.position - (ATLAS_SIZES[TEXTURE_TARGET] / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = ATLAS_SIZES[TEXTURE_TARGET] / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcPivot = (-ATLAS_SIZES[TEXTURE_TARGET] / 2.0f) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcPos = (rootFrame.position - (PREVIEW_TARGET_SIZE / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = PREVIEW_TARGET_SIZE / (PREVIEW_SIZE / 2.0f);
rootTransform = glm::translate(rootTransform, glm::vec3(ndcPos, 0.0f));
rootTransform = glm::rotate(rootTransform, glm::radians(rootFrame.rotation), glm::vec3(0, 0, 1));
rootTransform = glm::scale(rootTransform, glm::vec3(ndcScale, 1.0f));
f32 vertices[] = ATLAS_UV_VERTICES(TEXTURE_TARGET);
@@ -346,17 +345,15 @@ preview_draw(Preview* self)
/* Pivots */
if (self->settings->previewIsShowPivot)
{
for (auto it = animation->layerAnimations.rbegin(); it != animation->layerAnimations.rend(); it++)
/* Layers (Reversed) */
for (auto & [id, layerAnimation] : animation->layerAnimations)
{
s32 id = it->first;
Anm2LayerAnimation layerAnimation = it->second;
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
Anm2Frame frame = layerAnimation.frames[0];
Anm2Frame frame;
anm2_frame_from_time(self->anm2, animation, &frame, ANM2_LAYER_ANIMATION, id, self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
if (!frame.isVisible)
continue;
@@ -365,8 +362,8 @@ preview_draw(Preview* self)
glm::vec2 position = self->settings->previewIsRootTransform ? (frame.position + rootFrame.position) : frame.position;
glm::vec2 ndcPos = (position - (ATLAS_SIZES[TEXTURE_PIVOT] / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = ATLAS_SIZES[TEXTURE_PIVOT] / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcPos = (position - (PREVIEW_PIVOT_SIZE / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = PREVIEW_PIVOT_SIZE / (PREVIEW_SIZE / 2.0f);
pivotTransform = glm::translate(pivotTransform, glm::vec3(ndcPos, 0.0f));
pivotTransform = glm::scale(pivotTransform, glm::vec3(ndcScale, 1.0f));
@@ -403,21 +400,19 @@ preview_draw(Preview* self)
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
continue;
Anm2Frame frame = nullAnimation.frames[0];
Anm2Frame frame;
anm2_frame_from_time(self->anm2, animation, &frame, ANM2_NULL_ANIMATION, id, self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->animationID, self->time);
if (!frame.isVisible)
continue;
Anm2Null* null = NULL;
null = &self->anm2->nulls[id];
Anm2Null* null = &self->anm2->nulls[id];
glm::mat4 nullTransform = previewTransform;
TextureType textureType = null->isShowRect ? TEXTURE_SQUARE : TEXTURE_TARGET;
glm::vec2 size = null->isShowRect ? PREVIEW_POINT_SIZE : ATLAS_SIZES[TEXTURE_TARGET];
glm::vec2 size = null->isShowRect ? PREVIEW_POINT_SIZE : PREVIEW_TARGET_SIZE;
glm::vec2 pos = self->settings->previewIsRootTransform ? frame.position + (rootFrame.position) - (size / 2.0f) : frame.position - (size / 2.0f);
glm::vec2 ndcPos = pos / (PREVIEW_SIZE / 2.0f);
@@ -477,8 +472,6 @@ preview_draw(Preview* self)
}
}
glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

View File

@@ -15,6 +15,9 @@ static const vec2 PREVIEW_CENTER = {0, 0};
#define PREVIEW_GRID_MAX 1000
#define PREVIEW_GRID_OFFSET_MIN 0
#define PREVIEW_GRID_OFFSET_MAX 100
#define PREVIEW_MOVE_STEP 1
#define PREVIEW_ROTATE_STEP 1
#define PREVIEW_SCALE_STEP 1
static const f32 PREVIEW_AXIS_VERTICES[] =
{
@@ -26,16 +29,20 @@ static const f32 PREVIEW_AXIS_VERTICES[] =
static const vec2 PREVIEW_NULL_RECT_SIZE = {100, 100};
static const vec2 PREVIEW_POINT_SIZE = {2, 2};
static const vec2 PREVIEW_PIVOT_SIZE = {4, 4};
static const vec4 PREVIEW_ROOT_TINT = COLOR_GREEN;
static const vec4 PREVIEW_NULL_TINT = COLOR_BLUE;
static const vec4 PREVIEW_PIVOT_TINT = COLOR_RED;
static const vec2 PREVIEW_TARGET_SIZE = {16, 16};
struct Preview
{
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
Input* input = NULL;
Resources* resources = NULL;
Settings* settings = NULL;
s32* animationID = NULL;
GLuint axisVAO;
GLuint axisVBO;
GLuint fbo;
@@ -50,14 +57,9 @@ struct Preview
GLuint textureVBO;
bool isPlaying = false;
f32 time = 0;
ivec2 oldGridOffset = {-1, -1};
ivec2 oldGridSize = {-1, -1};
ivec2 viewport = PREVIEW_SIZE;
s32 animationID = -1;
s32 gridVertexCount = -1;
};
void preview_init(Preview* self, Anm2* anm2, Resources* resources, Input* input, Settings* settings);
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, s32* animationID, Resources* resources, Settings* settings);
void preview_draw(Preview* self);
void preview_tick(Preview* self);
void preview_free(Preview* self);

View File

@@ -52,6 +52,7 @@ enum SettingsItem
SETTINGS_PREVIEW_BACKGROUND_COLOR_B,
SETTINGS_PREVIEW_BACKGROUND_COLOR_A,
SETTINGS_EDITOR_IS_GRID,
SETTINGS_EDITOR_IS_GRID_SNAP,
SETTINGS_EDITOR_IS_BORDER,
SETTINGS_EDITOR_PAN_X,
SETTINGS_EDITOR_PAN_Y,
@@ -98,6 +99,7 @@ struct Settings
f32 previewBackgroundColorB = 0.286f;
f32 previewBackgroundColorA = 1.0f;
bool editorIsGrid = true;
bool editorIsGridSnap = true;
bool editorIsBorder = true;
f32 editorPanX = 0.0f;
f32 editorPanY = 0.0f;
@@ -144,6 +146,7 @@ static const SettingsEntry SETTINGS_ENTRIES[SETTINGS_COUNT] =
{"previewBackgroundColorB=", "previewBackgroundColorB=%f", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewBackgroundColorB)},
{"previewBackgroundColorA=", "previewBackgroundColorA=%f", SETTINGS_TYPE_FLOAT, offsetof(Settings, previewBackgroundColorA)},
{"editorIsGrid=", "editorIsGrid=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, editorIsGrid)},
{"editorIsGridSnap=", "editorIsGridSnap=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
{"editorIsBorder=", "editorIsBorder=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, editorIsBorder)},
{"editorPanX=", "editorPanX=%f", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorPanX)},
{"editorPanY=", "editorPanY=%f", SETTINGS_TYPE_FLOAT, offsetof(Settings, editorPanY)},

View File

@@ -34,6 +34,7 @@ _tick(State* state)
input_tick(&state->input);
editor_tick(&state->editor);
preview_tick(&state->preview);
tool_tick(&state->tool);
dialog_tick(&state->dialog);
imgui_tick(&state->imgui);
}
@@ -104,10 +105,29 @@ init(State* state)
printf(STRING_INFO_GLEW_INIT);
resources_init(&state->resources);
dialog_init(&state->dialog, &state->anm2, &state->resources, state->window);
preview_init(&state->preview, &state->anm2, &state->resources, &state->input, &state->settings);
editor_init(&state->editor, &state->resources, &state->settings);
dialog_init(&state->dialog, &state->anm2, &state->reference, &state->resources, state->window);
tool_init(&state->tool, &state->input);
preview_init
(
&state->preview,
&state->anm2,
&state->reference,
&state->animationID,
&state->resources,
&state->settings
);
editor_init
(
&state->editor,
&state->anm2,
&state->reference,
&state->animationID,
&state->spritesheetID,
&state->resources,
&state->settings
);
imgui_init
(
@@ -116,10 +136,14 @@ init(State* state)
&state->resources,
&state->input,
&state->anm2,
&state->reference,
&state->animationID,
&state->spritesheetID,
&state->editor,
&state->preview,
&state->settings,
state->window,
&state->tool,
state->window,
&state->glContext
);
@@ -144,10 +168,11 @@ loop(State* state)
SDL_Delay(TICK_DELAY - (state->tick - state->lastTick));
_tick(state);
_draw(state);
state->lastTick = state->tick;
}
_draw(state);
}
void

View File

@@ -18,14 +18,18 @@ struct State
Editor editor;
Preview preview;
Anm2 anm2;
Anm2Reference reference;
Resources resources;
Settings settings;
Tool tool;
bool isArgument = false;
bool isRunning = true;
char argument[PATH_MAX] = STRING_EMPTY;
char startPath[PATH_MAX] = STRING_EMPTY;
bool isArgument = false;
u64 tick = 0;
s32 animationID = -1;
s32 spritesheetID = -1;
u64 lastTick = 0;
bool isRunning = true;
u64 tick = 0;
};
void init(State* state);

33
src/tool.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include "tool.h"
void
tool_init(Tool* self, Input* input)
{
self->input = input;
}
void
tool_tick(Tool* self)
{
if (!self->isEnabled) return;
/* Input handling */
if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_PAN]))
self->type = TOOL_PAN;
if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_MOVE]))
self->type = TOOL_MOVE;
if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_SCALE]))
self->type = TOOL_SCALE;
if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_CROP]))
self->type = TOOL_CROP;
if
(
key_press(&self->input->keyboard, INPUT_KEYS[INPUT_ROTATE_LEFT]) ||
key_press(&self->input->keyboard, INPUT_KEYS[INPUT_ROTATE_RIGHT])
)
self->type = TOOL_ROTATE;
}

23
src/tool.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include "input.h"
#define TOOL_COUNT (TOOL_CROP + 1)
enum ToolType
{
TOOL_PAN,
TOOL_MOVE,
TOOL_ROTATE,
TOOL_SCALE,
TOOL_CROP
};
struct Tool
{
Input* input = NULL;
ToolType type = TOOL_PAN;
bool isEnabled = false;
};
void tool_init(Tool* self, Input* input);
void tool_tick(Tool* self);