mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-01-24 17:26:41 +00:00
2262 lines
52 KiB
C++
2262 lines
52 KiB
C++
/*
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
This file is part of Aquaria.
|
|
|
|
Aquaria is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "AnimationEditor.h"
|
|
#include "DSQ.h"
|
|
#include "AquariaMenuItem.h"
|
|
#include "../BBGE/Gradient.h"
|
|
#include "../BBGE/DebugFont.h"
|
|
#include "RenderBase.h"
|
|
#include "Game.h"
|
|
#include "SplineGrid.h"
|
|
#include "RenderGrid.h"
|
|
|
|
|
|
int TIMELINE_GRIDSIZE = 10;
|
|
float TIMELINE_UNIT = 0.1f;
|
|
float TIMELINE_UNIT_STEP = 0.01f;
|
|
const float TIMELINE_HEIGHT = 120;
|
|
const int KEYFRAME_POS_Y = 480;
|
|
const int KEYFRAME_HEIGHT_Y = 12;
|
|
const float TIMELINE_X_OFFS = 5;
|
|
const float TIMELINE_CENTER_Y = 535;
|
|
|
|
|
|
enum { NumPages = 9 }; // one for each number key 1-9
|
|
|
|
const Vector ScreenMsgPos(210, 55);
|
|
|
|
|
|
class TimelineRender;
|
|
|
|
|
|
class KeyframeWidget : public Quad
|
|
{
|
|
public:
|
|
KeyframeWidget(TimelineRender *tr, int key, int page);
|
|
float t;
|
|
int key, page;
|
|
static KeyframeWidget *movingWidget;
|
|
TimelineRender * const timeline;
|
|
|
|
void shiftLeft();
|
|
void shiftRight();
|
|
protected:
|
|
void onUpdate(float dt);
|
|
};
|
|
|
|
AnimationEditor *ae = 0;
|
|
|
|
KeyframeWidget *KeyframeWidget::movingWidget = 0;
|
|
|
|
static void notify(const std::string& s)
|
|
{
|
|
dsq->screenMessage(s, ScreenMsgPos);
|
|
}
|
|
|
|
class TimelineTickRender : public RenderObject
|
|
{
|
|
public:
|
|
TimelineTickRender()
|
|
: bg("black", Vector(400, 0))
|
|
{
|
|
addChild(&bg, PM_STATIC, RBP_ON);
|
|
bg.setWidthHeight(800, TIMELINE_HEIGHT);
|
|
bg.alphaMod = 0.2f;
|
|
}
|
|
|
|
virtual ~TimelineTickRender()
|
|
{
|
|
}
|
|
|
|
private:
|
|
Quad bg;
|
|
void onRender(const RenderState& rs) const OVERRIDE
|
|
{
|
|
glLineWidth(1);
|
|
glBegin(GL_LINES);
|
|
glColor4f(1, 1, 1, 1);
|
|
const float h2 = TIMELINE_HEIGHT * 0.5f;
|
|
for (int x = 0; x < 800; x += TIMELINE_GRIDSIZE)
|
|
{
|
|
glVertex3f(x + TIMELINE_X_OFFS, -h2, 0);
|
|
glVertex3f(x + TIMELINE_X_OFFS, h2, 0);
|
|
}
|
|
glColor4f(0, 1, 0, 1);
|
|
float tx = ae->getAnimTime() * (TIMELINE_GRIDSIZE / TIMELINE_UNIT);
|
|
glVertex3f(tx + TIMELINE_X_OFFS, -h2, 0);
|
|
glVertex3f(tx + TIMELINE_X_OFFS, h2, 0);
|
|
glEnd();
|
|
}
|
|
};
|
|
|
|
class TimelineRender : public Quad
|
|
{
|
|
public:
|
|
const int page;
|
|
DebugFont label;
|
|
|
|
TimelineRender(int page)
|
|
: page(page), label(6)
|
|
{
|
|
setWidthHeight(800, 10);
|
|
addChild(&label, PM_STATIC);
|
|
label.setAlign(ALIGN_LEFT);
|
|
label.offset = Vector(20, 0);
|
|
}
|
|
|
|
virtual ~TimelineRender()
|
|
{
|
|
}
|
|
|
|
void setLabel(const std::string& s)
|
|
{
|
|
label.setText(s);
|
|
}
|
|
|
|
KeyframeWidget *addKeyframe()
|
|
{
|
|
KeyframeWidget *w = new KeyframeWidget(this, keyframes.size(), page);
|
|
keyframes.push_back(w);
|
|
return w;
|
|
}
|
|
|
|
inline const std::vector<KeyframeWidget*>& getKeyframes() const { return keyframes; }
|
|
|
|
void resizeKeyframes(size_t n)
|
|
{
|
|
while(keyframes.size() > n)
|
|
{
|
|
KeyframeWidget *k = keyframes.back();
|
|
k->setLife(0.03f);
|
|
k->setDecayRate(1);
|
|
keyframes.pop_back();
|
|
}
|
|
|
|
while(keyframes.size() < n)
|
|
{
|
|
addKeyframe();
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
void onRender(const RenderState& rs) const OVERRIDE
|
|
{
|
|
SkeletalSprite *spr = ae->getPageSprite(page);
|
|
if(!spr->isLoaded())
|
|
return;
|
|
|
|
if(ae->curPage == page)
|
|
glColor4f(0.6f, 0.8f, 1, 0.5f);
|
|
else
|
|
glColor4f(0.5f, 0.3f, 0.3f, 0.5f);
|
|
|
|
const float h2 = height * 0.5f;
|
|
glPushMatrix();
|
|
glTranslatef(width * 0.5f, h2, 0);
|
|
Quad::onRender(rs);
|
|
glPopMatrix();
|
|
|
|
glColor4f(0, 1, 0, 1);
|
|
glLineWidth(3);
|
|
glBegin(GL_LINES);
|
|
float tx = spr->getAnimationLayer(0)->timer * (TIMELINE_GRIDSIZE / TIMELINE_UNIT);
|
|
glVertex3f(tx + TIMELINE_X_OFFS, h2 - 5, 0);
|
|
glVertex3f(tx + TIMELINE_X_OFFS, h2 + 5, 0);
|
|
glEnd();
|
|
}
|
|
|
|
std::vector<KeyframeWidget*> keyframes;
|
|
};
|
|
|
|
struct AnimationEditorPage
|
|
{
|
|
SkeletalSprite editSprite;
|
|
Quad *center;
|
|
TimelineRender *timeline;
|
|
std::string editingFile;
|
|
std::deque<SkeletalSprite> undoHistory;
|
|
size_t undoEntry;
|
|
|
|
AnimationEditorPage()
|
|
: center(NULL), timeline(NULL), undoEntry(0)
|
|
{
|
|
editSprite.cull = false;
|
|
}
|
|
|
|
~AnimationEditorPage()
|
|
{
|
|
//timeline->safeKill(); // is registered to global state, not necessary
|
|
editSprite.destroy();
|
|
}
|
|
|
|
void showCenter(bool on)
|
|
{
|
|
if(center)
|
|
center->setHidden(!on);
|
|
}
|
|
|
|
bool load(const char *fn) // pass NULL to unload
|
|
{
|
|
if(center)
|
|
{
|
|
center->safeKill();
|
|
center = NULL;
|
|
}
|
|
if(!fn)
|
|
fn = "";
|
|
clearUndoHistory();
|
|
editingFile = fn;
|
|
editSprite.loadSkeletal(editingFile);
|
|
timeline->setLabel(editingFile);
|
|
bool ok = editSprite.isLoaded();
|
|
if(ok)
|
|
{
|
|
center = new Quad("missingimage", Vector(0,0));
|
|
center->alpha.x = 0.2f;
|
|
center->scale = Vector(2,2);
|
|
editSprite.addChild(center, PM_POINTER);
|
|
center->moveToFront();
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void unload()
|
|
{
|
|
load(NULL);
|
|
}
|
|
|
|
void clearUndoHistory()
|
|
{
|
|
undoHistory.clear();
|
|
undoEntry = 0;
|
|
}
|
|
};
|
|
|
|
bool AnimationEditor::isMouseInRect() const
|
|
{
|
|
Vector mp=core->mouse.position;
|
|
const float w2 = rect->width * 0.5f;
|
|
const float h2 = rect->height * 0.5f;
|
|
const float left = rect->position.x - w2;
|
|
const float right = rect->position.x + w2;
|
|
const float top = rect->position.y - h2;
|
|
const float btm = rect->position.y + h2;
|
|
return mp.x >= left && mp.x <= right && mp.y >= top && mp.y <= btm;
|
|
}
|
|
|
|
void AnimationEditor::constrainMouse()
|
|
{
|
|
Vector mp=core->mouse.position;
|
|
const float w2 = rect->width * 0.5f;
|
|
const float h2 = rect->height * 0.5f;
|
|
const float left = rect->position.x - w2;
|
|
const float right = rect->position.x + w2;
|
|
const float top = rect->position.y - h2;
|
|
const float btm = rect->position.y + h2;
|
|
bool doit = false;
|
|
if (mp.x < left ) { mp.x = left; doit = true; }
|
|
if (mp.x > right) { mp.x = right; doit = true; }
|
|
if (mp.y < top ) { mp.y = top; doit = true; }
|
|
if (mp.y > btm ) { mp.y = btm; doit = true; }
|
|
|
|
if(doit)
|
|
core->setMousePosition(mp);
|
|
}
|
|
|
|
KeyframeWidget::KeyframeWidget(TimelineRender *tr, int key, int page)
|
|
: Quad(), key(key), page(page), timeline(tr)
|
|
{
|
|
setTexture("keyframe");
|
|
setWidthHeight(15, 30);
|
|
tr->addChild(this, PM_POINTER);
|
|
}
|
|
|
|
void KeyframeWidget::shiftLeft()
|
|
{
|
|
if (!offset.isInterpolating())
|
|
offset.interpolateTo(Vector(offset.x-80, 0), 0.1f, 0, 0, 0);
|
|
}
|
|
|
|
void KeyframeWidget::shiftRight()
|
|
{
|
|
if (!offset.isInterpolating())
|
|
offset.interpolateTo(Vector(offset.x+80, 0), 0.1f, 0, 0, 0);
|
|
}
|
|
|
|
void KeyframeWidget::onUpdate(float dt)
|
|
{
|
|
Quad::onUpdate(dt);
|
|
if (life != 1 || ae->isAnimating()) return;
|
|
|
|
Animation *ani = ae->getPageAnimation(page);
|
|
|
|
switch(ani->getKeyframe(this->key)->lerpType)
|
|
{
|
|
case 1:
|
|
color = Vector(0,0,1);
|
|
break;
|
|
case 2:
|
|
color = Vector(1,0,0);
|
|
break;
|
|
case 3:
|
|
color = Vector(1,1,0);
|
|
break;
|
|
default:
|
|
color = Vector(1,1,1);
|
|
break;
|
|
}
|
|
|
|
scale.x = scale.y = 1; // Always do selection rectangle checking with full scale, otherwise they are too tiny to grab
|
|
|
|
if (!movingWidget && isCoordinateInside(core->mouse.position))
|
|
{
|
|
if (core->mouse.buttons.left)
|
|
{
|
|
movingWidget = this;
|
|
ae->currentKey = this->key;
|
|
ae->selectPage(this->page);
|
|
}
|
|
}
|
|
if (movingWidget == this)
|
|
{
|
|
float lastT = ani->getKeyframe(this->key)->t;
|
|
this->position.x = int((core->mouse.position.x-offset.x)/TIMELINE_GRIDSIZE)*TIMELINE_GRIDSIZE+TIMELINE_GRIDSIZE/2;
|
|
float newT = int(this->position.x/TIMELINE_GRIDSIZE)*TIMELINE_UNIT;
|
|
ani->getKeyframe(this->key)->t = newT;
|
|
if (core->getShiftState())
|
|
{
|
|
ae->moveNextWidgets(newT-lastT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->position.x = ani->getKeyframe(this->key)->t*TIMELINE_GRIDSIZE*(1/TIMELINE_UNIT) + TIMELINE_GRIDSIZE/2;
|
|
}
|
|
|
|
if (movingWidget == this && !core->mouse.buttons.left)
|
|
{
|
|
movingWidget = 0;
|
|
ae->reorderKeys();
|
|
return;
|
|
}
|
|
|
|
if(!(this->key == ae->currentKey && this->page == ae->curPage))
|
|
scale.x = scale.y = 0.6f;
|
|
}
|
|
|
|
void AnimationEditor::cycleLerpType()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
if (editMode != AE_SELECT) return;
|
|
|
|
Animation *a = getCurrentPageAnimation();
|
|
|
|
if (core->getCtrlState())
|
|
{
|
|
if (a->getNumKeyframes() >= 2)
|
|
{
|
|
pushUndo();
|
|
|
|
SkeletalKeyframe *k1 = a->getFirstKeyframe();
|
|
SkeletalKeyframe *k2 = a->getLastKeyframe();
|
|
if (k1 && k2)
|
|
{
|
|
k2->copyAllButTime(k1);
|
|
}
|
|
notify("Copied Loop Key");
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pushUndo();
|
|
int& lt = a->getKeyframe(this->currentKey)->lerpType;
|
|
lt++;
|
|
if (lt > 3)
|
|
lt = 0;
|
|
}
|
|
}
|
|
|
|
AnimationEditor::AnimationEditor() : StateObject()
|
|
{
|
|
registerState(this, "AnimationEditor");
|
|
}
|
|
|
|
void AnimationEditor::resetScaleOrSave()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (core->getCtrlState())
|
|
saveFile();
|
|
else if(core->getAltState() && editingBone)
|
|
{
|
|
Vector scale(1,1);
|
|
Bone *b = editingBone;
|
|
do
|
|
scale *= b->scale;
|
|
while( (b = dynamic_cast<Bone*>(b->getParent())) ); // don't want to get entity scale; that's what the anim editor uses for zooming
|
|
std::ostringstream os;
|
|
os << scale.x;
|
|
if(!SDL_SetClipboardText(os.str().c_str()))
|
|
notify("Scale copied to clipboard");
|
|
}
|
|
else
|
|
getSelectedPageSprite()->scale = Vector(1,1);
|
|
}
|
|
|
|
void AnimationEditor::applyState()
|
|
{
|
|
dsq->toggleCursor(true, 0.1f);
|
|
core->cameraPos = Vector(0,0);
|
|
selectedStripPoint = 0;
|
|
mouseSelection = true;
|
|
renderBorderMode = RENDER_BORDER_MINIMAL;
|
|
ae = this;
|
|
StateObject::applyState();
|
|
editMode = AE_SELECT;
|
|
editingBone = 0;
|
|
editingBoneSprite = 0;
|
|
editingBonePage = -1;
|
|
editingBoneIdx = -1;
|
|
currentKey = 0;
|
|
splinegrid = 0;
|
|
assistedSplineEdit = true;
|
|
curPage = 0;
|
|
|
|
spriteRoot = new Quad("missingimage", Vector(400, 300));
|
|
spriteRoot->renderQuad = false;
|
|
addRenderObject(spriteRoot, LR_ENTITIES);
|
|
|
|
pages = new AnimationEditorPage[NumPages];
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
{
|
|
TimelineRender *tr = new TimelineRender(i);
|
|
tr->position = Vector(0, KEYFRAME_POS_Y + i * KEYFRAME_HEIGHT_Y);
|
|
addRenderObject(tr, LR_BLACKGROUND);
|
|
spriteRoot->addChild(&pages[i].editSprite, PM_STATIC);
|
|
pages[i].timeline = tr;
|
|
}
|
|
pages[0].load("naija");
|
|
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, lmbu), MOUSE_BUTTON_LEFT, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, lmbd), MOUSE_BUTTON_LEFT, 1);
|
|
addAction(MakeFunctionEvent(AnimationEditor, rmbu), MOUSE_BUTTON_RIGHT, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, rmbd), MOUSE_BUTTON_RIGHT, 1);
|
|
addAction(MakeFunctionEvent(AnimationEditor, mmbd), MOUSE_BUTTON_MIDDLE, 1);
|
|
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, cloneBoneAhead), KEY_SPACE, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, prevKey), KEY_LEFT, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, nextKey), KEY_RIGHT, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, deleteKey), KEY_DELETE, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, resetScaleOrSave), KEY_S, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, quit), KEY_F12, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, goToTitle), KEY_ESCAPE, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, load), KEY_F1, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, loadSkin), KEY_F5, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, clearRot), KEY_R, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, clearPos), KEY_P, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, flipRot), KEY_D, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, toggleHideBone), KEY_N, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, _copyKey), KEY_C, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, _pasteKey), KEY_V, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, undo), KEY_Z, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, redo), KEY_Y, 0);
|
|
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, cycleLerpType), KEY_L, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPrevBone), KEY_UP, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectNextBone), KEY_DOWN, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, editStripKey), KEY_E, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, prevAnim), KEY_PGUP, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, nextAnim), KEY_PGDN, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectAnim), KEY_F9, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, animateOrStop), KEY_RETURN, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, toggleRenderBorders), KEY_B, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, toggleMouseSelection), KEY_M, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, showAllBones), KEY_A, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, toggleGradient), KEY_G, 0);
|
|
|
|
/*addAction(MakeFunctionEvent(AnimationEditor, decrTimelineUnit), KEY_U, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, incrTimelineUnit), KEY_I, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, decrTimelineGrid), KEY_O, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, incrTimelineGrid), KEY_P, 0);*/
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, toggleSplineMode), KEY_W, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, flipH), KEY_F, 0);
|
|
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage0), KEY_1, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage1), KEY_2, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage2), KEY_3, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage3), KEY_4, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage4), KEY_5, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage5), KEY_6, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage6), KEY_7, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage7), KEY_8, 0);
|
|
addAction(MakeFunctionEvent(AnimationEditor, selectPage8), KEY_9, 0);
|
|
|
|
|
|
|
|
addAction(ACTION_SWIMLEFT, KEY_J, -1);
|
|
addAction(ACTION_SWIMRIGHT, KEY_K, -1);
|
|
addAction(ACTION_SWIMUP, KEY_UP, -1);
|
|
addAction(ACTION_SWIMDOWN, KEY_DOWN, -1);
|
|
|
|
const float yoffs = -55;
|
|
const float smallw = 50;
|
|
const float gap = 5;
|
|
|
|
Quad *back = new Quad;
|
|
{
|
|
back->color = 0;
|
|
back->setWidthHeight(800, 600);
|
|
back->position = Vector(400,300, -0.2f);
|
|
}
|
|
addRenderObject(back, LR_BACKDROP);
|
|
|
|
timelineTicks = new TimelineTickRender;
|
|
timelineTicks->position = Vector(0, TIMELINE_CENTER_Y);
|
|
addRenderObject(timelineTicks, LR_BACKGROUND);
|
|
|
|
bgGrad = new Gradient;
|
|
bgGrad->scale = Vector(800, 600);
|
|
bgGrad->position = Vector(400,300);
|
|
bgGrad->makeVertical(Vector(0.4f, 0.4f, 0.4f), Vector(0.8f, 0.8f, 0.8f));
|
|
addRenderObject(bgGrad, LR_BACKDROP);
|
|
|
|
DebugButton *a = new DebugButton(0, 0, 150);
|
|
a->position = Vector(10, 70+yoffs);
|
|
a->label->setText("prevKey (LEFT)");
|
|
a->event.set(MakeFunctionEvent(AnimationEditor, prevKey));
|
|
addRenderObject(a, LR_HUD);
|
|
|
|
DebugButton *a2 = new DebugButton(0, 0, 150);
|
|
a2->position = Vector(10, 100+yoffs);
|
|
a2->label->setText("nextKey (RIGHT)");
|
|
a2->event.set(MakeFunctionEvent(AnimationEditor, nextKey));
|
|
addRenderObject(a2, LR_HUD);
|
|
|
|
DebugButton *a3 = new DebugButton(0, 0, 150);
|
|
a3->position = Vector(10, 130+yoffs);
|
|
a3->label->setText("cloneKey");
|
|
a3->event.set(MakeFunctionEvent(AnimationEditor, newKey));
|
|
addRenderObject(a3, LR_HUD);
|
|
|
|
DebugButton *animate = new DebugButton(0, 0, 150);
|
|
animate->position = Vector(10, 200+yoffs);
|
|
animate->label->setText("animate (ENTER)");
|
|
animate->event.set(MakeFunctionEvent(AnimationEditor, animate));
|
|
addRenderObject(animate, LR_HUD);
|
|
|
|
DebugButton *stop = new DebugButton(0, 0, 150);
|
|
stop->position = Vector(10, 230+yoffs);
|
|
stop->label->setText("stop (S-ENTER)");
|
|
stop->event.set(MakeFunctionEvent(AnimationEditor, stop));
|
|
addRenderObject(stop, LR_HUD);
|
|
|
|
DebugButton *prevAnimation = new DebugButton(0, 0, 150);
|
|
prevAnimation->label->setText("prevAnim (PGUP)");
|
|
prevAnimation->position = Vector(10, 330+yoffs);
|
|
prevAnimation->event.set(MakeFunctionEvent(AnimationEditor, prevAnim));
|
|
addRenderObject(prevAnimation, LR_MENU);
|
|
|
|
DebugButton *nextAnimation = new DebugButton(0, 0, 150);
|
|
nextAnimation->label->setText("nextAnim (PGDN)");
|
|
nextAnimation->position = Vector(10, 360+yoffs);
|
|
nextAnimation->event.set(MakeFunctionEvent(AnimationEditor, nextAnim));
|
|
addRenderObject(nextAnimation, LR_MENU);
|
|
|
|
DebugButton *copyKey = new DebugButton(0, 0, 150);
|
|
copyKey->label->setText("copyKey");
|
|
copyKey->position = Vector(10, 390+yoffs);
|
|
copyKey->event.set(MakeFunctionEvent(AnimationEditor, copy));
|
|
addRenderObject(copyKey, LR_MENU);
|
|
|
|
DebugButton *pasteKey = new DebugButton(0, 0, 150);
|
|
pasteKey->label->setText("pasteKey");
|
|
pasteKey->position = Vector(10, 420+yoffs);
|
|
pasteKey->event.set(MakeFunctionEvent(AnimationEditor, paste));
|
|
addRenderObject(pasteKey, LR_MENU);
|
|
|
|
|
|
DebugButton *newAnim = new DebugButton(0, 0, 150);
|
|
newAnim->label->setText("NewAnim");
|
|
newAnim->position = Vector(640, 150+yoffs);
|
|
newAnim->event.set(MakeFunctionEvent(AnimationEditor, newAnim));
|
|
addRenderObject(newAnim, LR_MENU);
|
|
|
|
DebugButton *tm = new DebugButton(0, 0, 150);
|
|
tm->label->setText("SelMode (M)");
|
|
tm->position = Vector(640, 210+yoffs);
|
|
tm->event.set(MakeFunctionEvent(AnimationEditor, toggleMouseSelection));
|
|
addRenderObject(tm, LR_MENU);
|
|
|
|
DebugButton *rb = new DebugButton(0, 0, 150);
|
|
rb->label->setText("ShowJoints (B)");
|
|
rb->position = Vector(640, 240+yoffs);
|
|
rb->event.set(MakeFunctionEvent(AnimationEditor, toggleRenderBorders));
|
|
addRenderObject(rb, LR_MENU);
|
|
|
|
DebugButton *sa = new DebugButton(0, 0, 150);
|
|
sa->label->setText("ShowAll (A)");
|
|
sa->position = Vector(640, 270+yoffs);
|
|
sa->event.set(MakeFunctionEvent(AnimationEditor, showAllBones));
|
|
addRenderObject(sa, LR_MENU);
|
|
|
|
DebugButton *a4 = new DebugButton(0, 0, 150);
|
|
a4->label->setText("deleteKey");
|
|
a4->position = Vector(640, 340+yoffs);
|
|
a4->event.set(MakeFunctionEvent(AnimationEditor, deleteKey));
|
|
addRenderObject(a4, LR_MENU);
|
|
|
|
unitsize = new DebugFont(10, "Unitsize");
|
|
unitsize->position = Vector(650, 400+yoffs);
|
|
addRenderObject(unitsize, LR_MENU);
|
|
|
|
DebugButton *unitdown = new DebugButton(0, 0, 70);
|
|
unitdown->label->setText("Down");
|
|
unitdown->position = Vector(640, 420+yoffs);
|
|
unitdown->event.set(MakeFunctionEvent(AnimationEditor, decrTimelineUnit));
|
|
addRenderObject(unitdown, LR_MENU);
|
|
|
|
DebugButton *unitup = new DebugButton(0, 0, 70);
|
|
unitup->label->setText("Up");
|
|
unitup->position = Vector(640+80, 420+yoffs);
|
|
unitup->event.set(MakeFunctionEvent(AnimationEditor, incrTimelineUnit));
|
|
addRenderObject(unitup, LR_MENU);
|
|
|
|
gridsize = new DebugFont(10, "Gridsize");
|
|
gridsize->position = Vector(650, 450+yoffs);
|
|
addRenderObject(gridsize, LR_MENU);
|
|
|
|
DebugButton *griddown = new DebugButton(0, 0, 70);
|
|
griddown->label->setText("Down");
|
|
griddown->position = Vector(640, 470+yoffs);
|
|
griddown->event.set(MakeFunctionEvent(AnimationEditor, decrTimelineGrid));
|
|
addRenderObject(griddown, LR_MENU);
|
|
|
|
DebugButton *gridup = new DebugButton(0, 0, 70);
|
|
gridup->label->setText("Up");
|
|
gridup->position = Vector(640+80, 470+yoffs);
|
|
gridup->event.set(MakeFunctionEvent(AnimationEditor, incrTimelineGrid));
|
|
addRenderObject(gridup, LR_MENU);
|
|
|
|
DebugButton *save = new DebugButton(0, 0, 150 - smallw - gap);
|
|
save->label->setText("Save");
|
|
save->position = Vector(640, 100+yoffs);
|
|
save->event.set(MakeFunctionEvent(AnimationEditor, saveFile));
|
|
addRenderObject(save, LR_MENU);
|
|
|
|
DebugButton *saveall = new DebugButton(0, 0, smallw);
|
|
saveall->label->setText("All");
|
|
saveall->position = save->position + 150/2 + smallw/2 + gap;
|
|
saveall->event.set(MakeFunctionEvent(AnimationEditor, saveAll));
|
|
addRenderObject(saveall, LR_MENU);
|
|
|
|
DebugButton *load = new DebugButton(0, 0, 150 - smallw - gap);
|
|
load->label->setText("Reload");
|
|
load->position = Vector(640, 70+yoffs);
|
|
load->event.set(MakeFunctionEvent(AnimationEditor, reloadFile));
|
|
addRenderObject(load, LR_MENU);
|
|
|
|
DebugButton *loadall = new DebugButton(0, 0, smallw);
|
|
loadall->label->setText("All");
|
|
loadall->position = load->position + 150/2 + smallw/2 + gap;
|
|
loadall->event.set(MakeFunctionEvent(AnimationEditor, reloadAll));
|
|
addRenderObject(loadall, LR_MENU);
|
|
|
|
DebugButton *reverseAnim = new DebugButton(0, 0, 150);
|
|
reverseAnim->label->setText("reverseAnim");
|
|
reverseAnim->position = Vector(10, 480+yoffs);
|
|
reverseAnim->event.set(MakeFunctionEvent(AnimationEditor, reverseAnim));
|
|
addRenderObject(reverseAnim, LR_MENU);
|
|
|
|
DebugButton *bAssist = new DebugButton(0, 0, 150);
|
|
bAssist->position = Vector(10, 510+yoffs);
|
|
bAssist->event.set(MakeFunctionEvent(AnimationEditor, toggleSplineMode));
|
|
addRenderObject(bAssist, LR_MENU);
|
|
bSplineAssist = bAssist;
|
|
|
|
rect = new Quad("black", Vector(400,300+yoffs));
|
|
rect->alphaMod = 0.2f;
|
|
rect->renderBorder = true;
|
|
rect->renderCenter = false;
|
|
rect->borderAlpha = 5; // HACK to compensate alphaMod
|
|
rect->renderBorderColor = Vector(1,1,1);
|
|
rect->setWidthHeight(400,400);
|
|
addRenderObject(rect, LR_MENU);
|
|
|
|
text = new DebugFont();
|
|
text->position = Vector(200,90+yoffs);
|
|
text->setFontSize(6);
|
|
addRenderObject(text, LR_HUD);
|
|
|
|
text2 = new DebugFont();
|
|
text2->position = Vector(200,505+yoffs);
|
|
text2->setFontSize(6);
|
|
addRenderObject(text2, LR_HUD);
|
|
|
|
toptext = new DebugFont();
|
|
toptext->position = Vector(200,90-20+yoffs);
|
|
toptext->setFontSize(6);
|
|
addRenderObject(toptext, LR_HUD);
|
|
|
|
btmtext = new DebugFont();
|
|
btmtext->position = Vector(200,515+yoffs);
|
|
btmtext->setFontSize(6);
|
|
addRenderObject(btmtext, LR_HUD);
|
|
|
|
dsq->overlay->alpha.interpolateTo(0, 0.5f);
|
|
|
|
rebuildKeyframeWidgets();
|
|
|
|
dsq->resetTimer();
|
|
|
|
dsq->toggleCursor(true, 0.1f);
|
|
|
|
updateTimelineGrid();
|
|
updateTimelineUnit();
|
|
updateButtonLabels();
|
|
}
|
|
|
|
// FIXME: undo should be global?
|
|
|
|
void AnimationEditor::pushUndo()
|
|
{
|
|
std::deque<SkeletalSprite>& undoHistory = pages[curPage].undoHistory;
|
|
undoHistory.push_back(SkeletalSprite());
|
|
undoHistory.back().animations = pages[curPage].editSprite.animations;
|
|
|
|
if(undoHistory.size() > 50)
|
|
undoHistory.pop_front();
|
|
|
|
pages[curPage].undoEntry = undoHistory.size()-1;
|
|
}
|
|
|
|
void AnimationEditor::undo()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (core->getCtrlState())
|
|
{
|
|
std::deque<SkeletalSprite>& undoHistory = pages[curPage].undoHistory;
|
|
size_t& undoEntry = pages[curPage].undoEntry;
|
|
if (undoEntry < undoHistory.size())
|
|
{
|
|
getCurrentPageSprite()->animations = undoHistory[undoEntry].animations;
|
|
if(undoEntry > 0) {
|
|
undoEntry--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::redo()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (core->getCtrlState())
|
|
{
|
|
std::deque<SkeletalSprite>& undoHistory = pages[curPage].undoHistory;
|
|
size_t& undoEntry = pages[curPage].undoEntry;
|
|
|
|
undoEntry++;
|
|
if (undoEntry < undoHistory.size())
|
|
{
|
|
getCurrentPageSprite()->animations = undoHistory[undoEntry].animations;
|
|
}
|
|
else
|
|
{
|
|
undoEntry --;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::action(int id, int state, int source, InputDevice device)
|
|
{
|
|
if (editingBone && state)
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (id == ACTION_BONELEFT)
|
|
{
|
|
editingBone->position.x--;
|
|
applyTranslation();
|
|
}
|
|
if (id == ACTION_BONERIGHT)
|
|
{
|
|
editingBone->position.x++;
|
|
applyTranslation();
|
|
}
|
|
if (id == ACTION_BONEUP)
|
|
{
|
|
editingBone->position.y--;
|
|
applyTranslation();
|
|
}
|
|
if (id == ACTION_BONEDOWN)
|
|
{
|
|
editingBone->position.y++;
|
|
applyTranslation();
|
|
}
|
|
}
|
|
}
|
|
|
|
const float ANIM_EDIT_ZOOM = 0.5;
|
|
void AnimationEditor::zoomOut()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
core->globalScale -= Vector(ANIM_EDIT_ZOOM, ANIM_EDIT_ZOOM);
|
|
core->globalScaleChanged();
|
|
}
|
|
|
|
void AnimationEditor::zoomIn()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
core->globalScale += Vector(ANIM_EDIT_ZOOM, ANIM_EDIT_ZOOM);
|
|
core->globalScaleChanged();
|
|
}
|
|
|
|
void AnimationEditor::reorderKeys()
|
|
{
|
|
getCurrentPageAnimation()->reorderKeyframes();
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
|
|
void AnimationEditor::rebuildKeyframeWidgets()
|
|
{
|
|
Animation *a = getCurrentPageAnimation();
|
|
size_t n = a ? a->getNumKeyframes() : 0;
|
|
pages[curPage].timeline->resizeKeyframes(n);
|
|
}
|
|
|
|
void AnimationEditor::removeState()
|
|
{
|
|
delete [] pages;
|
|
pages = NULL;
|
|
StateObject::removeState();
|
|
core->cameraPos = Vector(0,0);
|
|
}
|
|
|
|
void AnimationEditor::toggleMouseSelection()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (editingBone)
|
|
{
|
|
editingBone->color = Vector(1,1,1);
|
|
}
|
|
|
|
mouseSelection = !mouseSelection;
|
|
}
|
|
|
|
void AnimationEditor::moveBoneStripPoint(const Vector &mov)
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
Bone *sel = editingBone;
|
|
if (sel)
|
|
{
|
|
BoneKeyframe *b = editingBoneSprite->getCurrentAnimation()->getKeyframe(currentKey)->getBoneKeyframe(sel->boneIdx);
|
|
if (b)
|
|
{
|
|
if (!sel->changeStrip.empty())
|
|
{
|
|
if (b->grid.size() < sel->changeStrip.size())
|
|
{
|
|
b->grid.resize(sel->changeStrip.size());
|
|
}
|
|
|
|
Vector v = sel->changeStrip[selectedStripPoint] + mov*0.006f;
|
|
sel->changeStrip[selectedStripPoint] = v;
|
|
b->grid[selectedStripPoint] = v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::update(float dt)
|
|
{
|
|
StateObject::update(dt);
|
|
|
|
const float tltime = getMouseTimelineTime();
|
|
|
|
{
|
|
{
|
|
std::ostringstream os;
|
|
for(int i = 0; i < NumPages; ++i)
|
|
{
|
|
if(curPage == i)
|
|
os << "[";
|
|
else
|
|
os << " ";
|
|
os << (i + 1);
|
|
if(curPage == i)
|
|
os << "]";
|
|
else
|
|
os << " ";
|
|
}
|
|
toptext->setText(os.str());
|
|
}
|
|
|
|
|
|
int pg = editingBonePage >= 0 ? editingBonePage : curPage;
|
|
|
|
std::ostringstream os;
|
|
os << "page " << (pg + 1) << " " << pages[pg].editingFile;
|
|
if(Animation *a = getPageSprite(pg)->getCurrentAnimationOrNull())
|
|
{
|
|
os << " anim[" << a->name << "] ";
|
|
os << "currentKey " << currentKey;
|
|
SkeletalKeyframe *k = a->getKeyframe(currentKey);
|
|
if (k)
|
|
{
|
|
os << " keyTime: " << k->t;
|
|
}
|
|
}
|
|
|
|
Vector ebdata;
|
|
int pass = 0;
|
|
int origpass = 0;
|
|
|
|
if (editingBone)
|
|
{
|
|
os << " bone: " << editingBone->name << " [idx " << editingBone->boneIdx << "]";
|
|
ebdata.x = editingBone->position.x;
|
|
ebdata.y = editingBone->position.y;
|
|
ebdata.z = editingBone->rotation.z;
|
|
pass = editingBone->getRenderPass();
|
|
origpass = editingBone->originalRenderPass;
|
|
}
|
|
text->setText(os.str());
|
|
|
|
char t2buf[128];
|
|
sprintf(t2buf, "Bone x: %.3f, y: %.3f, rot: %.3f strip: %u pass: %d (%d)", ebdata.x, ebdata.y, ebdata.z, (unsigned)selectedStripPoint, pass, origpass);
|
|
text2->setText(t2buf);
|
|
|
|
|
|
const float t = getAnimTime();
|
|
if(tltime >= 0)
|
|
sprintf(t2buf, "t: %.4f, mouse at %.4f", t, tltime);
|
|
else
|
|
sprintf(t2buf, "t: %.4f", t);
|
|
btmtext->setText(t2buf);
|
|
}
|
|
|
|
if (editMode == AE_STRIP)
|
|
{
|
|
if (isActing(ACTION_SWIMLEFT, -1))
|
|
moveBoneStripPoint(Vector(-dt, 0));
|
|
if (isActing(ACTION_SWIMRIGHT, -1))
|
|
moveBoneStripPoint(Vector(dt, 0));
|
|
|
|
if (isActing(ACTION_SWIMUP, -1))
|
|
moveBoneStripPoint(Vector(0, -dt));
|
|
if (isActing(ACTION_SWIMDOWN, -1))
|
|
moveBoneStripPoint(Vector(0, dt));
|
|
}
|
|
|
|
const bool ctrlPressed = core->getCtrlState();
|
|
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
pages[i].showCenter(ctrlPressed);
|
|
|
|
RenderObject *ctrlSprite; // what moving the mouse is currently controlling
|
|
if(splinegrid)
|
|
ctrlSprite = splinegrid;
|
|
else
|
|
ctrlSprite = ctrlPressed ? (RenderObject*)getSelectedPageSprite() : (RenderObject*)spriteRoot;
|
|
|
|
if (core->mouse.buttons.middle)
|
|
{
|
|
ctrlSprite->position += core->mouse.change;
|
|
}
|
|
|
|
float spd = 1.0f;
|
|
if (core->mouse.scrollWheelChange < 0)
|
|
{
|
|
if(splinegrid && core->getShiftState())
|
|
splinegrid->setPointScale(std::max(splinegrid->getPointScale() / 1.12f, 0.05f));
|
|
else
|
|
ctrlSprite->scale.x /= 1.12f;
|
|
}
|
|
else if (core->mouse.scrollWheelChange > 0)
|
|
{
|
|
if(splinegrid && core->getShiftState())
|
|
splinegrid->setPointScale(splinegrid->getPointScale() * 1.12f);
|
|
else
|
|
ctrlSprite->scale.x *= 1.12f;
|
|
}
|
|
if (core->getKeyState(KEY_PGDN) && core->getShiftState())
|
|
{
|
|
ctrlSprite->scale.x /= (1 + spd*dt);
|
|
}
|
|
if (core->getKeyState(KEY_PGUP) && core->getShiftState())
|
|
{
|
|
ctrlSprite->scale.x *= (1 + spd*dt);
|
|
}
|
|
if (ctrlSprite->scale.x < 0.05f)
|
|
{
|
|
ctrlSprite->scale.x = 0.05f;
|
|
}
|
|
ctrlSprite->scale.y = ctrlSprite->scale.x;
|
|
|
|
if (editMode == AE_SELECT)
|
|
{
|
|
updateEditingBone();
|
|
}
|
|
if (editingBone)
|
|
{
|
|
float m = 0.2f;
|
|
if(core->getKeyState(KEY_NUMPADSLASH))
|
|
{
|
|
editingBone->originalScale /= (1 + m*dt);
|
|
editingBone->scale = editingBone->originalScale;
|
|
}
|
|
if(core->getKeyState(KEY_NUMPADSTAR))
|
|
{
|
|
editingBone->originalScale *= (1 + m*dt);
|
|
editingBone->scale = editingBone->originalScale;
|
|
}
|
|
|
|
if (editMode == AE_EDITING_MOVE)
|
|
{
|
|
Vector add = core->mouse.change;
|
|
// adjust relative mouse movement to absolute bone rotation
|
|
if (editingBone->getParent())
|
|
{
|
|
float rot = editingBone->getParent()->getAbsoluteRotation().z;
|
|
if (editingBone->getParent()->isfhr())
|
|
{
|
|
add.x = -add.x;
|
|
add.rotate2D360(rot);
|
|
}
|
|
else
|
|
add.rotate2D360(360 - rot);
|
|
}
|
|
editingBone->position += add;
|
|
constrainMouse();
|
|
}
|
|
else if (editMode == AE_EDITING_ROT)
|
|
{
|
|
if (editingBone->getParent() && editingBone->getParent()->isfhr())
|
|
editingBone->rotation.z = rotOffset + (cursorOffset.x - core->mouse.position.x)/2;
|
|
else
|
|
editingBone->rotation.z = rotOffset + (core->mouse.position.x - cursorOffset.x)/2;
|
|
constrainMouse();
|
|
}
|
|
}
|
|
|
|
bool doUpdateBones = false;
|
|
if (editMode == AE_SELECT)
|
|
{
|
|
float t = 0;
|
|
bool hastime = false;
|
|
bool mod = false;
|
|
if(core->mouse.buttons.right)
|
|
{
|
|
t = tltime;
|
|
hastime = t >= 0;
|
|
mod = true;
|
|
}
|
|
if (!hastime && !isAnimating())
|
|
{
|
|
SkeletalSprite *editSprite = getCurrentPageSprite();
|
|
if(Animation *a = editSprite->getCurrentAnimationOrNull())
|
|
{
|
|
SkeletalKeyframe *k = a->getKeyframe(currentKey);
|
|
if (k)
|
|
{
|
|
t = k->t;
|
|
hastime = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(hastime)
|
|
{
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
{
|
|
SkeletalSprite *spr = getPageSprite(i);
|
|
if(Animation *a = spr->getCurrentAnimationOrNull())
|
|
{
|
|
float len = a->getAnimationLength();
|
|
float tt = t;
|
|
if(len && mod)
|
|
tt = fmodf(tt, len);
|
|
spr->setTime(tt);
|
|
}
|
|
}
|
|
doUpdateBones = true;
|
|
}
|
|
}
|
|
|
|
if(splinegrid && editingBone && editMode == AE_SPLINE && splinegrid->wasModified)
|
|
{
|
|
applySplineGridToBone();
|
|
splinegrid->wasModified = false;
|
|
}
|
|
|
|
if(doUpdateBones)
|
|
{
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
{
|
|
SkeletalSprite *spr = getPageSprite(i);
|
|
if(spr->isLoaded() && !spr->isAnimating())
|
|
spr->updateBones();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void AnimationEditor::_copyKey()
|
|
{
|
|
if (core->getCtrlState())
|
|
copy();
|
|
}
|
|
|
|
void AnimationEditor::copy()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
copyBuffer = *getCurrentPageAnimation()->getKeyframe(currentKey);
|
|
}
|
|
|
|
void AnimationEditor::_pasteKey()
|
|
{
|
|
if (core->getCtrlState())
|
|
paste();
|
|
}
|
|
|
|
void AnimationEditor::paste()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (core->getCtrlState())
|
|
{
|
|
SkeletalKeyframe *k = getCurrentPageAnimation()->getKeyframe(currentKey);
|
|
float time = k->t;
|
|
*k = copyBuffer;
|
|
k->t = time;
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::nextKey()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (editMode == AE_STRIP)
|
|
{
|
|
selectedStripPoint++;
|
|
if (selectedStripPoint >= editingBone->changeStrip.size()
|
|
&& selectedStripPoint > 0)
|
|
selectedStripPoint --;
|
|
}
|
|
else
|
|
{
|
|
SkeletalSprite *editSprite = getCurrentPageSprite();
|
|
|
|
if (core->getCtrlState())
|
|
{
|
|
const std::vector<KeyframeWidget*>& keyframeWidgets = pages[curPage].timeline->getKeyframes();
|
|
for (size_t i = 0; i < keyframeWidgets.size(); i++)
|
|
{
|
|
keyframeWidgets[i]->shiftLeft();
|
|
}
|
|
}
|
|
else if(Animation *a = editSprite->getCurrentAnimation())
|
|
{
|
|
currentKey++;
|
|
SkeletalKeyframe *k = a->getKeyframe(currentKey);
|
|
if (k)
|
|
editSprite->setTime(k->t);
|
|
else
|
|
currentKey --;
|
|
|
|
onKeyframeChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::prevKey()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (editMode == AE_STRIP)
|
|
{
|
|
if(selectedStripPoint > 0)
|
|
selectedStripPoint--;
|
|
}
|
|
else
|
|
{
|
|
SkeletalSprite *editSprite = getCurrentPageSprite();
|
|
|
|
if (core->getCtrlState())
|
|
{
|
|
const std::vector<KeyframeWidget*>& keyframeWidgets = pages[curPage].timeline->getKeyframes();
|
|
for (size_t i = 0; i < keyframeWidgets.size(); i++)
|
|
{
|
|
keyframeWidgets[i]->shiftRight();
|
|
}
|
|
}
|
|
else if(Animation *a = editSprite->getCurrentAnimation())
|
|
{
|
|
if (currentKey > 0)
|
|
{
|
|
currentKey --;
|
|
SkeletalKeyframe *k = a->getKeyframe(currentKey);
|
|
if (k)
|
|
editSprite->setTime(k->t);
|
|
|
|
onKeyframeChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void AnimationEditor::newAnim()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
std::string name = dsq->getUserInputString("NewAnimName", "");
|
|
if (!name.empty())
|
|
{
|
|
Animation anim = *getCurrentPageAnimation();
|
|
anim.name = name;
|
|
SkeletalSprite *spr = getCurrentPageSprite();
|
|
spr->animations.push_back(anim);
|
|
spr->lastAnimation();
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::newKey()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
getCurrentPageAnimation()->cloneKey(currentKey, TIMELINE_UNIT);
|
|
currentKey++;
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
|
|
void AnimationEditor::_stopExtraEditModes()
|
|
{
|
|
selectedStripPoint = 0;
|
|
editMode = AE_SELECT;
|
|
bgGrad->makeVertical(Vector(0.4f, 0.4f, 0.4f), Vector(0.8f, 0.8f, 0.8f));
|
|
|
|
if(splinegrid)
|
|
{
|
|
splinegrid->safeKill();
|
|
splinegrid = NULL;
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::editStripKey()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (editMode == AE_SPLINE || editMode == AE_STRIP)
|
|
{
|
|
_stopExtraEditModes();
|
|
}
|
|
else
|
|
{
|
|
if(editingBone && editingBone->getGrid())
|
|
{
|
|
DynamicRenderGrid *grid = editingBone->getGrid();
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
|
|
|
|
if(interp)
|
|
{
|
|
editMode = AE_SPLINE;
|
|
bgGrad->makeVertical(Vector(0.4f, 0.6f, 0.4f), Vector(0.8f, 1, 0.8f));
|
|
|
|
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
const size_t totalcp = interp->bsp.ctrlX() * interp->bsp.ctrlY();
|
|
const bool reset = bk->controlpoints.empty();
|
|
bk->controlpoints.resize(totalcp);
|
|
assert(!splinegrid);
|
|
|
|
splinegrid = new SplineGrid();
|
|
DynamicRenderGrid *rgrid = splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), grid->width(), grid->height(), interp->bsp.degX(), interp->bsp.degY());
|
|
rgrid->setDrawOrder(grid->getDrawOrder());
|
|
splinegrid->setTexture(editingBone->texture->name);
|
|
splinegrid->setWidthHeight(editingBone->width, editingBone->height);
|
|
splinegrid->position = Vector(400, 300);
|
|
splinegrid->setAssist(assistedSplineEdit);
|
|
|
|
if(reset)
|
|
splinegrid->resetControlPoints();
|
|
else
|
|
splinegrid->importKeyframe(bk);
|
|
|
|
addRenderObject(splinegrid, LR_PARTICLES_TOP);
|
|
}
|
|
else
|
|
{
|
|
editMode = AE_STRIP;
|
|
bgGrad->makeVertical(Vector(0.4f, 0.4f, 0.6f), Vector(0.8f, 0.8f, 1));
|
|
}
|
|
|
|
}
|
|
else if(editingBone)
|
|
{
|
|
notify("Bone has no grid, cannot edit grid");
|
|
dsq->sound->playSfx("denied");
|
|
}
|
|
else
|
|
{
|
|
notify("No bone selected for grid edit mode");
|
|
dsq->sound->playSfx("denied");
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::deleteKey()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (currentKey > 0)
|
|
{
|
|
getCurrentPageAnimation()->deleteKey(currentKey);
|
|
currentKey --;
|
|
}
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
|
|
void AnimationEditor::animate()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
if(pages[i].editSprite.isLoaded())
|
|
pages[i].editSprite.playCurrentAnimation(-1);
|
|
}
|
|
|
|
void AnimationEditor::stop()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
if(pages[i].editSprite.isLoaded())
|
|
pages[i].editSprite.stopAnimation();
|
|
}
|
|
|
|
void AnimationEditor::animateOrStop()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (core->getShiftState())
|
|
stop();
|
|
else
|
|
animate();
|
|
}
|
|
|
|
void AnimationEditor::lmbd()
|
|
{
|
|
if(editMode == AE_SELECT)
|
|
{
|
|
pushUndo();
|
|
updateEditingBone();
|
|
if (editingBone && isMouseInRect() && !editingBoneSprite->isAnimating())
|
|
{
|
|
cursorOffset = editingBone->getWorldPosition() - core->mouse.position;
|
|
editMode = AE_EDITING_MOVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::applyTranslation()
|
|
{
|
|
if (editingBone)
|
|
{
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
if (!core->getShiftState())
|
|
{
|
|
// one bone mode
|
|
BoneKeyframe *b = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->x = editingBone->position.x;
|
|
b->y = editingBone->position.y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BoneKeyframe *bcur = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (bcur)
|
|
{
|
|
int xdiff = editingBone->position.x - bcur->x;
|
|
int ydiff = editingBone->position.y - bcur->y;
|
|
if(!core->getCtrlState())
|
|
{
|
|
// all bones in one anim mode
|
|
for (size_t i = 0; i < a->getNumKeyframes(); ++i)
|
|
{
|
|
BoneKeyframe *b = a->getKeyframe(i)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->x += xdiff;
|
|
b->y += ydiff;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// all bones in all anims mode
|
|
for (size_t a = 0; a < editingBoneSprite->animations.size(); ++a)
|
|
{
|
|
for (size_t i = 0; i < editingBoneSprite->animations[a].getNumKeyframes(); ++i)
|
|
{
|
|
BoneKeyframe *b = editingBoneSprite->animations[a].getKeyframe(i)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->x += xdiff;
|
|
b->y += ydiff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::applyRotation()
|
|
{
|
|
if(editingBone)
|
|
{
|
|
BoneKeyframe *b = editingBoneSprite->getCurrentAnimation()->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot = editingBone->rotation.z = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::lmbu()
|
|
{
|
|
if(editMode == AE_EDITING_MOVE)
|
|
{
|
|
applyTranslation();
|
|
editMode = AE_SELECT;
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::rmbd()
|
|
{
|
|
if(editMode == AE_SELECT)
|
|
{
|
|
updateEditingBone();
|
|
if (editingBone && !editingBoneSprite->isAnimating())
|
|
{
|
|
pushUndo();
|
|
cursorOffset = core->mouse.position;
|
|
rotOffset = editingBone->rotation.z;
|
|
editMode = AE_EDITING_ROT;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::clearRot()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
updateEditingBone();
|
|
if (editingBone)
|
|
{
|
|
if(core->getCtrlState())
|
|
core->texmgr.load(editingBone->texture->name, TextureMgr::OVERWRITE);
|
|
else if(splinegrid)
|
|
splinegrid->resetControlPoints();
|
|
else
|
|
applyRotation();
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::flipRot()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
updateEditingBone();
|
|
if (editingBone)
|
|
{
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
if (!core->getShiftState())
|
|
{
|
|
BoneKeyframe *b = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot = -b->rot;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BoneKeyframe *bcur = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (bcur)
|
|
{
|
|
if (!core->getCtrlState())
|
|
{
|
|
for (size_t i = 0; i < a->getNumKeyframes(); ++i)
|
|
{
|
|
BoneKeyframe *b = a->getKeyframe(i)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot = -b->rot;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// all bones in all anims mode
|
|
for (size_t a = 0; a < editingBoneSprite->animations.size(); ++a)
|
|
{
|
|
for (size_t i = 0; i < editingBoneSprite->animations[a].getNumKeyframes(); ++i)
|
|
{
|
|
BoneKeyframe *b = editingBoneSprite->animations[a].getKeyframe(i)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot = -b->rot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::clearPos()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
updateEditingBone();
|
|
if (editingBone)
|
|
{
|
|
BoneKeyframe *b = editingBoneSprite->getCurrentAnimation()->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
editingBone->position = Vector(0,0);
|
|
b->x = b->y = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::toggleHideBone()
|
|
{
|
|
if (!dsq->isNested())
|
|
{
|
|
updateEditingBone();
|
|
if (editingBone)
|
|
{
|
|
editingBone->renderQuad = !editingBone->renderQuad;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::rmbu()
|
|
{
|
|
if(editMode != AE_EDITING_ROT)
|
|
return;
|
|
|
|
editMode = AE_SELECT;
|
|
if (editingBone)
|
|
{
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
if (!core->getShiftState())
|
|
{
|
|
// one bone mode
|
|
BoneKeyframe *b = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot = int(editingBone->rotation.z);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BoneKeyframe *bcur = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (bcur)
|
|
{
|
|
int rotdiff = editingBone->rotation.z - bcur->rot;
|
|
if (!core->getCtrlState())
|
|
{
|
|
for (size_t i = 0; i < a->getNumKeyframes(); ++i)
|
|
{
|
|
BoneKeyframe *b = a->getKeyframe(i)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot += rotdiff;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// all bones in all anims mode
|
|
for (size_t a = 0; a < editingBoneSprite->animations.size(); ++a)
|
|
{
|
|
for (size_t i = 0; i < editingBoneSprite->animations[a].getNumKeyframes(); ++i)
|
|
{
|
|
BoneKeyframe *b = editingBoneSprite->animations[a].getKeyframe(i)->getBoneKeyframe(editingBone->boneIdx);
|
|
if (b)
|
|
{
|
|
b->rot += rotdiff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::mmbd()
|
|
{
|
|
|
|
|
|
}
|
|
|
|
void AnimationEditor::cloneBoneAhead()
|
|
{
|
|
updateEditingBone();
|
|
if (editingBone && currentKey >= 0)
|
|
{
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
SkeletalKeyframe *s1 = a->getKeyframe(currentKey);
|
|
BoneKeyframe *b1 = 0;
|
|
if (s1)
|
|
b1 = s1->getBoneKeyframe(editingBone->boneIdx);
|
|
|
|
SkeletalKeyframe *s2 = a->getKeyframe(currentKey+1);
|
|
BoneKeyframe *b2 = 0;
|
|
if (s2)
|
|
b2 = s2->getBoneKeyframe(editingBone->boneIdx);
|
|
|
|
if (b1 && b2)
|
|
{
|
|
b2->x = b1->x;
|
|
b2->y = b1->y;
|
|
b2->rot = b1->rot;
|
|
b2->grid = b1->grid;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AnimationEditor::savePage(size_t pg)
|
|
{
|
|
return getPageSprite(pg)->saveSkeletal(pages[pg].editingFile);
|
|
}
|
|
|
|
void AnimationEditor::saveFile()
|
|
{
|
|
if(!getPageSprite(curPage)->isLoaded())
|
|
{
|
|
notify("Nothing to save");
|
|
return;
|
|
}
|
|
|
|
if(savePage(curPage))
|
|
notify("Saved anim: " + pages[curPage].editingFile);
|
|
else
|
|
notify("FAILED TO SAVE: " + pages[curPage].editingFile);
|
|
}
|
|
|
|
void AnimationEditor::saveAll()
|
|
{
|
|
bool ok = true;
|
|
std::ostringstream os;
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
if(getPageSprite(i)->isLoaded())
|
|
if(!savePage(i))
|
|
{
|
|
ok = false;
|
|
os << pages[i].editingFile << " ";
|
|
}
|
|
|
|
if(ok)
|
|
notify("All saved");
|
|
else
|
|
notify("Failed to save: " + os.str());
|
|
}
|
|
|
|
void AnimationEditor::reloadPage(size_t pg)
|
|
{
|
|
loadFile(pg, pages[pg].editingFile.c_str());
|
|
}
|
|
|
|
void AnimationEditor::loadFile(size_t pg, const char* fn)
|
|
{
|
|
SkeletalSprite::clearCache();
|
|
editingBone = 0;
|
|
editingBoneIdx = -1;
|
|
pages[pg].clearUndoHistory();
|
|
//editSprite->position = Vector(0,0);
|
|
pages[pg].load(fn);
|
|
currentKey = 0;
|
|
rebuildKeyframeWidgets();
|
|
|
|
// disable strip edit mode if still active
|
|
_stopExtraEditModes();
|
|
|
|
onKeyframeChanged();
|
|
}
|
|
|
|
void AnimationEditor::reloadFile()
|
|
{
|
|
if(getPageSprite(curPage)->isLoaded())
|
|
reloadPage(curPage);
|
|
else
|
|
load(); // prompt what to load here
|
|
}
|
|
|
|
void AnimationEditor::reloadAll()
|
|
{
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
reloadPage(i);
|
|
}
|
|
|
|
void AnimationEditor::goToTitle()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
if (!dsq->returnToScene.empty())
|
|
game->transitionToScene(dsq->returnToScene);
|
|
else
|
|
dsq->title(false);
|
|
}
|
|
|
|
void AnimationEditor::quit()
|
|
{
|
|
core->quit();
|
|
}
|
|
|
|
void AnimationEditor::nextAnim()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
if (core->getShiftState()) return;
|
|
|
|
if(editMode == AE_SELECT)
|
|
{
|
|
getCurrentPageSprite()->nextAnimation();
|
|
currentKey = 0;
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::prevAnim()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
if (core->getShiftState()) return;
|
|
|
|
if(editMode == AE_SELECT)
|
|
{
|
|
getCurrentPageSprite()->prevAnimation();
|
|
currentKey = 0;
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::selectAnim()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
std::string name = dsq->getUserInputString("Select anim name:");
|
|
if (name.empty())
|
|
return;
|
|
|
|
if(getCurrentPageSprite()->selectAnimation(name.c_str()))
|
|
{
|
|
currentKey = 0;
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
else
|
|
notify("No such anim name");
|
|
}
|
|
|
|
void AnimationEditor::reverseAnim()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
Animation *a = getCurrentPageAnimation();
|
|
if (a)
|
|
{
|
|
debugLog("calling reverse anim");
|
|
a->reverse();
|
|
rebuildKeyframeWidgets();
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::flipH()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
RenderObject *ro = core->getCtrlState() ? (RenderObject*)getSelectedPageSprite() : (RenderObject*)spriteRoot;
|
|
ro->flipHorizontal();
|
|
|
|
const Vector red(1,0,0), white(1,1,1);
|
|
toptext->color = spriteRoot->isfh() ? red : white;
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
pages[i].timeline->label.color = pages[i].editSprite.isfhr() ? red : white;
|
|
}
|
|
|
|
void AnimationEditor::load()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
std::string file = dsq->getUserInputString("Enter anim file to load:");
|
|
if (file.empty()) return;
|
|
loadFile(curPage, file.c_str());
|
|
}
|
|
|
|
void AnimationEditor::loadSkin()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
std::string file = dsq->getUserInputString("Enter skin file to load:");
|
|
if (file.empty()) return;
|
|
|
|
|
|
SkeletalSprite::clearCache();
|
|
getCurrentPageSprite()->loadSkin(file);
|
|
}
|
|
|
|
void AnimationEditor::moveNextWidgets(float dt)
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
bool move = false;
|
|
const std::vector<KeyframeWidget*>& keyframeWidgets = pages[curPage].timeline->getKeyframes();
|
|
Animation *a = getCurrentPageAnimation();
|
|
for (size_t i = 0; i < keyframeWidgets.size(); i++)
|
|
{
|
|
KeyframeWidget *w = keyframeWidgets[i];
|
|
if (move)
|
|
{
|
|
a->getKeyframe(w->key)->t += dt;
|
|
}
|
|
else if (KeyframeWidget::movingWidget == w)
|
|
{
|
|
move = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void AnimationEditor::toggleRenderBorders()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
renderBorderMode = (RenderBorderMode)(renderBorderMode + 1);
|
|
if(renderBorderMode > RENDER_BORDER_ALL)
|
|
renderBorderMode = RENDER_BORDER_NONE;
|
|
updateRenderBorders();
|
|
}
|
|
|
|
void AnimationEditor::updateRenderBorders()
|
|
{
|
|
SkeletalSprite *editSprite = getCurrentPageSprite();
|
|
if (!editSprite)
|
|
return;
|
|
|
|
// reset
|
|
for (size_t i = 0; i < editSprite->bones.size(); ++i)
|
|
{
|
|
Bone *b = editSprite->bones[i];
|
|
b->renderBorder = false;
|
|
b->renderCenter = false;
|
|
b->borderAlpha = 0.8f;
|
|
b->renderBorderColor = Vector(1,1,1);
|
|
}
|
|
|
|
if(renderBorderMode == RENDER_BORDER_NONE)
|
|
return;
|
|
else if(Animation *a = editSprite->getCurrentAnimation())
|
|
{
|
|
for(size_t i = 0; i < a->interpolators.size(); ++i)
|
|
{
|
|
const BoneGridInterpolator& bgip = a->interpolators[i];
|
|
if(Bone *b = editSprite->getBoneByIdx(bgip.idx))
|
|
{
|
|
b->renderBorder = true;
|
|
b->renderCenter = true;
|
|
b->borderAlpha = 0.4f;
|
|
b->renderBorderColor = Vector(0.2f, 0.9f, 0.2f);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(renderBorderMode == RENDER_BORDER_ALL)
|
|
{
|
|
for (size_t i = 0; i < editSprite->bones.size(); ++i)
|
|
{
|
|
Bone *b = editSprite->bones[i];
|
|
b->renderBorder = true;
|
|
b->renderCenter = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pick the closest bone
|
|
void AnimationEditor::updateEditingBone()
|
|
{
|
|
if(editMode != AE_SELECT)
|
|
return;
|
|
|
|
if(!mouseSelection)
|
|
{
|
|
if(!editingBoneSprite)
|
|
editingBoneSprite = getCurrentPageSprite();
|
|
editingBonePage = curPage;
|
|
Bone *b = (size_t)editingBoneIdx < editingBoneSprite->bones.size() ? editingBoneSprite->bones[editingBoneIdx] : NULL; // this uses underflow at -1
|
|
if(b && b->selectable)
|
|
_selectBone(b);
|
|
else
|
|
selectNextBone();
|
|
return;
|
|
}
|
|
|
|
// When selecting with the mouse, pick the closest bone to the cursor, no matter the skeleton it belongs to
|
|
float mind;
|
|
Bone *nearest = NULL;
|
|
SkeletalSprite *nearestSpr = NULL;
|
|
const Vector& p = core->mouse.position;
|
|
int page = -1, idx = -1;
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
{
|
|
SkeletalSprite& spr = pages[i].editSprite;
|
|
if(spr.isLoaded())
|
|
{
|
|
int k = spr.findSelectableBoneIdxClosestTo(p, true);
|
|
if(k >= 0)
|
|
{
|
|
Bone *b = spr.bones[k];
|
|
const float d = (b->getWorldPosition() - p).getSquaredLength2D();
|
|
if(!nearest || d < mind)
|
|
{
|
|
mind = d;
|
|
nearest = b;
|
|
nearestSpr = &spr;
|
|
page = i;
|
|
idx = k;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
editingBoneSprite = nearestSpr;
|
|
editingBonePage = page;
|
|
editingBoneIdx = idx;
|
|
_selectBone(nearest);
|
|
}
|
|
|
|
void AnimationEditor::showAllBones()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
SkeletalSprite *spr = getSelectedPageSprite();
|
|
for (size_t i = 0; i < spr->bones.size(); ++i)
|
|
spr->bones[i]->renderQuad = true;
|
|
}
|
|
|
|
void AnimationEditor::incrTimelineUnit()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
TIMELINE_UNIT += TIMELINE_UNIT_STEP;
|
|
updateTimelineUnit();
|
|
}
|
|
|
|
void AnimationEditor::decrTimelineUnit()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
float t = TIMELINE_UNIT - TIMELINE_UNIT_STEP;
|
|
if (t >= TIMELINE_UNIT_STEP)
|
|
TIMELINE_UNIT = t;
|
|
updateTimelineUnit();
|
|
}
|
|
|
|
void AnimationEditor::updateTimelineUnit()
|
|
{
|
|
std::ostringstream os;
|
|
os << "Unit: " << TIMELINE_UNIT;
|
|
unitsize->setText(os.str());
|
|
}
|
|
|
|
void AnimationEditor::incrTimelineGrid()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
TIMELINE_GRIDSIZE++;
|
|
updateTimelineGrid();
|
|
}
|
|
|
|
void AnimationEditor::decrTimelineGrid()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
int t = TIMELINE_GRIDSIZE - 1;
|
|
if (t > 0)
|
|
TIMELINE_GRIDSIZE = t;
|
|
updateTimelineGrid();
|
|
}
|
|
|
|
void AnimationEditor::toggleSplineMode()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
assistedSplineEdit = !assistedSplineEdit;
|
|
updateButtonLabels();
|
|
if(splinegrid)
|
|
splinegrid->setAssist(assistedSplineEdit);
|
|
}
|
|
|
|
void AnimationEditor::updateButtonLabels()
|
|
{
|
|
{
|
|
std::ostringstream os;
|
|
os << "S.Assist (W)(" << (assistedSplineEdit ? "on" : "off") << ")";
|
|
bSplineAssist->label->setText(os.str());
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::toggleGradient()
|
|
{
|
|
if (dsq->isNested()) return;
|
|
|
|
bgGrad->alpha.x = float(bgGrad->alpha.x <= 0);
|
|
}
|
|
|
|
float AnimationEditor::getMouseTimelineTime() const
|
|
{
|
|
Vector m = core->mouse.position;
|
|
if(m.x > 0 && m.x < 800 && m.y > TIMELINE_CENTER_Y-TIMELINE_HEIGHT/2 && m.y < TIMELINE_CENTER_Y+TIMELINE_HEIGHT/2)
|
|
{
|
|
float t = (m.x - TIMELINE_X_OFFS) * TIMELINE_UNIT / TIMELINE_GRIDSIZE;
|
|
t = std::max(t, 0.0f);
|
|
return t;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Animation* AnimationEditor::getPageAnimation(size_t page) const
|
|
{
|
|
return pages[page].editSprite.getCurrentAnimation();
|
|
}
|
|
|
|
Animation* AnimationEditor::getCurrentPageAnimation() const
|
|
{
|
|
return getPageAnimation(curPage);
|
|
}
|
|
|
|
SkeletalSprite* AnimationEditor::getPageSprite(size_t page) const
|
|
{
|
|
return &pages[page].editSprite;
|
|
}
|
|
|
|
SkeletalSprite* AnimationEditor::getCurrentPageSprite() const
|
|
{
|
|
return getPageSprite(curPage);
|
|
}
|
|
|
|
SkeletalSprite * AnimationEditor::getSelectedPageSprite() const
|
|
{
|
|
return editingBoneSprite ? editingBoneSprite : getCurrentPageSprite();
|
|
}
|
|
|
|
bool AnimationEditor::isAnimating() const
|
|
{
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
if(getPageSprite(i)->isAnimating())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
float AnimationEditor::getAnimTime() const
|
|
{
|
|
SkeletalSprite *cur = getCurrentPageSprite();
|
|
if(cur->isLoaded() && cur->isAnimating())
|
|
return cur->getAnimationLayer(0)->timer;
|
|
|
|
for(size_t i = 0; i < NumPages; ++i)
|
|
{
|
|
SkeletalSprite *spr = getPageSprite(i);
|
|
if(spr->isLoaded() && spr->isAnimating())
|
|
return spr->getAnimationLayer(0)->timer;
|
|
}
|
|
|
|
if(Animation *a = getCurrentPageSprite()->getCurrentAnimationOrNull())
|
|
{
|
|
SkeletalKeyframe *k = a->getKeyframe(currentKey);
|
|
if(k)
|
|
return k->t;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void AnimationEditor::selectPage(unsigned page)
|
|
{
|
|
if(editMode != AE_SELECT)
|
|
return;
|
|
|
|
if(!mouseSelection)
|
|
{
|
|
editingBoneSprite = &pages[page].editSprite;
|
|
updateEditingBone();
|
|
}
|
|
|
|
curPage = page;
|
|
}
|
|
|
|
void AnimationEditor::updateTimelineGrid()
|
|
{
|
|
std::ostringstream os;
|
|
os << "Grid: " << TIMELINE_GRIDSIZE;
|
|
gridsize->setText(os.str());
|
|
}
|
|
|
|
void AnimationEditor::onKeyframeChanged()
|
|
{
|
|
applyBoneToSplineGrid();
|
|
|
|
updateRenderBorders(); // restore default state
|
|
}
|
|
|
|
void AnimationEditor::applyBoneToSplineGrid()
|
|
{
|
|
if(splinegrid && editingBone)
|
|
{
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
|
|
assert(bk->grid.size() == editingBone->getGrid()->linearsize());
|
|
splinegrid->importKeyframe(bk);
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::applySplineGridToBone()
|
|
{
|
|
if(splinegrid && editingBone)
|
|
{
|
|
Animation *a = editingBoneSprite->getCurrentAnimation();
|
|
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
|
|
assert(bk->grid.size() == editingBone->getGrid()->linearsize());
|
|
splinegrid->exportKeyframe(bk);
|
|
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
|
|
interp->updateGridAndBone(*bk, editingBone);
|
|
}
|
|
}
|
|
|
|
void AnimationEditor::_selectBone(Bone *b)
|
|
{
|
|
if(editingBone)
|
|
editingBone->color = Vector(1,1,1);
|
|
|
|
if (b)
|
|
b->color = mouseSelection ? Vector(1,0,0) : Vector(0.5,0.5,1);
|
|
else
|
|
{
|
|
editingBonePage = -1;
|
|
editingBoneSprite = NULL;
|
|
editingBoneIdx = -1;
|
|
}
|
|
|
|
editingBone = b;
|
|
}
|
|
|
|
void AnimationEditor::selectPrevBone()
|
|
{
|
|
if (dsq->isNested() || mouseSelection || editMode != AE_SELECT)
|
|
return;
|
|
|
|
SkeletalSprite *spr = editingBoneSprite ? editingBoneSprite : getCurrentPageSprite();
|
|
if(spr->bones.empty())
|
|
return;
|
|
|
|
size_t idx = editingBoneIdx;
|
|
Bone *b = NULL;
|
|
do
|
|
{
|
|
idx++;
|
|
if(idx == editingBoneIdx)
|
|
{
|
|
idx = -1;
|
|
break;
|
|
}
|
|
if (idx >= spr->bones.size())
|
|
idx = 0;
|
|
b = spr->bones[idx];
|
|
}
|
|
while (!b->selectable);
|
|
editingBoneIdx = idx;
|
|
editingBonePage = curPage;
|
|
editingBoneSprite = spr;
|
|
_selectBone(b);
|
|
}
|
|
|
|
void AnimationEditor::selectNextBone()
|
|
{
|
|
if (dsq->isNested() || mouseSelection || editMode != AE_SELECT)
|
|
return;
|
|
|
|
SkeletalSprite *spr = editingBoneSprite ? editingBoneSprite : getCurrentPageSprite();
|
|
if(spr->bones.empty())
|
|
return;
|
|
|
|
size_t idx = editingBoneIdx;
|
|
Bone *b = NULL;
|
|
do
|
|
{
|
|
idx--;
|
|
if(idx == editingBoneIdx)
|
|
{
|
|
idx = -1;
|
|
break;
|
|
}
|
|
if (idx >= spr->bones.size())
|
|
idx = spr->bones.size()-1;
|
|
b = spr->bones[idx];
|
|
}
|
|
while (!b->selectable);
|
|
editingBoneIdx = idx;
|
|
editingBonePage = curPage;
|
|
editingBoneSprite = spr;
|
|
_selectBone(b);
|
|
}
|