684 lines
16 KiB
C
684 lines
16 KiB
C
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <Emgui.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "Dialog.h"
|
|
#include "Editor.h"
|
|
#include "LoadSave.h"
|
|
#include "TrackView.h"
|
|
#include "rlog.h"
|
|
#include "minmax.h"
|
|
#include "TrackData.h"
|
|
#include "RemoteConnection.h"
|
|
#include "MinecraftiaFont.h"
|
|
#include "../../sync/sync.h"
|
|
#include "../../sync/base.h"
|
|
#include "../../sync/data.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if defined(EMGUI_MACOSX)
|
|
#define FONT_PATH "/Library/Fonts/"
|
|
#elif defined(EMGUI_WINDOWS)
|
|
#define FONT_PATH "C:\\Windows\\Fonts\\"
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct CopyEntry
|
|
{
|
|
int track;
|
|
struct track_key keyFrame;
|
|
} CopyEntry;
|
|
|
|
typedef struct CopyData
|
|
{
|
|
CopyEntry* entries;
|
|
int bufferWidth;
|
|
int bufferHeight;
|
|
int count;
|
|
|
|
} CopyData;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct EditorData
|
|
{
|
|
TrackViewInfo trackViewInfo;
|
|
TrackData trackData;
|
|
CopyEntry* copyEntries;
|
|
int copyCount;
|
|
} EditorData;
|
|
|
|
static EditorData s_editorData;
|
|
static CopyData s_copyData;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline struct sync_track** getTracks()
|
|
{
|
|
return s_editorData.trackData.syncData.tracks;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline void setActiveTrack(int track)
|
|
{
|
|
s_editorData.trackData.activeTrack = track;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline int getActiveTrack()
|
|
{
|
|
return s_editorData.trackData.activeTrack;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline int getTrackCount()
|
|
{
|
|
return s_editorData.trackData.syncData.num_tracks;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_create()
|
|
{
|
|
int id;
|
|
Emgui_create("foo");
|
|
id = Emgui_loadFontBitmap(g_minecraftiaFont, g_minecraftiaFontSize, EMGUI_FONT_MEMORY, 32, 128, g_minecraftiaFontLayout);
|
|
memset(&s_editorData, 0, sizeof(s_editorData));
|
|
|
|
RemoteConnection_createListner();
|
|
|
|
s_editorData.trackViewInfo.smallFontId = id;
|
|
|
|
Emgui_setDefaultFont();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_setWindowSize(int x, int y)
|
|
{
|
|
s_editorData.trackViewInfo.windowSizeX = x;
|
|
s_editorData.trackViewInfo.windowSizeY = y;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_init()
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void drawStatus()
|
|
{
|
|
char temp[256];
|
|
int active_track = 0;
|
|
int current_row = 0;
|
|
float value = 0.0f;
|
|
const char *str = "---";
|
|
struct sync_track** tracks = getTracks();
|
|
const int sizeY = s_editorData.trackViewInfo.windowSizeY;
|
|
|
|
active_track = getActiveTrack();
|
|
current_row = s_editorData.trackViewInfo.rowPos;
|
|
|
|
if (tracks)
|
|
{
|
|
const struct sync_track* track = tracks[active_track];
|
|
int row = s_editorData.trackViewInfo.rowPos;
|
|
int idx = key_idx_floor(track, row);
|
|
if (idx >= 0)
|
|
{
|
|
switch (track->keys[idx].type)
|
|
{
|
|
case KEY_STEP: str = "step"; break;
|
|
case KEY_LINEAR: str = "linear"; break;
|
|
case KEY_SMOOTH: str = "smooth"; break;
|
|
case KEY_RAMP: str = "ramp"; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
value = sync_get_val(track, row);
|
|
}
|
|
|
|
snprintf(temp, 256, "track %d row %d value %f type %s", active_track, current_row, value, str);
|
|
|
|
Emgui_fill(Emgui_color32(0x10, 0x10, 0x10, 0xff), 1, sizeY - 12, 400, 11);
|
|
Emgui_drawText(temp, 3, sizeY - 10, Emgui_color32(255, 255, 255, 255));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_update()
|
|
{
|
|
Emgui_begin();
|
|
|
|
TrackView_render(&s_editorData.trackViewInfo, &s_editorData.trackData);
|
|
|
|
drawStatus();
|
|
|
|
Emgui_end();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void copySelection(int row, int track, int selectLeft, int selectRight, int selectTop, int selectBottom)
|
|
{
|
|
CopyEntry* entry = 0;
|
|
int copy_count = 0;
|
|
struct sync_track** tracks = getTracks();
|
|
|
|
// Count how much we need to copy
|
|
|
|
for (track = selectLeft; track <= selectRight; ++track)
|
|
{
|
|
struct sync_track* t = tracks[track];
|
|
for (row = selectTop; row <= selectBottom; ++row)
|
|
{
|
|
int idx = sync_find_key(t, row);
|
|
if (idx < 0)
|
|
continue;
|
|
|
|
copy_count++;
|
|
}
|
|
}
|
|
|
|
free(s_copyData.entries);
|
|
entry = s_copyData.entries = malloc(sizeof(CopyEntry) * copy_count);
|
|
|
|
for (track = selectLeft; track <= selectRight; ++track)
|
|
{
|
|
struct sync_track* t = tracks[track];
|
|
for (row = selectTop; row <= selectBottom; ++row)
|
|
{
|
|
int idx = sync_find_key(t, row);
|
|
if (idx < 0)
|
|
continue;
|
|
|
|
entry->track = track - selectLeft;
|
|
entry->keyFrame = t->keys[idx];
|
|
entry->keyFrame.row -= selectTop;
|
|
entry++;
|
|
}
|
|
}
|
|
|
|
s_copyData.bufferWidth = selectRight - selectLeft + 1;
|
|
s_copyData.bufferHeight = selectBottom - selectTop + 1;
|
|
s_copyData.count = copy_count;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void deleteArea(int rowPos, int track, int bufferWidth, int bufferHeight)
|
|
{
|
|
int i, j;
|
|
const int track_count = getTrackCount();
|
|
struct sync_track** tracks = getTracks();
|
|
|
|
for (i = 0; i < bufferWidth; ++i)
|
|
{
|
|
size_t trackPos = track + i;
|
|
if (trackPos >= track_count)
|
|
continue;
|
|
|
|
size_t trackIndex = trackPos;
|
|
struct sync_track* t = tracks[trackIndex];
|
|
|
|
for (j = 0; j < bufferHeight; ++j)
|
|
{
|
|
int row = rowPos + j;
|
|
|
|
RemoteConnection_sendDeleteKeyCommand(t->name, row);
|
|
|
|
if (is_key_frame(t, row))
|
|
sync_del_key(t, row);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static char s_editBuffer[512];
|
|
static bool is_editing = false;
|
|
|
|
bool Editor_keyDown(int key, int modifiers)
|
|
{
|
|
bool handled_key = true;
|
|
TrackViewInfo* viewInfo = &s_editorData.trackViewInfo;
|
|
bool paused = RemoteConnection_isPaused();
|
|
struct sync_track** tracks = getTracks();
|
|
int active_track = getActiveTrack();
|
|
int row_pos = viewInfo->rowPos;
|
|
|
|
const int selectLeft = mini(viewInfo->selectStartTrack, viewInfo->selectStopTrack);
|
|
const int selectRight = maxi(viewInfo->selectStartTrack, viewInfo->selectStopTrack);
|
|
const int selectTop = mini(viewInfo->selectStartRow, viewInfo->selectStopRow);
|
|
const int selectBottom = maxi(viewInfo->selectStartRow, viewInfo->selectStopRow);
|
|
|
|
if (key == ' ')
|
|
{
|
|
// TODO: Don't start playing if we are in edit mode (but space shouldn't be added in edit mode but we still
|
|
// shouldn't start playing if we do
|
|
|
|
RemoteConnection_sendPauseCommand(!paused);
|
|
Editor_update();
|
|
return true;
|
|
}
|
|
|
|
if (!paused)
|
|
return false;
|
|
|
|
switch (key)
|
|
{
|
|
case EMGUI_ARROW_DOWN:
|
|
{
|
|
int row = row_pos;
|
|
|
|
row += modifiers & EDITOR_KEY_ALT ? 8 : 1;
|
|
viewInfo->rowPos = row;
|
|
|
|
if (modifiers & EDITOR_KEY_SHIFT)
|
|
{
|
|
viewInfo->selectStopRow = row;
|
|
break;
|
|
}
|
|
|
|
viewInfo->selectStartRow = viewInfo->selectStopRow = row;
|
|
viewInfo->selectStartTrack = viewInfo->selectStopTrack = active_track;
|
|
|
|
RemoteConnection_sendSetRowCommand(row);
|
|
|
|
break;
|
|
}
|
|
|
|
case EMGUI_ARROW_UP:
|
|
{
|
|
int row = row_pos;
|
|
|
|
row -= modifiers & EDITOR_KEY_ALT ? 8 : 1;
|
|
|
|
if ((modifiers & EDITOR_KEY_COMMAND) || row < 0)
|
|
row = 0;
|
|
|
|
viewInfo->rowPos = row;
|
|
|
|
if (modifiers & EDITOR_KEY_SHIFT)
|
|
{
|
|
viewInfo->selectStopRow = row;
|
|
break;
|
|
}
|
|
|
|
viewInfo->selectStartRow = viewInfo->selectStopRow = row;
|
|
viewInfo->selectStartTrack = viewInfo->selectStopTrack = active_track;
|
|
|
|
RemoteConnection_sendSetRowCommand(row);
|
|
handled_key = true;
|
|
|
|
break;
|
|
}
|
|
|
|
case EMGUI_ARROW_LEFT:
|
|
{
|
|
int track = getActiveTrack() - 1;
|
|
|
|
if (modifiers & EDITOR_KEY_COMMAND)
|
|
track = 0;
|
|
|
|
setActiveTrack(track < 0 ? 0 : track);
|
|
|
|
if (modifiers & EDITOR_KEY_SHIFT)
|
|
{
|
|
viewInfo->selectStopTrack = track;
|
|
break;
|
|
}
|
|
|
|
viewInfo->selectStartRow = viewInfo->selectStopRow = row_pos;
|
|
viewInfo->selectStartTrack = viewInfo->selectStopTrack = track;
|
|
|
|
handled_key = true;
|
|
|
|
break;
|
|
}
|
|
|
|
case EMGUI_ARROW_RIGHT:
|
|
{
|
|
int track = getActiveTrack(); track++;
|
|
int track_count = getTrackCount();
|
|
|
|
if (track >= track_count)
|
|
track = track_count - 1;
|
|
|
|
if (modifiers & EDITOR_KEY_COMMAND)
|
|
track = track_count - 1;
|
|
|
|
setActiveTrack(track);
|
|
|
|
if (modifiers & EDITOR_KEY_SHIFT)
|
|
{
|
|
viewInfo->selectStopTrack = track;
|
|
break;
|
|
}
|
|
|
|
viewInfo->selectStartRow = viewInfo->selectStopRow = row_pos;
|
|
viewInfo->selectStartTrack = viewInfo->selectStopTrack = track;
|
|
|
|
handled_key = true;
|
|
|
|
break;
|
|
}
|
|
|
|
default : handled_key = false; break;
|
|
}
|
|
|
|
// handle copy of tracks/values
|
|
|
|
if (key == 'c' && (modifiers & EDITOR_KEY_COMMAND))
|
|
{
|
|
copySelection(row_pos, active_track, selectLeft, selectRight, selectTop, selectBottom);
|
|
return true;
|
|
}
|
|
|
|
if (key == 'x' && (modifiers & EDITOR_KEY_COMMAND))
|
|
{
|
|
copySelection(row_pos, active_track, selectLeft, selectRight, selectTop, selectBottom);
|
|
deleteArea(selectTop, selectLeft, s_copyData.bufferWidth, s_copyData.bufferHeight);
|
|
handled_key = true;
|
|
}
|
|
|
|
// Handle paste of data
|
|
|
|
if (key == 'v' && (modifiers & EDITOR_KEY_COMMAND))
|
|
{
|
|
const int buffer_width = s_copyData.bufferWidth;
|
|
const int buffer_height = s_copyData.bufferHeight;
|
|
const int buffer_size = s_copyData.count;
|
|
const int track_count = getTrackCount();
|
|
|
|
if (!s_copyData.entries)
|
|
return false;
|
|
|
|
// First clear the paste area
|
|
|
|
deleteArea(row_pos, active_track, buffer_width, buffer_height);
|
|
|
|
for (int i = 0; i < buffer_size; ++i)
|
|
{
|
|
const CopyEntry* ce = &s_copyData.entries[i];
|
|
|
|
assert(ce->track >= 0);
|
|
assert(ce->track < buffer_width);
|
|
assert(ce->keyFrame.row >= 0);
|
|
assert(ce->keyFrame.row < buffer_height);
|
|
|
|
size_t trackPos = active_track + ce->track;
|
|
if (trackPos < track_count)
|
|
{
|
|
size_t trackIndex = trackPos;
|
|
struct track_key key = ce->keyFrame;
|
|
key.row += row_pos;
|
|
|
|
rlog(R_INFO, "key.row %d\n", key.row);
|
|
|
|
sync_set_key(tracks[trackIndex], &key);
|
|
|
|
RemoteConnection_sendSetKeyCommand(tracks[trackIndex]->name, &key);
|
|
}
|
|
}
|
|
|
|
handled_key = true;
|
|
}
|
|
|
|
// Handle biasing of values
|
|
|
|
if ((key >= '1' && key <= '9') && ((modifiers & EDITOR_KEY_CTRL) || (modifiers & EDITOR_KEY_ALT)))
|
|
{
|
|
struct sync_track** tracks;
|
|
int track, row;
|
|
|
|
float bias_value = 0.0f;
|
|
tracks = getTracks();
|
|
|
|
switch (key)
|
|
{
|
|
case '1' : bias_value = 0.01f; break;
|
|
case '2' : bias_value = 0.1f; break;
|
|
case '3' : bias_value = 1.0f; break;
|
|
case '4' : bias_value = 10.f; break;
|
|
case '5' : bias_value = 100.0f; break;
|
|
case '6' : bias_value = 1000.0f; break;
|
|
case '7' : bias_value = 10000.0f; break;
|
|
}
|
|
|
|
bias_value = modifiers & EDITOR_KEY_ALT ? -bias_value : bias_value;
|
|
|
|
for (track = selectLeft; track <= selectRight; ++track)
|
|
{
|
|
struct sync_track* t = tracks[track];
|
|
|
|
for (row = selectTop; row <= selectBottom; ++row)
|
|
{
|
|
int idx = sync_find_key(t, row);
|
|
if (idx < 0)
|
|
continue;
|
|
|
|
struct track_key newKey = t->keys[idx];
|
|
newKey.value += bias_value;
|
|
|
|
sync_set_key(t, &newKey);
|
|
|
|
RemoteConnection_sendSetKeyCommand(t->name, &newKey);
|
|
}
|
|
}
|
|
|
|
Editor_update();
|
|
|
|
return true;
|
|
}
|
|
|
|
// do edit here and biasing here
|
|
|
|
if ((key >= '0' && key <= '9') || key == '.' || key == '-')
|
|
{
|
|
if (!is_editing)
|
|
{
|
|
memset(s_editBuffer, 0, sizeof(s_editBuffer));
|
|
is_editing = true;
|
|
}
|
|
|
|
s_editBuffer[strlen(s_editBuffer)] = key;
|
|
s_editorData.trackData.editText = s_editBuffer;
|
|
|
|
return true;
|
|
}
|
|
else if (is_editing)
|
|
{
|
|
// if we press esc we discard the value
|
|
|
|
if (key != 27)
|
|
{
|
|
struct track_key key;
|
|
|
|
key.row = row_pos;
|
|
key.value = atof(s_editBuffer);
|
|
key.type = 0;
|
|
|
|
struct sync_track* track = tracks[active_track];
|
|
const char* track_name = track->name;
|
|
|
|
sync_set_key(track, &key);
|
|
|
|
rlog(R_INFO, "Setting key %f at %d row %d (name %s)\n", key.value, active_track, key.row, track_name);
|
|
|
|
RemoteConnection_sendSetKeyCommand(track_name, &key);
|
|
}
|
|
|
|
handled_key = true;
|
|
|
|
is_editing = false;
|
|
s_editorData.trackData.editText = 0;
|
|
}
|
|
|
|
if (key == 'i')
|
|
{
|
|
struct sync_track* track = tracks[active_track];
|
|
int row = viewInfo->rowPos;
|
|
|
|
int idx = key_idx_floor(track, row);
|
|
if (idx < 0)
|
|
return false;
|
|
|
|
// copy and modify
|
|
struct track_key newKey = track->keys[idx];
|
|
newKey.type = ((newKey.type + 1) % KEY_TYPE_COUNT);
|
|
|
|
sync_set_key(track, &newKey);
|
|
|
|
RemoteConnection_sendSetKeyCommand(track->name, &newKey);
|
|
|
|
handled_key = true;
|
|
}
|
|
|
|
if (handled_key)
|
|
Editor_update();
|
|
|
|
return handled_key;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int processCommands()
|
|
{
|
|
int strLen, newRow, serverIndex;
|
|
unsigned char cmd = 0;
|
|
int ret = 0;
|
|
|
|
if (RemoteConnection_recv((char*)&cmd, 1, 0))
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case GET_TRACK:
|
|
{
|
|
char trackName[4096];
|
|
|
|
memset(trackName, 0, sizeof(trackName));
|
|
|
|
RemoteConnection_recv((char *)&strLen, sizeof(int), 0);
|
|
strLen = ntohl(strLen);
|
|
|
|
if (!RemoteConnection_connected())
|
|
return 0;
|
|
|
|
if (!RemoteConnection_recv(trackName, strLen, 0))
|
|
return 0;
|
|
|
|
rlog(R_INFO, "Got trackname %s (%d) from demo\n", trackName, strLen);
|
|
|
|
// find track
|
|
|
|
serverIndex = TrackData_createGetTrack(&s_editorData.trackData, trackName);
|
|
|
|
// setup remap and send the keyframes to the demo
|
|
RemoteConnection_mapTrackName(trackName);
|
|
RemoteConnection_sendKeyFrames(trackName, s_editorData.trackData.syncData.tracks[serverIndex]);
|
|
ret = 1;
|
|
|
|
break;
|
|
}
|
|
|
|
case SET_ROW:
|
|
{
|
|
int i = 0;
|
|
ret = RemoteConnection_recv((char*)&newRow, sizeof(int), 0);
|
|
|
|
if (ret == -1)
|
|
{
|
|
// retry to get the data and do it for max of 20 times otherwise disconnect
|
|
|
|
for (i = 0; i < 20; ++i)
|
|
{
|
|
if (RemoteConnection_recv((char*)&newRow, sizeof(int), 0) == 4)
|
|
{
|
|
s_editorData.trackViewInfo.rowPos = htonl(newRow);
|
|
rlog(R_INFO, "row from demo %d\n", s_editorData.trackViewInfo.rowPos);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s_editorData.trackViewInfo.rowPos = htonl(newRow);
|
|
rlog(R_INFO, "row from demo %d\n", s_editorData.trackViewInfo.rowPos);
|
|
}
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_timedUpdate()
|
|
{
|
|
int processed_commands = 0;
|
|
|
|
RemoteConnection_updateListner();
|
|
|
|
while (RemoteConnection_pollRead())
|
|
processed_commands |= processCommands();
|
|
|
|
if (!RemoteConnection_isPaused() || processed_commands)
|
|
{
|
|
Editor_update();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void onOpen()
|
|
{
|
|
if (LoadSave_loadRocketXMLDialog(&s_editorData.trackData))
|
|
Editor_update();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void onSave()
|
|
{
|
|
LoadSave_saveRocketXMLDialog(&s_editorData.trackData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_menuEvent(int menuItem)
|
|
{
|
|
printf("%d\n", menuItem);
|
|
switch (menuItem)
|
|
{
|
|
//case EDITOR_MENU_NEW : onNew(); break;
|
|
case EDITOR_MENU_OPEN : onOpen(); break;
|
|
case EDITOR_MENU_SAVE :
|
|
case EDITOR_MENU_SAVE_AS : onSave(); break;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Editor_destroy()
|
|
{
|
|
}
|
|
|