Wrote a file serializer and parser for animations

This commit is contained in:
PS 2020-10-10 00:10:51 -07:00
parent b816474dd5
commit 1940483620
8 changed files with 675 additions and 16 deletions

View File

@ -577,20 +577,6 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
return Result;
}
struct animation_clip
{
char* Name;
s32 NameLength;
animation_proc* Proc;
};
s32 GlobalAnimationClipsCount = 3;
animation_clip GlobalAnimationClips[] = {
{ "Test Pattern One", 16, TestPatternOne },
{ "Test Pattern Two", 16, TestPatternTwo },
{ "Test Pattern Three", 18, TestPatternThree },
};
internal void
DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem)
{

View File

@ -29,6 +29,13 @@ enum blend_mode
BlendMode_Count,
};
global gs_const_string BlendModeStrings[] = {
ConstString("Overwrite"),
ConstString("Add"),
ConstString("Multiply"),
ConstString("Count"),
};
struct anim_layer
{
gs_string Name;
@ -44,6 +51,8 @@ struct anim_layer_array
struct animation
{
gs_string Name;
anim_layer_array Layers;
gs_list<animation_block> Blocks;
@ -87,6 +96,67 @@ struct animation_system
};
// TODO(pjs): Better name - something like animation_prototype
struct animation_clip
{
char* Name;
s32 NameLength;
animation_proc* Proc;
};
// Serialization
enum animation_field
{
AnimField_FileIdent,
AnimField_AnimName,
AnimField_LayersCount,
AnimField_BlocksCount,
AnimField_PlayableRange,
AnimField_PlayableRangeMin,
AnimField_PlayableRangeMax,
AnimField_LayersArray,
AnimField_Layer,
AnimField_LayerName,
AnimField_LayerBlendMode,
AnimField_BlocksArray,
AnimField_Block,
AnimField_BlockFrameRange,
AnimField_BlockFrameRangeMin,
AnimField_BlockFrameRangeMax,
AnimField_BlockLayerIndex,
AnimField_BlockAnimName,
AnimField_Count,
};
global gs_const_string AnimationFieldStrings[] = {
ConstString("lumenarium_animation_file"), // AnimField_FileIdent
ConstString("animation_name"),// AnimField_AnimName
ConstString("layers_count"),// AnimField_LayersCount
ConstString("blocks_count"),// AnimField_BlocksCount
ConstString("playable_range"),// AnimField_PlayableRange
ConstString("min"),// AnimField_PlayableRangeMin
ConstString("max"),// AnimField_PlayableRangeMax
ConstString("layers"),// AnimField_LayersArray
ConstString("layer"),// AnimField_Layer
ConstString("name"),// AnimField_LayerName
ConstString("blend"),// AnimField_LayerBlendMode
ConstString("blocks"),// AnimField_BlocksArray
ConstString("block"),// AnimField_Block
ConstString("frame_range"),// AnimField_BlockFrameRange
ConstString("min"),// AnimField_BlockFrameRangeMin
ConstString("max"),// AnimField_BlockFrameRangeMax
ConstString("layer_index"),// AnimField_BlockLayerIndex
ConstString("animation_name"),// AnimField_BlockAnimName
};
//////////////////////////
//
// Anim Layers Array

View File

@ -0,0 +1,10 @@
//
// File: foldhaus_animation_parser.cpp
// Author: Peter Slattery
// Creation Date: 2020-10-09
//
#ifndef FOLDHAUS_ANIMATION_PARSER_CPP
#define FOLDHAUS_ANIMATION_PARSER_CPP
#endif // FOLDHAUS_ANIMATION_PARSER_CPP

View File

@ -5,6 +5,185 @@
//
#ifndef FOLDHAUS_ANIMATION_SERIALIZER_CPP
internal gs_string
AnimSerializer_Serialize(animation Anim, animation_clip* GlobalClips, gs_memory_arena* Arena)
{
serializer Serializer = {0};
Serializer.String = PushString(Arena, 4096);
Serializer.Identifiers = AnimationFieldStrings;
Serializer.IdentifiersCount = AnimField_Count;
Serializer_WriteF(&Serializer, "%S;\n", AnimationFieldStrings[AnimField_FileIdent]);
Serializer_WriteStringValue(&Serializer, AnimField_AnimName, Anim.Name.ConstString);
Serializer_WriteValue(&Serializer, AnimField_LayersCount, Anim.Layers.Count);
Serializer_WriteValue(&Serializer, AnimField_BlocksCount, Anim.Blocks.Used);
Serializer_OpenStruct(&Serializer, AnimField_PlayableRange);
{
Serializer_WriteValue(&Serializer, AnimField_PlayableRangeMin, (u32)Anim.PlayableRange.Min);
Serializer_WriteValue(&Serializer, AnimField_PlayableRangeMax, (u32)Anim.PlayableRange.Max);
}
Serializer_CloseStruct(&Serializer);
Serializer_OpenStruct(&Serializer, AnimField_LayersArray);
for (u32 i = 0; i < Anim.Layers.Count; i++)
{
anim_layer LayerAt = Anim.Layers.Values[i];
Serializer_OpenStruct(&Serializer, AnimField_Layer);
{
Serializer_WriteStringValue(&Serializer, AnimField_LayerName, LayerAt.Name.ConstString);
Serializer_WriteStringValue(&Serializer, AnimField_LayerBlendMode, BlendModeStrings[LayerAt.BlendMode]);
}
Serializer_CloseStruct(&Serializer);
}
Serializer_CloseStruct(&Serializer);
Serializer_OpenStruct(&Serializer, AnimField_BlocksArray);
for (u32 i = 0; i < Anim.Blocks.Used; i++)
{
gs_list_entry<animation_block>* AnimationBlockEntry = Anim.Blocks.GetEntryAtIndex(i);
if (EntryIsFree(AnimationBlockEntry)) { continue; }
gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle;
animation_block AnimationBlockAt = AnimationBlockEntry->Value;
// TODO(pjs): Systematize the AnimationProcHandle
// :AnimProcHandle
u32 AnimationProcIndex = AnimationBlockAt.AnimationProcHandle - 1;
animation_clip Animation = GlobalClips[AnimationProcIndex];
Serializer_OpenStruct(&Serializer, AnimField_Block);
{
Serializer_OpenStruct(&Serializer, AnimField_BlockFrameRange);
{
Serializer_WriteValue(&Serializer, AnimField_BlockFrameRangeMin, (u32)AnimationBlockAt.Range.Min);
Serializer_WriteValue(&Serializer, AnimField_BlockFrameRangeMax, (u32)AnimationBlockAt.Range.Max);
}
Serializer_CloseStruct(&Serializer);
Serializer_WriteValue(&Serializer, AnimField_BlockLayerIndex, AnimationBlockAt.Layer);
Serializer_WriteStringValue(&Serializer, AnimField_BlockAnimName, ConstString(Animation.Name));
}
Serializer_CloseStruct(&Serializer);
}
Serializer_CloseStruct(&Serializer);
return Serializer.String;
}
internal animation
AnimParser_Parse(gs_string File, animation_clip* GlobalClips, gs_memory_arena* Arena, u32 AnimClipsCount, animation_clip* AnimClips)
{
animation Result = {0};
parser Parser = {0};
Parser.String = File;
Parser.At = Parser.String.Str;
Parser.Identifiers = AnimationFieldStrings;
Parser.IdentifiersCount = AnimField_Count;
Parser.Arena = Arena;
if (Parser_ReadString(&Parser, AnimationFieldStrings[AnimField_FileIdent]))
{
Result.Name = Parser_ReadStringValue(&Parser, AnimField_AnimName);
Result.Layers.CountMax = Parser_ReadU32Value(&Parser, AnimField_LayersCount);
Result.Layers.Values = PushArray(Arena, anim_layer, Result.Layers.CountMax);
// TODO(pjs): We're not using this now because Blocks are built on gs_list or something,
// but I want to replace that eventually, so this is here to preallocate the blocks we need
u32 BlocksCount = Parser_ReadU32Value(&Parser, AnimField_BlocksCount);
if (Parser_ReadOpenStruct(&Parser, AnimField_PlayableRange))
{
Result.PlayableRange.Min = Parser_ReadU32Value(&Parser, AnimField_PlayableRangeMin);
Result.PlayableRange.Max = Parser_ReadU32Value(&Parser, AnimField_PlayableRangeMax);
}
else
{
// TODO(pjs): Error
}
if (Parser_ReadOpenStruct(&Parser, AnimField_LayersArray))
{
while (!Parser_ReadCloseStruct(&Parser))
{
anim_layer Layer = {0};
if (Parser_ReadOpenStruct(&Parser, AnimField_Layer))
{
Layer.Name = Parser_ReadStringValue(&Parser, AnimField_LayerName);
gs_string BlendModeName = Parser_ReadStringValue(&Parser, AnimField_LayerBlendMode);
for (u32 i = 0; i < BlendMode_Count; i++)
{
if (StringsEqual(BlendModeName.ConstString, BlendModeStrings[i]))
{
Layer.BlendMode = (blend_mode)i;
break;
}
}
if (Parser_ReadCloseStruct(&Parser))
{
Animation_AddLayer(&Result, Layer);
}
else
{
// TODO(pjs): Error
}
}
}
}
if (Parser_ReadOpenStruct(&Parser, AnimField_BlocksArray))
{
while(!Parser_ReadCloseStruct(&Parser))
{
animation_block Block = {0};
if (Parser_ReadOpenStruct(&Parser, AnimField_Block))
{
if (Parser_ReadOpenStruct(&Parser, AnimField_BlockFrameRange))
{
Block.Range.Min = Parser_ReadU32Value(&Parser, AnimField_BlockFrameRangeMin);
Block.Range.Max = Parser_ReadU32Value(&Parser, AnimField_BlockFrameRangeMax);
Parser_ReadCloseStruct(&Parser);
}
else
{
// TODO(pjs): Error
}
Block.Layer = Parser_ReadU32Value(&Parser, AnimField_BlockLayerIndex);
// TODO(pjs): AnimName -> Animation Proc Handle
gs_string AnimName = Parser_ReadStringValue(&Parser, AnimField_BlockAnimName);
Block.AnimationProcHandle = 0;
for (u32 i = 0; i < AnimClipsCount; i++)
{
if (StringEqualsCharArray(AnimName.ConstString, AnimClips[i].Name, CStringLength(AnimClips[i].Name)))
{
Block.AnimationProcHandle = i + 1;
break;
}
}
if (Parser_ReadCloseStruct(&Parser))
{
Result.Blocks.PushElementOnList(Block);
}
else
{
// TODO(pjs): Error
}
}
}
}
}
return Result;
}
#define FOLDHAUS_ANIMATION_SERIALIZER_CPP
#endif // FOLDHAUS_ANIMATION_SERIALIZER_CPP

View File

@ -0,0 +1,18 @@
//
// File: foldhaus_parser.h
// Author: Peter Slattery
// Creation Date: 2020-10-09
//
#ifndef FOLDHAUS_PARSER_H
struct parser
{
gs_string String;
gs_const_string* Identifiers;
u32 IdentifiersCount;
};
#define FOLDHAUS_PARSER_H
#endif // FOLDHAUS_PARSER_H

View File

@ -0,0 +1,384 @@
//
// File: foldhaus_serializer.h
// Author: Peter Slattery
// Creation Date: 2020-10-09
//
#ifndef FOLDHAUS_SERIALIZER_H
struct serializer
{
gs_string String;
u32 Indent;
gs_const_string* Identifiers;
u32 IdentifiersCount;
};
internal gs_const_string
Serializer_GetIdent(serializer* Serializer, u32 Index)
{
gs_const_string Result = Serializer->Identifiers[Index];
return Result;
}
// Serializing
internal void
Serializer_WriteIndent(serializer* Serializer)
{
for (u32 i = 0; i < Serializer->Indent; i++)
{
AppendPrintF(&Serializer->String, " ");
}
}
internal void
Serializer_WriteF(serializer* Serializer, char* Format, ...)
{
Serializer_WriteIndent(Serializer);
va_list Args;
va_start(Args, Format);
PrintFArgsList(&Serializer->String, Format, Args);
va_end(Args);
}
internal void
Serializer_OpenStruct(serializer* Serializer, gs_const_string StructName)
{
Serializer_WriteF(Serializer, "%S:{\n", StructName);
Serializer->Indent++;
}
internal void
Serializer_OpenStruct(serializer* Serializer, u32 StructIdentifier)
{
gs_const_string Ident = Serializer_GetIdent(Serializer, StructIdentifier);
Serializer_WriteF(Serializer, "%S:{\n", Ident);
Serializer->Indent++;
}
internal void
Serializer_CloseStruct(serializer* Serializer)
{
Serializer->Indent--;
Serializer_WriteF(Serializer, "};\n");
}
internal void
Serializer_WriteValue(serializer* Serializer, gs_const_string Ident, gs_const_string Value)
{
Serializer_WriteF(Serializer, "%S: %S;\n", Ident, Value);
}
internal void
Serializer_WriteValue(serializer* Serializer, u32 IdentIndex, gs_const_string Value)
{
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
Serializer_WriteValue(Serializer, Ident, Value);
}
internal void
Serializer_WriteValue(serializer* Serializer, gs_const_string Ident, u32 Value)
{
Serializer_WriteF(Serializer, "%S: %d;\n", Ident, Value);
}
internal void
Serializer_WriteValue(serializer* Serializer, u32 IdentIndex, u32 Value)
{
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
Serializer_WriteValue(Serializer, Ident, Value);
}
internal void
Serializer_WriteValue(serializer* Serializer, gs_const_string Ident, r32 Value)
{
Serializer_WriteF(Serializer, "%S: %f;\n", Ident, Value);
}
internal void
Serializer_WriteValue(serializer* Serializer, u32 IdentIndex, r32 Value)
{
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
Serializer_WriteValue(Serializer, Ident, Value);
}
internal void
Serializer_WriteStringValue(serializer* Serializer, gs_const_string Ident, gs_const_string Value)
{
Serializer_WriteF(Serializer, "%S: \"%S\";\n", Ident, Value);
}
internal void
Serializer_WriteStringValue(serializer* Serializer, u32 IdentIndex, gs_const_string Value)
{
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
Serializer_WriteStringValue(Serializer, Ident, Value);
}
internal void
Serializer_WriteV3Value(serializer* Serializer, gs_const_string Ident, v3 Value)
{
Serializer_WriteF(Serializer, "%S: (%f, %f, %f);\n", Ident, Value.x, Value.y, Value.z);
}
internal void
Serializer_WriteV3Value(serializer* Serializer, u32 IdentIndex, v3 Value)
{
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
Serializer_WriteV3Value(Serializer, Ident, Value);
}
// Parsing
struct parser
{
gs_string String;
gs_const_string* Identifiers;
u32 IdentifiersCount;
u32 Line;
char* LineStart;
char* At;
gs_memory_arena* Arena;
};
internal gs_const_string
Parser_GetIdent(parser Parser, u32 Index)
{
gs_const_string Result = Parser.Identifiers[Index];
return Result;
}
internal bool
Parser_AtValidPosition(parser Parser)
{
u64 Offset = Parser.At - Parser.String.Str;
bool Result = (Offset < Parser.String.Length);
return Result;
}
internal void
Parser_AdvanceChar(parser* P)
{
if (IsNewline(P->At[0]))
{
P->Line += 1;
P->LineStart = P->At + 1;
}
P->At++;
}
internal void
Parser_EatWhitespace(parser* P)
{
while(Parser_AtValidPosition(*P) && IsNewlineOrWhitespace(P->At[0]))
{
Parser_AdvanceChar(P);
}
}
internal void
Parser_EatToNewLine(parser* P)
{
while(Parser_AtValidPosition(*P) && !IsNewline(P->At[0]))
{
Parser_AdvanceChar(P);
}
Parser_EatWhitespace(P);
}
internal bool
Parser_AdvanceIfTokenEquals(parser* P, gs_const_string Value)
{
bool Result = true;
char* PAt = P->At;
char* VAt = Value.Str;
while (*VAt != 0)
{
if (*PAt != *VAt)
{
Result = false;
break;
}
PAt += 1;
VAt += 1;
}
// TODO(Peter): What if the token is a subset of Value? ie. this would return true for
// T->At = hello_world and Value = hello_
// But the next token we read would fail
if (Result)
{
P->At = PAt;
Parser_EatWhitespace(P);
}
return Result;
}
internal bool
Parser_AdvanceIfLineEnd(parser* P)
{
bool Result = Parser_AdvanceIfTokenEquals(P, ConstString(";"));
return Result;
}
internal bool
Parser_ReadString(parser* P, gs_const_string String)
{
// string;
bool Result = Parser_AdvanceIfTokenEquals(P, String);
Result &= Parser_AdvanceIfLineEnd(P);
return Result;
}
internal bool
Parser_ReadString(parser* P, u32 IdentIndex)
{
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
return Parser_ReadString(P, Ident);
}
internal gs_string
Parser_ReadStringValue(parser* P, gs_const_string Ident)
{
// ident: "value";
gs_string Result = {};
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
Parser_AdvanceIfTokenEquals(P, ConstString(":")) &&
Parser_AdvanceIfTokenEquals(P, ConstString("\"")))
{
gs_const_string FileString = {0};
FileString.Str = P->At;
while (Parser_AtValidPosition(*P) && P->At[0] != '"')
{
Parser_AdvanceChar(P);
}
FileString.Length = P->At - FileString.Str;
if (Parser_AdvanceIfTokenEquals(P, ConstString("\"")) &&
Parser_AdvanceIfLineEnd(P))
{
Result = PushStringF(P->Arena, FileString.Length, "%S", FileString);
}
else
{
// TODO(pjs): Error
}
}
return Result;
}
internal gs_string
Parser_ReadStringValue(parser* P, u32 IdentIndex)
{
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
return Parser_ReadStringValue(P, Ident);
}
internal bool
Parser_ReadOpenStruct(parser* P, gs_const_string Ident)
{
// ident: {
bool Result = Parser_AdvanceIfTokenEquals(P, Ident);
Result &= Parser_AdvanceIfTokenEquals(P, ConstString(":"));
Result &= Parser_AdvanceIfTokenEquals(P, ConstString("{"));
return Result;
}
internal bool
Parser_ReadOpenStruct(parser* P, u32 IdentIndex)
{
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
return Parser_ReadOpenStruct(P, Ident);
}
internal bool
Parser_ReadCloseStruct(parser* P)
{
// }
bool Result = Parser_AdvanceIfTokenEquals(P, ConstString("}"));
Result &= Parser_AdvanceIfLineEnd(P);
return Result;
}
internal gs_const_string
Parser_ReadNumberString(parser* P)
{
gs_const_string Result = {};
Result.Str = P->At;
while(Parser_AtValidPosition(*P) && IsNumericExtended(P->At[0]))
{
Parser_AdvanceChar(P);
}
Result.Length = P->At - Result.Str;
return Result;
}
internal u32
Parser_ReadU32Value(parser* P, gs_const_string Ident)
{
// ident: value;
u32 Result = 0;
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
Parser_AdvanceIfTokenEquals(P, ConstString(":")))
{
gs_const_string NumStr = Parser_ReadNumberString(P);
if (Parser_AdvanceIfLineEnd(P))
{
Result = (u32)ParseInt(NumStr);
}
else
{
// TODO(pjs): Error
}
}
return Result;
}
internal u32
Parser_ReadU32Value(parser* P, u32 IdentIndex)
{
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
return Parser_ReadU32Value(P, Ident);
}
internal r32
Parser_ReadR32Value(parser* P, gs_const_string Ident)
{
// ident: value;
r32 Result = 0;
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
Parser_AdvanceIfTokenEquals(P, ConstString(":")))
{
gs_const_string NumStr = Parser_ReadNumberString(P);
if (Parser_AdvanceIfLineEnd(P))
{
Result = (r32)ParseFloat(NumStr);
}
else
{
// TODO(pjs): Error
}
}
return Result;
}
internal u32
Parser_ReadR32Value(parser* P, u32 IdentIndex)
{
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
return Parser_ReadR32Value(P, Ident);
}
#define FOLDHAUS_SERIALIZER_H
#endif // FOLDHAUS_SERIALIZER_H

View File

@ -144,6 +144,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->AnimationSystem.SecondsPerFrame = 1.f / 24.f;
animation Anim = {0};
Anim.Name = PushStringF(&State->Permanent, 256, "test_anim_one");
Anim.Layers.CountMax = 8;
Anim.Layers.Values = PushArray(State->AnimationSystem.Storage, anim_layer, Anim.Layers.CountMax);
Anim.PlayableRange.Min = 0;
@ -152,9 +153,9 @@ INITIALIZE_APPLICATION(InitializeApplication)
Animation_AddLayer(&Anim, MakeString("Color Layer"), BlendMode_Multiply, &State->AnimationSystem);
Animation_AddLayer(&Anim, MakeString("Sparkles"), BlendMode_Add, &State->AnimationSystem);
Animation_AddBlock(&Anim, 22, 123, 2, 1);
AnimationArray_Push(&State->AnimationSystem.Animations, Anim);
} // End Animation Playground
@ -280,6 +281,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
u32 FramesIntoBlock = CurrentFrame - Block.Range.Min;
r32 SecondsIntoBlock = FramesIntoBlock * State->AnimationSystem.SecondsPerFrame;
// :AnimProcHandle
u32 AnimationProcIndex = Block.AnimationProcHandle - 1;
animation_proc* AnimationProc = GlobalAnimationClips[AnimationProcIndex].Proc;
AnimationProc(&LayerLEDBuffers[Layer], *Assembly, SecondsIntoBlock, State->Transient);

View File

@ -8,6 +8,8 @@
#include "../meta/gs_meta_include.h"
#include "../meta/gs_meta_lexer.h"
#include "engine/foldhaus_serializer.h"
#include "../gs_libs/gs_font.h"
#include "foldhaus_log.h"
@ -32,6 +34,7 @@ typedef struct app_state app_state;
#include "editor/foldhaus_operation_mode.h"
#include "engine/animation/foldhaus_animation.h"
#include "engine/animation/foldhaus_animation_serializer.cpp"
struct app_state
{
@ -226,6 +229,13 @@ struct panel_definition
s32 InputCommandsCount;
};
s32 GlobalAnimationClipsCount = 3;
animation_clip GlobalAnimationClips[] = {
{ "Test Pattern One", 16, TestPatternOne },
{ "Test Pattern Two", 16, TestPatternTwo },
{ "Test Pattern Three", 18, TestPatternThree },
};
#include "editor/panels/foldhaus_panel_sculpture_view.h"
#include "editor/panels/foldhaus_panel_profiler.h"
#include "editor/panels/foldhaus_panel_dmx_view.h"