Merge pull request #1 from peter-slattery/animation

Animation
This commit is contained in:
Peter Slattery 2019-12-29 08:50:49 -08:00 committed by GitHub
commit e3705842dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 3479 additions and 6101 deletions

View File

@ -10,7 +10,7 @@ IF NOT EXIST .\build\ mkdir .\build
C:\programs\ctime\ctime.exe -begin %ProjectDevPath%\build\win32_foldhaus_build_time.ctm
set CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except-
set CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src
set CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -O2 %CommonCompilerFlags%
set CommonLinkerFlags= -opt:ref

15
data/blumen_lumen.fold Normal file
View File

@ -0,0 +1,15 @@
led_strip_count 12
led_strip { 0, 0, 0, INTERPOLATE_POINTS, (0.000000, 1.000000, 0.000000), (0.000000, 0.050000, 1.000000), 70 }
led_strip { 0, 1, 0, INTERPOLATE_POINTS, (0.500000, 0.866025, 0.000000), (0.025000, 0.043301, 1.000000), 70 }
led_strip { 0, 2, 0, INTERPOLATE_POINTS, (0.866025, 0.499999, 0.000000), (0.043301, 0.024999, 1.000000), 70 }
led_strip { 0, 3, 0, INTERPOLATE_POINTS, (1.000000, -0.000000, 0.000000), (0.050000, -0.000000, 1.000000), 70 }
led_strip { 0, 4, 0, INTERPOLATE_POINTS, (0.866025, -0.500000, 0.000000), (0.043301, -0.025000, 1.000000), 70 }
led_strip { 0, 5, 0, INTERPOLATE_POINTS, (0.500000, -0.866025, 0.000000), (0.025000, -0.043301, 1.000000), 70 }
led_strip { 0, 6, 0, INTERPOLATE_POINTS, (-0.000000, -1.000000, 0.000000), (-0.000000, -0.050000, 1.000000), 70 }
led_strip { 0, 7, 0, INTERPOLATE_POINTS, (-0.500000, -0.866025, 0.000000), (-0.025000, -0.043301, 1.000000), 70 }
led_strip { 0, 8, 0, INTERPOLATE_POINTS, (-0.866025, -0.499999, 0.000000), (-0.043301, -0.024999, 1.000000), 70 }
led_strip { 0, 9, 0, INTERPOLATE_POINTS, (-1.000000, 0.000000, 0.000000), (-0.050000, 0.000000, 1.000000), 70 }
led_strip { 0, 10, 0, INTERPOLATE_POINTS, (-0.866025, 0.499999, 0.000000), (-0.043301, 0.024999, 1.000000), 70 }
led_strip { 0, 11, 0, INTERPOLATE_POINTS, (-0.499999, 0.866025, 0.000000), (-0.024999, 0.043301, 1.000000), 70 }
END_OF_ASSEMBLY_FILE

View File

@ -0,0 +1,21 @@
// NOTE(Peter): stuff this in a function and itll print out the code needed to generate a blumen
// TODO(Peter): Modify this when you get actual blumen measurements
MakeStringBuffer(Buffer, 256);
v3 InnerVectors[12];
v3 OuterVectors[12];
for (s32 i = 0; i < 12; i++)
{
r32 Theta = ((r32)i / 12.0f) * 2 * PI;
v3 Direction = v3{GSSin(Theta), GSCos(Theta), 0};
InnerVectors[i] = Direction;
OuterVectors[i] = v3{Direction.x * 0.05f, Direction.y * 0.05f, 1};
PrintF(&Buffer, "led_strip { 0, %d, 0, INTERPOLATE_POINTS, (%f, %f, %f), (%f, %f, %f), 70 }\n",
i,
InnerVectors[i].x, InnerVectors[i].y, InnerVectors[i].z,
OuterVectors[i].x, OuterVectors[i].y, OuterVectors[i].z);
NullTerminate(&Buffer);
OutputDebugStringA(Buffer.Memory);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

99
meta/gs_string_builder.h Normal file
View File

@ -0,0 +1,99 @@
#define STRING_BUILDER_ARRAY_BUFFER_SIZE 32
struct string_array
{
string** Buckets;
s32 BucketSize;
s32 BucketCount;
s32 Used;
free_list FreeList;
};
internal string*
GetEntryAtIndex (s32 Index, string_array Buffer)
{
string* Result = 0;
if (Buffer.Buckets)
{
bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize);
Result = Buffer.Buckets[BucketIndex.Bucket] + BucketIndex.IndexInBucket;
}
return Result;
}
internal s32
PushElement (string Data, string_array* Buffer)
{
s32 Result = -1;
if (Buffer->Used >= Buffer->BucketSize * Buffer->BucketCount)
{
Buffer->Buckets = (string**)GrowBuffer(Buffer->BucketSize, sizeof(string), &Buffer->BucketCount, (void**)Buffer->Buckets);
}
s32 Index = Buffer->Used++;
s32 BucketIndex = Index / Buffer->BucketSize;
s32 IndexInBucket = Index % Buffer->BucketSize;
Buffer->Buckets[BucketIndex][IndexInBucket] = Data;
Result = Index;
return Result;
}
struct string_builder
{
string_array Buffer;
s32 BufferElementSize;
};
internal string_builder
InitStringBuilder(s32 BufferSize)
{
string_builder Result = {};
Result.BufferElementSize = BufferSize;
Result.Buffer.BucketSize = STRING_BUILDER_ARRAY_BUFFER_SIZE;
Result.Buffer.FreeList.Next = &Result.Buffer.FreeList;
return Result;
}
internal void
GrowStringBuilder (string_builder* StringBuilder)
{
string NewSegment = {};
NewSegment.Memory = (char*)malloc(StringBuilder->BufferElementSize * sizeof(char));
NewSegment.Max = StringBuilder->BufferElementSize;
PushElement(NewSegment, &StringBuilder->Buffer);
}
internal void
StringBuilderPrintF (string_builder* StringBuilder, char* Format, ...)
{
string Addition = {};
Addition.Max = 2048;
Addition.Memory = (char*)malloc(Addition.Max * sizeof(char));
va_list Args;
va_start(Args, Format);
Addition.Length = PrintFArgsList(Addition.Memory, Addition.Max, Format, Args);
s32 CharsCopied = 0;
while (CharsCopied < Addition.Length)
{
s32 StringBuilderTailIndex = StringBuilder->Buffer.Used - 1;
string* LastString = GetEntryAtIndex(StringBuilderTailIndex, StringBuilder->Buffer);
if (!LastString || LastString->Length >= LastString->Max)
{
GrowStringBuilder(StringBuilder);
StringBuilderTailIndex = StringBuilder->Buffer.Used - 1;
LastString = GetEntryAtIndex(StringBuilderTailIndex, StringBuilder->Buffer);
}
while (CharsCopied < Addition.Length && LastString->Length < LastString->Max)
{
LastString->Memory[LastString->Length++] = Addition.Memory[CharsCopied++];
}
}
free(Addition.Memory);
}

View File

@ -0,0 +1,147 @@
// TODO
// [] - animation blending
// [] - delete a layer
// [] - will need a way to create an empty layer
// [] - get a list of all animation procs
#define ANIMATION_PROC(name) void name(assembly* Assembly, r32 Time)
typedef ANIMATION_PROC(animation_proc);
struct animation_block
{
// TODO(Peter): Should we change this to frames??
r32 StartTime;
r32 EndTime;
animation_proc* Proc;
u32 Layer;
};
struct animation_block_handle
{
s32 Index;
// NOTE(Peter): Zero is invalid
u32 Generation;
};
struct animation_block_entry
{
u32 Generation;
animation_block Block;
free_list Free;
};
#define ANIMATION_SYSTEM_LAYERS_MAX 128
#define ANIMATION_SYSTEM_BLOCKS_MAX 128
struct animation_system
{
animation_block_entry Blocks[ANIMATION_SYSTEM_BLOCKS_MAX];
free_list FreeList;
u32 BlocksCount;
r32 Time;
s32 LastUpdatedFrame;
r32 SecondsPerFrame;
b32 TimelineShouldAdvance;
// :Temporary
r32 AnimationStart;
r32 AnimationEnd;
};
internal b32
AnimationBlockHandlesAreEqual(animation_block_handle A, animation_block_handle B)
{
b32 Result = ((A.Index == B.Index) && (A.Generation == B.Generation));
return Result;
}
internal b32
AnimationBlockHandleIsValid(animation_block_handle Handle)
{
b32 Result = Handle.Generation != 0;
return Result;
}
internal void
InitializeAnimationSystem(animation_system* System)
{
*System = {0};
System->FreeList.Next = &System->FreeList;
}
inline b32
AnimationBlockIsFree(animation_block_entry Entry)
{
// NOTE(Peter): If we've set Free.Next to zero, we've removed it from the
// free list.
b32 Result = Entry.Free.Next != 0;
return Result;
}
internal animation_block_entry*
GetEntryAtIndex(u32 Index, animation_system* System)
{
Assert(Index < System->BlocksCount);
animation_block_entry* Result = System->Blocks + Index;
return Result;
}
internal animation_block*
GetAnimationBlockWithHandle(animation_block_handle Handle, animation_system* System)
{
animation_block* Result = 0;
animation_block_entry* Entry = GetEntryAtIndex(Handle.Index, System);
if (Entry && Entry->Generation == Handle.Generation)
{
Result = &Entry->Block;
}
return Result;
}
internal animation_block_handle
AddAnimationBlock(animation_block Block, animation_system* System)
{
animation_block_handle Result = {0};
if (System->FreeList.Next != 0
&& System->FreeList.Next != &System->FreeList)
{
free_list* FreeEntry = System->FreeList.Next;
Result.Index = FreeEntry->Index;
System->FreeList.Next = FreeEntry->Next;
}
else
{
Assert(System->BlocksCount < ANIMATION_SYSTEM_BLOCKS_MAX);
Result.Index = System->BlocksCount++;
}
animation_block_entry* Entry = GetEntryAtIndex(Result.Index, System);
Entry->Generation += 1;
Result.Generation = Entry->Generation;
System->Blocks[Result.Index].Block = Block;
System->Blocks[Result.Index].Free.Next = 0;
return Result;
}
internal void
RemoveAnimationBlock(animation_block_handle Handle, animation_system* System)
{
animation_block_entry* Entry = GetEntryAtIndex(Handle.Index, System);
// NOTE(Peter): I'm pretty sure this doesn't need to be an assert but at the moment, there
// is no reason why we shouldn't always be able to remove an entry when we request it.
// For now, I'm putting this assert here so we deal with this intentionally when the first
// case comes up.
// TODO: When we do deal with the above note, I'm guessing we want to return true or false
// to signal if we were able to remove the entry or not so that the calling site can deal
// with the removed reference
Assert(Handle.Generation == Entry->Generation);
Entry->Free.Index = Handle.Index;
Entry->Free.Next = System->FreeList.Next;
System->FreeList.Next = &Entry->Free;
}

View File

@ -1,12 +1,22 @@
#include "foldhaus_platform.h"
#include "foldhaus_app.h"
internal void
SetPanelDefinitionExternal(panel* Panel, s32 OldPanelDefinitionIndex, s32 NewPanelDefinitionIndex)
{
if(OldPanelDefinitionIndex >= 0)
{
GlobalPanelDefs[OldPanelDefinitionIndex].Cleanup(Panel);
}
GlobalPanelDefs[NewPanelDefinitionIndex].Init(Panel);
}
internal v4
MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 WindowHeight)
MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, rect WindowBounds)
{
DEBUG_TRACK_SCOPE(MouseToWorldRay);
r32 X = ((2.0f * MouseX) / WindowWidth) - 1;
r32 Y = ((2.0f * MouseY) / WindowHeight) - 1;
r32 X = ((2.0f * MouseX) / Width(WindowBounds)) - 1;
r32 Y = ((2.0f * MouseY) / Height(WindowBounds)) - 1;
v4 ScreenPos = v4{X, Y, -1, 1};
@ -22,73 +32,10 @@ MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 Win
return WorldPosition;
}
struct draw_leds_job_data
{
led* LEDs;
pixel* Colors;
s32 StartIndex;
s32 OnePastLastIndex;
render_quad_batch_constructor* Batch;
m44 FaceCameraMatrix;
m44 ModelViewMatrix;
r32 LEDHalfWidth;
};
internal void
DrawLEDsInBufferRangeJob (s32 ThreadID, void* JobData)
{
DEBUG_TRACK_FUNCTION;
draw_leds_job_data* Data = (draw_leds_job_data*)JobData;
s32 LEDCount = Data->OnePastLastIndex - Data->StartIndex;
quad_batch_constructor_reserved_range BatchReservedRange = ThreadSafeReserveRangeInQuadConstructor(Data->Batch, LEDCount * 2);
s32 TrisUsed = 0;
r32 HalfWidth = Data->LEDHalfWidth;
v4 P0_In = v4{-HalfWidth, -HalfWidth, 0, 1};
v4 P1_In = v4{HalfWidth, -HalfWidth, 0, 1};
v4 P2_In = v4{HalfWidth, HalfWidth, 0, 1};
v4 P3_In = v4{-HalfWidth, HalfWidth, 0, 1};
v2 UV0 = v2{0, 0};
v2 UV1 = v2{1, 0};
v2 UV2 = v2{1, 1};
v2 UV3 = v2{0, 1};
led* LED = Data->LEDs + Data->StartIndex;
for (s32 LEDIdx = 0;
LEDIdx < LEDCount;
LEDIdx++)
{
pixel PixelColor = Data->Colors[LED->Index];
v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f};
v4 V4Position = LED->Position;
V4Position.w = 0;
v4 P0 = P0_In + V4Position;
v4 P1 = P1_In + V4Position;
v4 P2 = P2_In + V4Position;
v4 P3 = P3_In + V4Position;
SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++,
P0, P1, P2, UV0, UV1, UV2, Color, Color, Color);
SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++,
P0, P2, P3, UV0, UV2, UV3, Color, Color, Color);
LED++;
}
}
struct send_sacn_job_data
{
platform_socket_handle SendSocket;
platform_get_send_address* GetSendAddress;
platform_send_to* SendTo;
dmx_buffer_list* DMXBuffers;
};
@ -99,7 +46,6 @@ SACNSendDMXBufferListJob (s32 ThreadID, void* JobData)
DEBUG_TRACK_FUNCTION;
send_sacn_job_data* Data = (send_sacn_job_data*)JobData;
platform_get_send_address* GetSendAddress = Data->GetSendAddress;
platform_socket_handle SendSocket = Data->SendSocket;
platform_send_to* SendTo = Data->SendTo;
@ -109,10 +55,11 @@ SACNSendDMXBufferListJob (s32 ThreadID, void* JobData)
dmx_buffer Buffer = DMXBufferAt->Buffer;
u_long V4SendAddress = SACNGetUniverseSendAddress(Buffer.Universe);
platform_network_address_handle SendAddress = GetSendAddress(
AF_INET,
HostToNetU16(DEFAULT_STREAMING_ACN_PORT),
HostToNetU32(V4SendAddress));
platform_network_address SendAddress = {};
SendAddress.Family = AF_INET;
SendAddress.Port = DEFAULT_STREAMING_ACN_PORT;
SendAddress.Address = V4SendAddress;
SendTo(SendSocket, SendAddress, (const char*)Buffer.Base, Buffer.TotalSize, 0);
@ -120,57 +67,6 @@ SACNSendDMXBufferListJob (s32 ThreadID, void* JobData)
}
}
internal void
LoadAssembly (app_state* State, context Context, char* Path)
{
arena_snapshot TempMemorySnapshot = TakeSnapshotOfArena(*State->Transient);
platform_memory_result TestAssemblyFile = Context.PlatformReadEntireFile(Path);
Assert(TestAssemblyFile.Size > 0);
assembly_definition AssemblyDefinition = ParseAssemblyFile(TestAssemblyFile.Base, TestAssemblyFile.Size, State->Transient);
Context.PlatformFree(TestAssemblyFile.Base, TestAssemblyFile.Size);
string PathString = MakeStringLiteral(Path);
s32 IndexOfLastSlash = FastLastIndexOfCharInCharArray(PathString.Memory, PathString.Length, '\\');
string FileName = Substring(PathString, IndexOfLastSlash + 1);
r32 Scale = 100;
Assert(State->AssembliesCount < ASSEMBLY_LIST_LENGTH);
s32 AssemblyMemorySize = GetAssemblyMemorySizeFromDefinition(AssemblyDefinition, FileName);
u8* AssemblyMemory = Context.PlatformAlloc(AssemblyMemorySize);
assembly NewAssembly = ConstructAssemblyFromDefinition(AssemblyDefinition,
FileName,
v3{0, 0, 0},
Scale,
AssemblyMemory,
AssemblyMemorySize);
State->AssemblyList[State->AssembliesCount++] = NewAssembly;
State->TotalLEDsCount += NewAssembly.LEDCount;
ClearArenaToSnapshot(State->Transient, TempMemorySnapshot);
}
internal void
UnloadAssembly (s32 AssemblyIndex, app_state* State, context Context)
{
assembly* Assembly = State->AssemblyList + AssemblyIndex;
State->TotalLEDsCount -= Assembly->LEDCount;
Context.PlatformFree(Assembly->Arena.Base, Assembly->Arena.Size);
if (AssemblyIndex != (State->AssembliesCount - 1))
{
State->AssemblyList[AssemblyIndex] = State->AssemblyList[State->AssembliesCount - 1];
}
else
{
*Assembly = {};
}
State->AssembliesCount -= 1;
}
////////////////////////////////////////////////////////////////////////
RELOAD_STATIC_DATA(ReloadStaticData)
@ -180,36 +76,25 @@ RELOAD_STATIC_DATA(ReloadStaticData)
GlobalDebugServices = DebugServices;
GSAlloc = Alloc;
GSFree = Free;
if (State->DefaultInputCommandRegistry.Size > 0)
{
RegisterKeyPressCommand(&State->DefaultInputCommandRegistry, KeyCode_MouseLeftButton, Command_Began, KeyCode_Invalid,
Begin3DViewMouseRotate);
RegisterKeyPressCommand(&State->DefaultInputCommandRegistry, KeyCode_U, Command_Began, KeyCode_Invalid, OpenUniverseView);
RegisterKeyPressCommand(&State->DefaultInputCommandRegistry, KeyCode_Tab, Command_Began, KeyCode_Invalid, OpenNodeView);
}
}
INITIALIZE_APPLICATION(InitializeApplication)
{
app_state* State = (app_state*)Context.MemoryBase;
u8* MemoryCursor = Context.MemoryBase + sizeof(app_state);
s32 PermanentStorageSize = Megabytes(32);
s32 TransientStorageSize = Context.MemorySize - PermanentStorageSize;
State->Permanent = BootstrapArenaIntoMemory(MemoryCursor, PermanentStorageSize);
State->Transient = BootstrapArenaIntoMemory(MemoryCursor + PermanentStorageSize, TransientStorageSize);
InitMemoryArena(&State->SACNMemory, 0, 0, Context.PlatformAlloc);
InitializeInputCommandRegistry(&State->DefaultInputCommandRegistry, 32, State->Permanent);
State->Permanent = {};
State->Permanent.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
State->Permanent.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
State->Transient = {};
State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
State->Transient.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
s32 CommandQueueSize = 32;
command_queue_entry* CommandQueueMemory = PushArray(State->Permanent,
command_queue_entry* CommandQueueMemory = PushArray(&State->Permanent,
command_queue_entry,
CommandQueueSize);
State->CommandQueue = InitializeCommandQueue(CommandQueueMemory, CommandQueueSize);
State->ActiveTextEntry.Buffer = MakeString(PushArray(State->Permanent, char, 256), 0, 256);
State->ActiveTextEntry.Buffer = MakeString(PushArray(&State->Permanent, char, 256), 0, 256);
// TODO(Peter): put in InitializeInterface?
r32 FontSize = 14;
@ -217,12 +102,12 @@ INITIALIZE_APPLICATION(InitializeApplication)
platform_memory_result FontFile = Context.PlatformReadEntireFile("Anonymous Pro.ttf");
if (FontFile.Size)
{
bitmap_font* Font = PushStruct(State->Permanent, bitmap_font);
bitmap_font* Font = PushStruct(&State->Permanent, bitmap_font);
Font->BitmapWidth = 512;
Font->BitmapHeight = 512;
Font->BitmapBytesPerPixel = 4;
Font->BitmapMemory = PushArray(State->Permanent, u8, Font->BitmapWidth * Font->BitmapHeight * Font->BitmapBytesPerPixel);
Font->BitmapMemory = PushArray(&State->Permanent, u8, Font->BitmapWidth * Font->BitmapHeight * Font->BitmapBytesPerPixel);
Font->BitmapStride = Font->BitmapWidth * Font->BitmapBytesPerPixel;
GSMemSet(Font->BitmapMemory, 0, Font->BitmapStride * Font->BitmapHeight);
@ -235,8 +120,8 @@ INITIALIZE_APPLICATION(InitializeApplication)
Font->CodepointDictionarySize = (FontInfo.CodepointOnePastLast - FontInfo.CodepointStart);
Font->CodepointDictionaryCount = 0;
Font->CodepointKeys = PushArray(State->Permanent, char, Font->CodepointDictionarySize);
Font->CodepointValues = PushArray(State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize);
Font->CodepointKeys = PushArray(&State->Permanent, char, Font->CodepointDictionarySize);
Font->CodepointValues = PushArray(&State->Permanent, codepoint_bitmap, Font->CodepointDictionarySize);
for (s32 Codepoint = FontInfo.CodepointStart;
Codepoint < FontInfo.CodepointOnePastLast;
@ -248,18 +133,17 @@ INITIALIZE_APPLICATION(InitializeApplication)
u32 CodepointW, CodepointH;
Context.PlatformDrawFontCodepoint(
Font->BitmapMemory,
Font->BitmapWidth,
Font->BitmapHeight,
CodepointX, CodepointY,
Codepoint, FontInfo,
&CodepointW, &CodepointH);
Font->BitmapMemory,
Font->BitmapWidth,
Font->BitmapHeight,
CodepointX, CodepointY,
Codepoint, FontInfo,
&CodepointW, &CodepointH);
AddCodepointToFont(Font, Codepoint, 0, 0, CodepointW, CodepointH, CodepointX, CodepointY);
}
State->Interface.Font = Font;
State->Font = Font;
Font->BitmapTextureHandle = Context.PlatformGetGPUTextureHandle(Font->BitmapMemory,
Font->BitmapWidth, Font->BitmapHeight);
@ -282,12 +166,15 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->NetworkProtocolHeaderSize = STREAM_HEADER_SIZE;
State->Camera.FieldOfView = DegreesToRadians(45.0f);
State->Camera.AspectRatio = (r32)Context.WindowWidth / (r32)Context.WindowHeight;
State->Camera.AspectRatio = AspectRatio(State->WindowBounds);
State->Camera.Near = 1.0f;
State->Camera.Far = 100.0f;
State->Camera.Position = v3{0, 0, -250};
State->Camera.LookAt = v3{0, 0, 0};
State->AssemblyList.BucketSize = 32;
State->AssemblyList.FreeList.Next = &State->AssemblyList.FreeList;
State->ActiveAssemblyIndecies.BucketSize = 32;
#if 1
char Path[] = "radialumia.fold";
LoadAssembly(State, Context, Path);
@ -297,54 +184,73 @@ INITIALIZE_APPLICATION(InitializeApplication)
GlobalDebugServices->Interface.RenderSculpture = true;
State->NodeList = AllocateNodeList(State->Permanent, 128);
State->OutputNode = PushOutputNodeOnList(State->NodeList, v2{500, 250}, State->Permanent);
{
State->NodeRenderSettings.PortColors[MemberType_r32] = RedV4;
State->NodeRenderSettings.PortColors[MemberType_s32] = GreenV4;
State->NodeRenderSettings.PortColors[MemberType_v4] = BlueV4;
State->NodeRenderSettings.Font = State->Font;
}
ReloadStaticData(Context, GlobalDebugServices, Alloc, Free);
{ // MODES PLAYGROUND
State->Modes.ActiveModesCount = 0;
// Setup Operation Modes
State->Modes.ActiveModesCount = 0;
State->Modes.Arena = {};
State->Modes.Arena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
State->Modes.Arena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
State->Modes.Arena.FindAddressRule = FindAddress_InLastBufferOnly;
s32 ModesMemorySize = Kilobytes(32);
u8* ModesMemory = PushSize(State->Permanent, ModesMemorySize);
InitMemoryArena(&State->Modes.Arena, ModesMemory, ModesMemorySize, 0);
}
{ // Animation PLAYGROUND
InitializeAnimationSystem(&State->AnimationSystem);
State->AnimationSystem.SecondsPerFrame = 1.f / 24.f;
State->AnimationSystem.AnimationStart = 0;
State->AnimationSystem.AnimationEnd = 15;
} // End Animation Playground
InitializePanelSystem(&State->PanelSystem);
panel* Panel = TakeNewPanel(&State->PanelSystem);
SetPanelDefinition(Panel, 0);
}
internal void
HandleInput (app_state* State, input_queue InputQueue, mouse_state Mouse)
HandleInput (app_state* State, rect WindowBounds, input_queue InputQueue, mouse_state Mouse)
{
DEBUG_TRACK_FUNCTION;
input_command_registry ActiveCommands = State->DefaultInputCommandRegistry;
if (State->Modes.ActiveModesCount > 0)
b32 PanelSystemHandledInput = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
if (!PanelSystemHandledInput)
{
ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
}
for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
{
input_entry Event = InputQueue.Entries[EventIdx];
// NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
// Down and Up over Held. In other words, we don't want to call a Held command on the
// frame when the button was released, even if the command is registered to both events
if (KeyTransitionedDown(Event))
input_command_registry ActiveCommands = {};
if (State->Modes.ActiveModesCount > 0)
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue);
ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
}
else if (KeyTransitionedUp(Event))
else
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
panel_and_bounds PanelWithMouseOverIt = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, WindowBounds);
if (!PanelWithMouseOverIt.Panel) { return; }
panel_definition PanelDefinition = GlobalPanelDefs[PanelWithMouseOverIt.Panel->PanelDefinitionIndex];
if (!PanelDefinition.InputCommands) { return; }
ActiveCommands.Commands = PanelDefinition.InputCommands;
ActiveCommands.Size = sizeof(*PanelDefinition.InputCommands) / sizeof(PanelDefinition.InputCommands[0]);
ActiveCommands.Used = ActiveCommands.Size;
}
else if (KeyHeldDown(Event))
for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue);
input_entry Event = InputQueue.Entries[EventIdx];
// NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
// Down and Up over Held. In other words, we don't want to call a Held command on the
// frame when the button was released, even if the command is registered to both events
if (KeyTransitionedDown(Event))
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue);
}
else if (KeyTransitionedUp(Event))
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
}
else if (KeyHeldDown(Event))
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue);
}
}
}
@ -377,6 +283,7 @@ CreateDMXBuffers(assembly Assembly, s32 BufferHeaderSize, memory_arena* Arena)
NewBuffer->Buffer.Base = PushArray(Arena, u8, BufferSize);
NewBuffer->Buffer.TotalSize = BufferSize;
NewBuffer->Buffer.HeaderSize = BufferHeaderSize;
NewBuffer->Next = 0;
// Append
if (!Result) {
@ -386,6 +293,7 @@ CreateDMXBuffers(assembly Assembly, s32 BufferHeaderSize, memory_arena* Arena)
Head->Next = NewBuffer;
Head = NewBuffer;
u8* DestChannel = Head->Buffer.Base + BufferHeaderSize;
for (s32 LEDIdx = LEDUniverseRange.RangeStart;
LEDIdx < LEDUniverseRange.RangeOnePastLast;
LEDIdx++)
@ -393,8 +301,11 @@ CreateDMXBuffers(assembly Assembly, s32 BufferHeaderSize, memory_arena* Arena)
led LED = Assembly.LEDs[LEDIdx];
pixel Color = Assembly.Colors[LED.Index];
s32 DestinationStartChannel = LEDIdx * 3;
*((pixel*)(Head->Buffer.Base + DestinationStartChannel)) = Color;
DestChannel[0] = Color.R;
DestChannel[1] = Color.G;
DestChannel[2] = Color.B;
DestChannel += 3;
}
}
@ -405,40 +316,62 @@ UPDATE_AND_RENDER(UpdateAndRender)
{
DEBUG_TRACK_FUNCTION;
app_state* State = (app_state*)Context.MemoryBase;
State->WindowBounds = Context.WindowBounds;
// NOTE(Peter): We do this at the beginning because all the render commands are stored in Transient,
// and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't
// zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically
// incorrect to clear the arena, and then access the memory later.
ClearArena(State->Transient);
ClearArena(&State->Transient);
HandleInput(State, InputQueue, Mouse);
HandleInput(State, State->WindowBounds, InputQueue, Mouse);
for (s32 AssemblyIndex = 0; AssemblyIndex < State->AssembliesCount; AssemblyIndex++)
{
assembly Assembly = State->AssemblyList[AssemblyIndex];
UpdateOutputNodeCalculations(State->OutputNode, State->NodeList,
State->Permanent, State->Transient,
Assembly.LEDs,
Assembly.Colors,
Assembly.LEDCount,
Context.DeltaTime);
ResetNodesUpdateState(State->NodeList);
if (State->AnimationSystem.TimelineShouldAdvance) {
State->AnimationSystem.Time += Context.DeltaTime;
if (State->AnimationSystem.Time > State->AnimationSystem.AnimationEnd)
{
State->AnimationSystem.Time -= State->AnimationSystem.AnimationEnd;
}
}
s32 CurrentFrame = (s32)(State->AnimationSystem.Time / State->AnimationSystem.SecondsPerFrame);
if (CurrentFrame != State->AnimationSystem.LastUpdatedFrame)
{
State->AnimationSystem.LastUpdatedFrame = CurrentFrame;
r32 FrameTime = CurrentFrame * State->AnimationSystem.SecondsPerFrame;
for (u32 i = 0; i < State->AnimationSystem.BlocksCount; i++)
{
animation_block_entry BlockEntry = State->AnimationSystem.Blocks[i];
if (!AnimationBlockIsFree(BlockEntry))
{
animation_block Block = BlockEntry.Block;
if (State->AnimationSystem.Time >= Block.StartTime
&& State->AnimationSystem.Time <= Block.EndTime)
{
for (s32 j = 0; j < State->ActiveAssemblyIndecies.Used; j++)
{
array_entry_handle* AssemblyHandle = GetElementAtIndex(j, State->ActiveAssemblyIndecies);
assembly* Assembly = GetElementWithHandle(*AssemblyHandle, State->AssemblyList);
Block.Proc(Assembly, FrameTime - Block.StartTime);
}
}
}
}
}
ClearTransientNodeColorBuffers(State->NodeList);
s32 HeaderSize = State->NetworkProtocolHeaderSize;
dmx_buffer_list* DMXBuffers = 0;
for (s32 i = 0; i < State->AssembliesCount; i++)
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
{
assembly Assembly = State->AssemblyList[i];
dmx_buffer_list* NewDMXBuffers = CreateDMXBuffers(Assembly, HeaderSize, State->Transient);
array_entry_handle* AssemblyHandle = GetElementAtIndex(i, State->ActiveAssemblyIndecies);
assembly* Assembly = GetElementWithHandle(*AssemblyHandle, State->AssemblyList);
dmx_buffer_list* NewDMXBuffers = CreateDMXBuffers(*Assembly, HeaderSize, &State->Transient);
DMXBuffers = DMXBufferListAppend(DMXBuffers, NewDMXBuffers);
}
DEBUG_IF(GlobalDebugServices->Interface.SendSACNData)
{
switch (State->NetworkProtocol)
{
case NetworkProtocol_SACN:
@ -453,139 +386,46 @@ UPDATE_AND_RENDER(UpdateAndRender)
CurrentDMXBuffer = CurrentDMXBuffer->Next;
}
send_sacn_job_data* Job = PushStruct(State->Transient, send_sacn_job_data);
send_sacn_job_data* Job = PushStruct(&State->Transient, send_sacn_job_data);
Job->SendSocket = State->SACN.SendSocket;
Job->GetSendAddress = Context.PlatformGetSendAddress;
Job->SendTo = Context.PlatformSendTo;
Job->DMXBuffers = DMXBuffers;
Context.GeneralWorkQueue->PushWorkOnQueue(
Context.GeneralWorkQueue,
SACNSendDMXBufferListJob,
Job);
Context.GeneralWorkQueue,
SACNSendDMXBufferListJob,
Job);
}break;
InvalidDefaultCase;
}
}
////////////////////////////////
// Render Assembly
///////////////////////////////
if (Context.WindowIsVisible)
PushRenderOrthographic(RenderBuffer, 0, 0, Width(State->WindowBounds), Height(State->WindowBounds));
PushRenderClearScreen(RenderBuffer);
panel_layout PanelsToRender = GetPanelLayout(&State->PanelSystem, State->WindowBounds, &State->Transient);
DrawAllPanels(PanelsToRender, RenderBuffer, Mouse, State, Context);
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
{
State->Camera.AspectRatio = (r32)Context.WindowWidth / (r32)Context.WindowHeight;
m44 ModelViewMatrix = GetCameraModelViewMatrix(State->Camera);
m44 ProjectionMatrix = GetCameraPerspectiveProjectionMatrix(State->Camera);
r32 LEDHalfWidth = .5f;
PushRenderPerspective(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHeight, State->Camera);
PushRenderClearScreen(RenderBuffer);
// TODO(Peter): Pretty sure this isn't working right now
m44 FaceCameraMatrix = GetLookAtMatrix(v4{0, 0, 0, 1}, V4(State->Camera.Position, 1));
FaceCameraMatrix = FaceCameraMatrix;
DEBUG_IF(GlobalDebugServices->Interface.RenderSculpture) // DebugServices RenderSculpture Toggle
operation_mode OperationMode = State->Modes.ActiveModes[m];
if (OperationMode.Render != 0)
{
DEBUG_TRACK_SCOPE(RenderSculpture);
s32 MaxLEDsPerJob = 2048;
render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->TotalLEDsCount);
for (s32 AssemblyIdx = 0; AssemblyIdx < State->AssembliesCount; AssemblyIdx++)
{
assembly Assembly = State->AssemblyList[AssemblyIdx];
s32 JobsNeeded = IntegerDivideRoundUp(Assembly.LEDCount, MaxLEDsPerJob);
for (s32 Job = 0; Job < JobsNeeded; Job++)
{
draw_leds_job_data* JobData = PushStruct(State->Transient, draw_leds_job_data);
JobData->LEDs = Assembly.LEDs;
JobData->Colors = Assembly.Colors;
JobData->StartIndex = Job * MaxLEDsPerJob;
JobData->OnePastLastIndex = GSMin(JobData->StartIndex + MaxLEDsPerJob, Assembly.LEDCount);
JobData->Batch = &RenderLEDsBatch;
JobData->FaceCameraMatrix;
JobData->ModelViewMatrix = ModelViewMatrix;
JobData->LEDHalfWidth = LEDHalfWidth;
Context.GeneralWorkQueue->PushWorkOnQueue(
Context.GeneralWorkQueue,
DrawLEDsInBufferRangeJob,
JobData);
}
}
Context.GeneralWorkQueue->DoQueueWorkUntilDone(Context.GeneralWorkQueue, 0);
Context.GeneralWorkQueue->ResetWorkQueue(Context.GeneralWorkQueue);
OperationMode.Render(State, RenderBuffer, OperationMode, Mouse);
}
}
///////////////////////////////////////
// Interface
//////////////////////////////////////
DEBUG_TRACK_SCOPE(DrawInterface);
PushRenderOrthographic(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHeight);
///////////////////////////////////////
// Menu Bar
//////////////////////////////////////
r32 TopBarHeight = 40;
// Checking for overflows
{
DEBUG_TRACK_SCOPE(OverflowChecks);
AssertAllocationsNoOverflow(State->Permanent);
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
{
panel_result TopBarPanel = EvaluatePanel(RenderBuffer,
v2{0, Context.WindowHeight - TopBarHeight},
v2{Context.WindowWidth, Context.WindowHeight},
0, State->Interface);
v2 ButtonDim = v2{200, (r32)NewLineYOffset(*State->Interface.Font) + 10};
v2 ButtonPos = v2{State->Interface.Margin.x, Context.WindowHeight - (ButtonDim.y + 10)};
button_result LoadAssemblyBtn = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
MakeStringLiteral("Load Assembly"),
State->Interface, Mouse);
string InterfaceString = MakeString(PushArray(State->Transient, char, 256), 256);
for (int i = 0; i < State->AssembliesCount; i++)
{
PrintF(&InterfaceString, "Unload %.*s", State->AssemblyList[i].Name.Length, State->AssemblyList[i].Name.Memory);
ButtonPos.x += ButtonDim.x + 10;
button_result UnloadAssemblyBtn = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
InterfaceString, State->Interface, Mouse);
if (UnloadAssemblyBtn.Pressed)
{
UnloadAssembly(i, State, Context);
}
}
if (LoadAssemblyBtn.Pressed)
{
char FilePath[256];
b32 Success = Context.PlatformGetFilePath(FilePath, 256, "Foldhaus Files\0*.fold\0\0");
if (Success)
{
LoadAssembly(State, Context, FilePath);
}
}
array_entry_handle* AssemblyHandle = GetElementAtIndex(i, State->ActiveAssemblyIndecies);
assembly* Assembly = GetElementWithHandle(*AssemblyHandle, State->AssemblyList);
AssertAllocationsNoOverflow(Assembly->Arena);
}
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
{
operation_mode OperationMode = State->Modes.ActiveModes[m];
if (OperationMode.Render != 0)
{
OperationMode.Render(State, RenderBuffer, OperationMode, Mouse);
}
}
DrawDebugInterface(RenderBuffer, 25,
State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight,
Context.DeltaTime, State, State->Camera, Mouse, State->Transient);
}
}

View File

@ -13,18 +13,21 @@
#include "foldhaus_node.h"
#include "assembly_parser.cpp"
#include "test_patterns.h"
#include "foldhaus_interface.h"
// TODO(Peter): something we can do later is to remove all reliance on app_state and context
// from foldhaus_pane.h. It should just emit lists of things that the app can iterate over and
// perform operations on, like panel_draw_requests = { bounds, panel* } etc.
#include "foldhaus_panel.h"
typedef struct app_state app_state;
#include "foldhaus_command_dispatch.h"
#include "foldhaus_command_dispatch.cpp"
#include "foldhaus_operation_mode.h"
#include "animation/foldhaus_animation.h"
#include "foldhaus_text_entry.h"
#include "foldhaus_default_nodes.h"
#include "generated/foldhaus_nodes_generated.cpp"
#include "foldhaus_search_lister.h"
enum network_protocol
@ -37,9 +40,10 @@ enum network_protocol
struct app_state
{
memory_arena* Permanent;
memory_arena* Transient;
memory_arena SACNMemory;
rect WindowBounds;
memory_arena Permanent;
memory_arena Transient;
s32 NetworkProtocolHeaderSize;
network_protocol NetworkProtocol;
@ -47,37 +51,168 @@ struct app_state
streaming_acn SACN;
s32 TotalLEDsCount;
// TODO(Peter): Make this dynamic. We want them contiguous in memory since we'll be accessing them
// mostly by looping through them. On the other hand, I don't expect there to ever be more than 100
// of them at once.
#define ASSEMBLY_LIST_LENGTH 32
assembly AssemblyList[ASSEMBLY_LIST_LENGTH];
s32 AssembliesCount;
assembly_array AssemblyList;
array_entry_handle_contiguous_array ActiveAssemblyIndecies;
camera Camera;
r32 PixelsToWorldScale;
operation_mode_system Modes;
input_command_registry DefaultInputCommandRegistry;
input_command_queue CommandQueue;
text_entry ActiveTextEntry;
node_list* NodeList;
node_header* OutputNode;
node_render_settings NodeRenderSettings;
bitmap_font* Font;
interface_config Interface;
animation_system AnimationSystem;
animation_block_handle SelectedAnimationBlockHandle;
panel_system PanelSystem;
};
internal void OpenColorPicker(app_state* State, v4* Address);
// BEGIN TEMPORARY PATTERNS
internal void
TestPatternOne(assembly* Assembly, r32 Time)
{
for (s32 Range = 0; Range < Assembly->LEDUniverseMapCount; Range++)
{
leds_in_universe_range LEDUniverseRange = Assembly->LEDUniverseMap[Range];
for (s32 LEDIdx = LEDUniverseRange.RangeStart;
LEDIdx < LEDUniverseRange.RangeOnePastLast;
LEDIdx++)
{
led LED = Assembly->LEDs[LEDIdx];
Assembly->Colors[LED.Index].R = 255;
Assembly->Colors[LED.Index].B = 255;
Assembly->Colors[LED.Index].G = 255;
}
}
}
internal void
TestPatternTwo(assembly* Assembly, r32 Time)
{
r32 PeriodicTime = (Time / PI) * 2;
r32 ZeroOneSin = (GSSin(PeriodicTime) * .5f) + .5f;
r32 ZeroOneCos = (GSCos(PeriodicTime) * .5f) + .5f;
pixel Color = { (u8)(ZeroOneSin * 255), 0, (u8)(ZeroOneCos * 255) };
v4 Center = v4{0, 0, 0, 1};
r32 ThetaZ = Time / 2;
v4 Normal = v4{GSCos(ThetaZ), 0, GSSin(ThetaZ), 0}; // NOTE(Peter): dont' need to normalize. Should always be 1
v4 Right = Cross(Normal, v4{0, 1, 0, 0});
v4 FrontCenter = Center + (Normal * 25);
v4 BackCenter = Center - (Normal * 25);
r32 OuterRadiusSquared = 1000000;
r32 InnerRadiusSquared = 0;
for (s32 Range = 0; Range < Assembly->LEDUniverseMapCount; Range++)
{
leds_in_universe_range LEDUniverseRange = Assembly->LEDUniverseMap[Range];
for (s32 LEDIdx = LEDUniverseRange.RangeStart;
LEDIdx < LEDUniverseRange.RangeOnePastLast;
LEDIdx++)
{
led LED = Assembly->LEDs[LEDIdx];
v4 Position = LED.Position;
v4 ToFront = Position + FrontCenter;
v4 ToBack = Position + BackCenter;
r32 ToFrontDotNormal = Dot(ToFront, Normal);
r32 ToBackDotNormal = Dot(ToBack, Normal);
ToFrontDotNormal = GSClamp01(ToFrontDotNormal * 1000);
ToBackDotNormal = GSClamp01(ToBackDotNormal * 1000);
r32 SqDistToCenter = MagSqr(Position);
if (SqDistToCenter < OuterRadiusSquared && SqDistToCenter > InnerRadiusSquared)
{
if (XOR(ToFrontDotNormal > 0, ToBackDotNormal > 0))
{
Assembly->Colors[LED.Index] = Color;
}
else
{
Assembly->Colors[LED.Index] = {};
}
}
else
{
Assembly->Colors[LED.Index] = {};
}
}
}
}
internal void
TestPatternThree(assembly* Assembly, r32 Time)
{
r32 GreenSize = 20.0f;
r32 BlueSize = 25.0f;
r32 RedSize = 25.0f;
r32 GreenPosition = -GreenSize + (Time * 45);
r32 BluePosition = -BlueSize + (Time * 25);
r32 RedPosition = (100 + RedSize) + (Time * -35);
for (s32 Range = 0; Range < Assembly->LEDUniverseMapCount; Range++)
{
leds_in_universe_range LEDUniverseRange = Assembly->LEDUniverseMap[Range];
for (s32 LEDIdx = LEDUniverseRange.RangeStart;
LEDIdx < LEDUniverseRange.RangeOnePastLast;
LEDIdx++)
{
led LED = Assembly->LEDs[LEDIdx];
u8 Red = 0;
u8 Green = 0;
u8 Blue = 0;
r32 GreenDistance = GSAbs(LED.Position.z - GreenPosition);
r32 GreenBrightness = GSClamp(0.0f, GreenSize - GreenDistance, GreenSize) / GreenSize;
Green = (u8)(GreenBrightness * 255);
r32 BlueDistance = GSAbs(LED.Position.z - BluePosition);
r32 BlueBrightness = GSClamp(0.0f, BlueSize - BlueDistance, BlueSize) / BlueSize;
Blue = (u8)(BlueBrightness * 255);
r32 RedDistance = GSAbs(LED.Position.z - RedPosition);
r32 RedBrightness = GSClamp(0.0f, RedSize - RedDistance, RedSize) / RedSize;
Red = (u8)(RedBrightness * 255);
Assembly->Colors[LED.Index].R = Red;
Assembly->Colors[LED.Index].B = Blue;
Assembly->Colors[LED.Index].G = Green;
}
}
}
// END TEMPORARY PATTERNS
#include "foldhaus_assembly.cpp"
#include "foldhaus_node.cpp"
#include "foldhaus_debug_visuals.h"
//#include "foldhaus_sacn_view.cpp"
#include "foldhaus_text_entry.cpp"
#include "foldhaus_search_lister.cpp"
#define PANEL_INIT_PROC(name) void name(panel* Panel)
typedef PANEL_INIT_PROC(panel_init_proc);
#define PANEL_CLEANUP_PROC(name) void name(panel* Panel)
typedef PANEL_CLEANUP_PROC(panel_cleanup_proc);
#define PANEL_RENDER_PROC(name) void name(panel Panel, rect PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
typedef PANEL_RENDER_PROC(panel_render_proc);
#include "panels/foldhaus_panel_sculpture_view.h"
#include "panels/foldhaus_panel_profiler.h"
#include "panels/foldhaus_panel_dmx_view.h"
#include "panels/foldhaus_panel_animation_timeline.h"
#include "panels/foldhaus_panel_hierarchy.h"
#include "generated/foldhaus_panels_generated.h"
#include "foldhaus_interface.cpp"

View File

@ -10,13 +10,12 @@ GetAssemblyMemorySizeFromDefinition(assembly_definition Definition, string Name)
internal assembly
ConstructAssemblyFromDefinition (assembly_definition Definition,
string AssemblyName,
v3 RootPosition,
v4 RootPosition,
r32 Scale,
u8* MemoryBase,
s32 MemorySize)
memory_arena Arena)
{
assembly Assembly = {};
Assembly.Arena = CreateMemoryArena(MemoryBase, MemorySize);
Assembly.Arena = Arena;
Assembly.Name = MakeString(PushArray(&Assembly.Arena, char, AssemblyName.Length), AssemblyName.Length);
CopyStringTo(AssemblyName, &Assembly.Name);
@ -44,8 +43,8 @@ ConstructAssemblyFromDefinition (assembly_definition Definition,
// now. The assert is to remind you to create more cases when necessary
Assert(StripDef.InterpolationType == StripInterpolate_Points);
v4 WS_StripStart = V4(StripDef.InterpolatePositionStart * Scale, 1);
v4 WS_StripEnd = V4(StripDef.InterpolatePositionEnd * Scale, 1);
v4 WS_StripStart = RootPosition + V4(StripDef.InterpolatePositionStart * Scale, 1);
v4 WS_StripEnd = RootPosition + V4(StripDef.InterpolatePositionEnd * Scale, 1);
s32 LEDsInStripCount = StripDef.LEDsPerStrip;
Assert(Assembly.LEDCount + LEDsInStripCount <= Definition.TotalLEDCount);
@ -63,3 +62,56 @@ ConstructAssemblyFromDefinition (assembly_definition Definition,
Assert(Assembly.LEDCount == Definition.TotalLEDCount);
return Assembly;
}
static v4 TempAssemblyOffsets[] = { v4{0, 0, 0, 0}, v4{250, 0, 75, 0}, v4{-250, 0, 75, 0} };
s32 TempAssemblyOffsetsCount = 3;
internal void
LoadAssembly (app_state* State, context Context, char* Path)
{
platform_memory_result TestAssemblyFile = Context.PlatformReadEntireFile(Path);
Assert(TestAssemblyFile.Size > 0);
assembly_definition AssemblyDefinition = ParseAssemblyFile(TestAssemblyFile.Base, TestAssemblyFile.Size, &State->Transient);
Context.PlatformFree(TestAssemblyFile.Base, TestAssemblyFile.Size);
string PathString = MakeStringLiteral(Path);
s32 IndexOfLastSlash = FastLastIndexOfCharInCharArray(PathString.Memory, PathString.Length, '\\');
string FileName = Substring(PathString, IndexOfLastSlash + 1);
r32 Scale = 100;
memory_arena AssemblyArena = {};
AssemblyArena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc;
AssemblyArena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc;
v4 Offset = TempAssemblyOffsets[State->ActiveAssemblyIndecies.Used % TempAssemblyOffsetsCount];
assembly NewAssembly = ConstructAssemblyFromDefinition(AssemblyDefinition,
FileName,
Offset,
Scale,
AssemblyArena);
array_entry_handle NewAssemblyHandle = PushElement(NewAssembly, &State->AssemblyList);
PushElement(NewAssemblyHandle, &State->ActiveAssemblyIndecies);
State->TotalLEDsCount += NewAssembly.LEDCount;
}
internal void
UnloadAssembly (s32 AssemblyIndex, app_state* State, context Context)
{
assembly* Assembly = GetElementAtIndex(AssemblyIndex, State->AssemblyList);
State->TotalLEDsCount -= Assembly->LEDCount;
FreeMemoryArena(&Assembly->Arena, (gs_memory_free*)Context.PlatformFree);
RemoveElementAtIndex(AssemblyIndex, &State->AssemblyList);
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
{
array_entry_handle Handle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
if (Handle.Index == AssemblyIndex)
{
RemoveElementAtIndex(i, &State->ActiveAssemblyIndecies);
break;
}
}
}

View File

@ -24,7 +24,7 @@ struct leds_in_universe_range
struct assembly
{
static_memory_arena Arena;
memory_arena Arena;
string Name;
string FilePath;
@ -36,3 +36,5 @@ struct assembly
s32 LEDUniverseMapCount;
leds_in_universe_range* LEDUniverseMap;
};
TYPEDEF_ARRAY(assembly);

View File

@ -1,149 +0,0 @@
internal void
InitializeInputCommandRegistry (input_command_registry* CommandRegistry,
s32 Size,
memory_arena* Storage)
{
CommandRegistry->Commands = PushArray(Storage, input_command, Size);
CommandRegistry->Size = Size;
CommandRegistry->Used = 0;
}
internal void
RegisterMouseWheelCommand (input_command_registry* CommandRegistry,
input_command_proc* Proc)
{
CommandRegistry->MouseWheelCommand = Proc;
}
internal s32
GetCommandIndexInQueue(input_command_queue* Queue, input_command Command, input_entry Event)
{
s32 Result = -1;
for (s32 CommandIndex = 0; CommandIndex < Queue->Used; CommandIndex++)
{
command_queue_entry* Entry = Queue->Commands + CommandIndex;
if(Entry->Event.Key == Event.Key)
{
Result = CommandIndex;
break;
}
}
return Result;
}
internal input_command_queue
InitializeCommandQueue(command_queue_entry* Memory, s32 MemorySize)
{
input_command_queue Result = {};
Result.Size = MemorySize;
Result.Used = 0;
Result.Commands = Memory;
return Result;
}
internal void
ClearCommandQueue(input_command_queue* Queue)
{
Queue->Used = 0;
}
internal void
PushCommandOnQueue(input_command_queue* Queue, input_command Command, input_entry Event)
{
Assert(Queue->Used < Queue->Size);
command_queue_entry Entry = {};
Entry.Command = Command;
Entry.Event = Event;
Queue->Commands[Queue->Used++] = Entry;
}
internal void
RemoveCommandFromQueue(input_command_queue* Queue, s32 Index)
{
s32 CommandIndex = Index;
if (CommandIndex < Queue->Used)
{
Queue->Used -= 1;
for (; CommandIndex < Queue->Used; CommandIndex++)
{
Queue->Commands[CommandIndex] = Queue->Commands[CommandIndex + 1];
}
}
}
internal void
RemoveCommandFromQueue(input_command_queue* Queue, input_command Command, input_entry Event)
{
s32 CommandIndex = GetCommandIndexInQueue(Queue, Command, Event);
// NOTE(Peter): If we made it through the queue without finding an event, there wasn't one
// to remove. This happens when we've changed command registries as a result of an input command,
// and the command exists in the new registry.
// For example:
// clicking a mouse button triggers a command to switch registries
// the new registry tracks mouse drag (persist until release)
// when the mouse is released, the event fires, but there is no mouse down event to remove
// For this reason, I'm allowing the case where we try and remove a command where non exists
// I don't think this is a great solution but Im not super familiar with the codebase right now
// so leaving it as is. revisit if it becomes a problem.
RemoveCommandFromQueue(Queue, CommandIndex);
}
internal input_command*
FindExistingCommand (input_command_registry CommandRegistry, key_code Key, key_code Mdfr, b32 Flags)
{
input_command* Result = 0;
for (s32 Cmd = 0; Cmd < CommandRegistry.Used; Cmd++)
{
input_command* Command = CommandRegistry.Commands + Cmd;
if (Command->Key == Key && Command->Mdfr == Mdfr)
{
b32 FlagsOverlap = Flags & Command->Flags;
if (FlagsOverlap)
{
Result = Command;
break;
}
}
}
return Result;
}
internal b32
FindAndPushExistingCommand(input_command_registry CommandRegistry, input_entry Event, b32 Flags, input_command_queue* CommandQueue)
{
b32 CommandFound = false;
input_command* Command = FindExistingCommand(CommandRegistry, Event.Key, (key_code)0, Flags);
if (Command)
{
PushCommandOnQueue(CommandQueue, *Command, Event);
CommandFound = true;
}
return CommandFound;
}
internal void
RegisterKeyPressCommand (input_command_registry* CommandRegistry,
key_code Key,
b32 Flags,
key_code Mdfr,
input_command_proc* Proc)
{
input_command* Command = FindExistingCommand(*CommandRegistry, Key, Mdfr, Flags);
if (!Command)
{
Assert(CommandRegistry->Size > CommandRegistry->Used);
Assert(Mdfr == KeyCode_Invalid || Mdfr == KeyCode_LeftShift || Mdfr == KeyCode_RightShift ||
Mdfr == KeyCode_LeftCtrl || Mdfr == KeyCode_RightCtrl || Mdfr == KeyCode_Alt);
Command = CommandRegistry->Commands + CommandRegistry->Used++;
}
Command->Key = Key;
Command->Flags = Flags;
Command->Mdfr = Mdfr;
Command->Proc = Proc;
}

View File

@ -44,3 +44,153 @@ struct input_command_queue
s32 Used;
command_queue_entry* Commands;
};
internal void
InitializeInputCommandRegistry (input_command_registry* CommandRegistry,
s32 Size,
memory_arena* Storage)
{
CommandRegistry->Commands = PushArray(Storage, input_command, Size);
CommandRegistry->Size = Size;
CommandRegistry->Used = 0;
}
internal void
RegisterMouseWheelCommand (input_command_registry* CommandRegistry,
input_command_proc* Proc)
{
CommandRegistry->MouseWheelCommand = Proc;
}
internal s32
GetCommandIndexInQueue(input_command_queue* Queue, input_command Command, input_entry Event)
{
s32 Result = -1;
for (s32 CommandIndex = 0; CommandIndex < Queue->Used; CommandIndex++)
{
command_queue_entry* Entry = Queue->Commands + CommandIndex;
if(Entry->Event.Key == Event.Key)
{
Result = CommandIndex;
break;
}
}
return Result;
}
internal input_command_queue
InitializeCommandQueue(command_queue_entry* Memory, s32 MemorySize)
{
input_command_queue Result = {};
Result.Size = MemorySize;
Result.Used = 0;
Result.Commands = Memory;
return Result;
}
internal void
ClearCommandQueue(input_command_queue* Queue)
{
Queue->Used = 0;
}
internal void
PushCommandOnQueue(input_command_queue* Queue, input_command Command, input_entry Event)
{
Assert(Queue->Used < Queue->Size);
command_queue_entry Entry = {};
Entry.Command = Command;
Entry.Event = Event;
Queue->Commands[Queue->Used++] = Entry;
}
internal void
RemoveCommandFromQueue(input_command_queue* Queue, s32 Index)
{
s32 CommandIndex = Index;
if (CommandIndex < Queue->Used)
{
Queue->Used -= 1;
for (; CommandIndex < Queue->Used; CommandIndex++)
{
Queue->Commands[CommandIndex] = Queue->Commands[CommandIndex + 1];
}
}
}
internal void
RemoveCommandFromQueue(input_command_queue* Queue, input_command Command, input_entry Event)
{
s32 CommandIndex = GetCommandIndexInQueue(Queue, Command, Event);
// NOTE(Peter): If we made it through the queue without finding an event, there wasn't one
// to remove. This happens when we've changed command registries as a result of an input command,
// and the command exists in the new registry.
// For example:
// clicking a mouse button triggers a command to switch registries
// the new registry tracks mouse drag (persist until release)
// when the mouse is released, the event fires, but there is no mouse down event to remove
// For this reason, I'm allowing the case where we try and remove a command where non exists
// I don't think this is a great solution but Im not super familiar with the codebase right now
// so leaving it as is. revisit if it becomes a problem.
RemoveCommandFromQueue(Queue, CommandIndex);
}
internal input_command*
FindExistingCommand (input_command_registry CommandRegistry, key_code Key, key_code Mdfr, b32 Flags)
{
input_command* Result = 0;
for (s32 Cmd = 0; Cmd < CommandRegistry.Used; Cmd++)
{
input_command* Command = CommandRegistry.Commands + Cmd;
if (Command->Key == Key && Command->Mdfr == Mdfr)
{
b32 FlagsOverlap = Flags & Command->Flags;
if (FlagsOverlap)
{
Result = Command;
break;
}
}
}
return Result;
}
internal b32
FindAndPushExistingCommand(input_command_registry CommandRegistry, input_entry Event, b32 Flags, input_command_queue* CommandQueue)
{
b32 CommandFound = false;
input_command* Command = FindExistingCommand(CommandRegistry, Event.Key, (key_code)0, Flags);
if (Command)
{
PushCommandOnQueue(CommandQueue, *Command, Event);
CommandFound = true;
}
return CommandFound;
}
internal void
RegisterKeyPressCommand (input_command_registry* CommandRegistry,
key_code Key,
b32 Flags,
key_code Mdfr,
input_command_proc* Proc)
{
input_command* Command = FindExistingCommand(*CommandRegistry, Key, Mdfr, Flags);
if (!Command)
{
Assert(CommandRegistry->Size > CommandRegistry->Used);
Assert(Mdfr == KeyCode_Invalid || Mdfr == KeyCode_LeftShift || Mdfr == KeyCode_RightShift ||
Mdfr == KeyCode_LeftCtrl || Mdfr == KeyCode_RightCtrl || Mdfr == KeyCode_Alt);
Command = CommandRegistry->Commands + CommandRegistry->Used++;
}
Command->Key = Key;
Command->Flags = Flags;
Command->Mdfr = Mdfr;
Command->Proc = Proc;
}

View File

@ -1,194 +1,3 @@
internal void
RenderProfiler_ScopeVisualization(render_command_buffer* RenderBuffer,
interface_config Interface, mouse_state Mouse,
v2 Min, v2 Max,
debug_frame* VisibleFrame, memory_arena* Memory)
{
v4 ThreadColors[] = {
v4{.73f, .33f, .83f, 1},
v4{0, .50f, .50f, 1},
v4{.83f, 0, 0, 1},
v4{.33f, .49f, .83f, 1},
v4{.74f, .40f, .25f, 1},
};
r32 Width = Max.x - Min.x;
r32 DepthHeight = 64;
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
debug_scope_record_list* ThreadScopeCalls = GetScopeListForThreadInFrame(GlobalDebugServices,
VisibleFrame);
MakeStringBuffer(String, 256);
for (s32 i = 0; i < ThreadScopeCalls->Count; i++)
{
scope_record* Record = ThreadScopeCalls->Calls + i;
scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash);
r32 PercentStart = (r32)(Record->StartCycles - FrameStartCycles) / (r32)FrameTotalCycles;
r32 PercentEnd = (r32)(Record->EndCycles - FrameStartCycles) / (r32)FrameTotalCycles;
v2 ScopeMin = v2{Min.x + (Width * PercentStart), Max.y - ((Record->CallDepth + 1) * DepthHeight)};
v2 ScopeMax = v2{Min.x + (Width * PercentEnd), ScopeMin.y + (DepthHeight - 4)};
if ((ScopeMax.x - ScopeMin.x) >= 1)
{
v4 Color = ThreadColors[0];
if (PointIsInRange(Mouse.Pos, ScopeMin, ScopeMax))
{
Color = GreenV4;
}
PushRenderQuad2D(RenderBuffer, ScopeMin, ScopeMax, Color);
PushRenderBoundingBox2D(RenderBuffer, ScopeMin, ScopeMax, 1, BlackV4);
if (PointIsInRange(Mouse.Pos, ScopeMin, ScopeMax))
{
PushRenderQuad2D(RenderBuffer, Mouse.Pos, Mouse.Pos + v2{256, 32}, BlackV4);
PrintF(&String, "%.*s : %d - %d", Name->Name.Length, Name->Name.Memory, Record->StartCycles, Record->EndCycles);
DrawString(RenderBuffer, String, Interface.Font, Mouse.Pos, WhiteV4);
}
}
}
}
internal void
RenderProfiler_ListVisualization(render_command_buffer* RenderBuffer,
interface_config Interface, mouse_state Mouse,
v2 Min, v2 Max,
debug_frame* VisibleFrame, memory_arena* Memory)
{
MakeStringBuffer(String, 256);
r32 YAt = Max.y - Interface.Font->PixelHeight;
r32 Column0X = Min.x;
r32 Column1X = Column0X + 256;
r32 Column2X = Column1X + 128;
r32 Column3X = Column2X + 128;
r32 Column4X = Column3X + 100;
for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
{
scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
if (NameEntry.Hash != 0)
{
collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
PrintF(&String, "%.*s", NameEntry.Name.Length, NameEntry.Name.Memory);
DrawString(RenderBuffer, String, Interface.Font, v2{Column0X, YAt}, WhiteV4);
PrintF(&String, "%f", CollatedRecord->PercentFrameTime);
DrawString(RenderBuffer, String, Interface.Font, v2{Column1X, YAt}, WhiteV4);
PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
DrawString(RenderBuffer, String, Interface.Font, v2{Column2X, YAt}, WhiteV4);
PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
DrawString(RenderBuffer, String, Interface.Font, v2{Column3X, YAt}, WhiteV4);
PrintF(&String, "%d calls", CollatedRecord->CallCount);
DrawString(RenderBuffer, String, Interface.Font, v2{Column4X, YAt}, WhiteV4);
YAt -= Interface.Font->PixelHeight + 4;
if (YAt < Min.y) { break; }
}
}
}
internal void
DrawDebugFrameList (render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, v2 BoundsMin, v2 BoundsMax, memory_arena* Memory)
{
string String = InitializeEmptyString(PushArray(Memory, char, 256), 256);
v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 };
r32 FrameListHeight = 64;
v2 FrameListMin = v2{BoundsMin.x + 16, BoundsMax.y - (16 + FrameListHeight)};
v2 FrameListMax = v2{BoundsMax.x - 16, BoundsMax.y - 16};
r32 FrameListPadding = 4;
r32 FrameListInnerWidth = (FrameListMax.x - FrameListMin.x) - (FrameListPadding * 2);
r32 SingleFrameStep = FrameListInnerWidth / DEBUG_FRAME_COUNT;
r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2);
PushRenderBoundingBox2D(RenderBuffer, FrameListMin, FrameListMax, 2, WhiteV4);
if (PointIsInRange(Mouse.Pos, FrameListMin, FrameListMax) &&
MouseButtonHeldDown(Mouse.LeftButtonState))
{
r32 LocalMouseX = (Mouse.Pos.x - FrameListMin.x) + FrameListPadding;
s32 ClosestFrameIndex = (LocalMouseX / SingleFrameStep);
if (ClosestFrameIndex >= 0 && ClosestFrameIndex < DEBUG_FRAME_COUNT)
{
GlobalDebugServices->RecordFrames = false;
GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex;
}
}
for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++)
{
v2 Min = v2{FrameListMin.x + FrameListPadding + (F * SingleFrameStep), FrameListMin.y + 4};
v2 Max = v2{Min.x + SingleFrameWidth, FrameListMax.y - 4};
s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F);
if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; }
v4 Color = FrameColors[GSClamp(0, FramesAgo, 3)];
PushRenderQuad2D(RenderBuffer, Min, Max, Color);
}
debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
PrintF(&String, "Frame %d - Total Cycles: %lld",
GlobalDebugServices->CurrentDebugFrame - 1,
FrameTotalCycles);
DrawString(RenderBuffer, String, Interface.Font, FrameListMin - v2{0, 32}, WhiteV4);
v2 ButtonMin = v2{FrameListMax.x - 128, FrameListMin.y - 32};
v2 ButtonMax = ButtonMin + v2{128, 28};
button_result ShouldResumeRecording = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
MakeString("Resume Recording"), Interface, Mouse);
if (ShouldResumeRecording.Pressed)
{
GlobalDebugServices->RecordFrames = true;
}
ButtonMin = v2{FrameListMin.x, FrameListMin.y - 60};
ButtonMax = v2{FrameListMin.x + 128, FrameListMin.y - 42};
button_result ActivateScopeView = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
MakeString("Scope View"), Interface, Mouse);
ButtonMin.x += 152;
ButtonMax.x += 152;
button_result ActivateListView = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
MakeString("List View"), Interface, Mouse);
if (ActivateScopeView.Pressed) { GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER; }
if (ActivateListView.Pressed) { GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST; }
v2 ViewModeMin = v2{FrameListMin.x, BoundsMin.y};
v2 ViewModeMax = v2{FrameListMax.x, FrameListMin.y - 96};
if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER)
{
RenderProfiler_ScopeVisualization(RenderBuffer, Interface, Mouse,
ViewModeMin, ViewModeMax,
VisibleFrame, Memory);
}
else
{
RenderProfiler_ListVisualization(RenderBuffer, Interface, Mouse,
ViewModeMin, ViewModeMax,
VisibleFrame, Memory);
}
}
internal void
DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_config Interface, r32 WindowWidth, r32 WindowHeight, r32 DeltaTime, app_state* State, camera Camera, mouse_state Mouse, memory_arena* Transient)
{
@ -197,8 +6,6 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
v2 TopOfDebugView = v2{StartX, WindowHeight - (NewLineYOffset(*Interface.Font) + 5)};
v2 TopOfScreenLinePos = TopOfDebugView;
arena_snapshot StartTempMemory = TakeSnapshotOfArena(*Transient);
string DebugString = InitializeEmptyString(PushArray(Transient, char, 256), 256);
if (GlobalDebugServices->Interface.ShowCameraMouse)
@ -211,12 +18,12 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
r32 FramesPerSecond = 1.0f / DeltaTime;
PrintF(&DebugString, "Framerate: %.*f s %d fps | Modes: %d Memory Used: %d / %d | Commands: %d",
PrintF(&DebugString, "Framerate: %.*f s %d fps | Modes: %d Memory Used: %d / %d | Commands: %d | HI SAM!!!! ",
5, DeltaTime,
(u32)FramesPerSecond,
State->Modes.ActiveModesCount,
State->Modes.Arena.CurrentRegion->Used,
State->Modes.Arena.CurrentRegion->Size,
State->Modes.Arena.TotalUsed,
State->Modes.Arena.TotalSize,
State->CommandQueue.Used);
DrawString(RenderBuffer, DebugString, Interface.Font, TopOfScreenLinePos, WhiteV4);
@ -298,13 +105,6 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
TopOfScreenLinePos, WhiteV4);
TopOfScreenLinePos.y -= NewLineYOffset(*Interface.Font);
PrintF(&DebugString, "Nodes Memory: %d / %d",
State->NodeList->TotalUsed,
State->NodeList->TotalMax);
DrawString(RenderBuffer, DebugString, Interface.Font,
TopOfScreenLinePos, WhiteV4);
TopOfScreenLinePos.y -= NewLineYOffset(*Interface.Font);
PrintF(&DebugString, "Render Buffer: %d / %d (at this point)",
RenderBuffer->CommandMemoryUsed,
RenderBuffer->CommandMemorySize);
@ -312,58 +112,4 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
TopOfScreenLinePos, WhiteV4);
TopOfScreenLinePos.y -= NewLineYOffset(*Interface.Font);
}
if (GlobalDebugServices->Interface.ShowTrackedScopes)
{
v2 ProfilerMin = v2{TopOfDebugView.x, TopOfDebugView.y - 500};
v2 ProfilerMax = v2{TopOfDebugView.x + 700, TopOfDebugView.y - 64};
PushRenderQuad2D(RenderBuffer, ProfilerMin, ProfilerMax, v4{0, 0, 0, .8f});
DrawDebugFrameList(RenderBuffer, Interface, Mouse, ProfilerMin, ProfilerMax, Transient);
#if 0
r32 ColumnsStartX = TopOfScreenLinePos.x;
for (s32 i = 0; i < GlobalDebugServices->ScopeHistogramUsed; i++)
{
v2 Register = v2{ColumnsStartX, TopOfScreenLinePos.y};
s32 CurrentFrame = GlobalDebugServices->ScopeHistogramSorted[i].CurrentFrame - 1;
if (CurrentFrame < 0) { CurrentFrame = HISTOGRAM_DEPTH - 1; }
u64 CyclesPerHit = GlobalDebugServices->ScopeHistogramSorted[i].PerFrame_Cycles[CurrentFrame];
r32 SecondsPerHit = (r32)CyclesPerHit / (r32)GlobalDebugServices->PerformanceCountFrequency;
// Column 1
PrintF(&DebugString, "%.*s",
GlobalDebugServices->ScopeHistogramSorted[i].ScopeName.Length,
GlobalDebugServices->ScopeHistogramSorted[i].ScopeName.Memory);
r32 ColumnOneX = DrawString(RenderBuffer, DebugString, Interface.Font,
Register, WhiteV4).x;
Register.x += GSMax(ColumnOneX - Register.x, 250.f);
// Column 2
PrintF(&DebugString, "%d hits", GlobalDebugServices->ScopeHistogramSorted[i].PerFrame_CallCount[CurrentFrame]);
r32 ColumnTwoX = DrawString(RenderBuffer, DebugString, Interface.Font,
Register, WhiteV4).x;
Register.x += GSMax(ColumnTwoX - Register.x, 150.f);
// Column 3
PrintF(&DebugString, "%lld cycles", CyclesPerHit);
r32 ColumnThreeX = DrawString(RenderBuffer, DebugString, Interface.Font,
Register, WhiteV4).x;
Register.x += GSMax(ColumnThreeX - Register.x, 200.f);
PrintF(&DebugString, "%f sec", SecondsPerHit);
r32 ColumnFourX = DrawString(RenderBuffer, DebugString, Interface.Font,
Register, WhiteV4).x;
Register.x += GSMax(ColumnFourX - Register.x, 200.f);
TopOfScreenLinePos.y -= NewLineYOffset(*Interface.Font);
}
#endif
}
ZeroArenaToSnapshot(Transient, StartTempMemory);
ClearArenaToSnapshot(Transient, StartTempMemory);
}

View File

@ -117,8 +117,7 @@ input_command UniverseViewCommands [] = {
FOLDHAUS_INPUT_COMMAND_PROC(OpenUniverseView)
{
operation_mode* UniverseViewMode = ActivateOperationModeWithCommands(&State->Modes, UniverseViewCommands);
UniverseViewMode->Render = RenderUniverseView;
operation_mode* UniverseViewMode = ActivateOperationModeWithCommands(&State->Modes, UniverseViewCommands, RenderUniverseView);
// State Setup
universe_view_operation_state* OpState = CreateOperationState(UniverseViewMode,
@ -130,542 +129,458 @@ FOLDHAUS_INPUT_COMMAND_PROC(OpenUniverseView)
////////////////////////////////////////
//
// Node Lister
// Panels
//
///////////////////////////////////////
struct node_lister_operation_state
enum panel_edit_mode
{
search_lister SearchLister;
v2 ListPosition;
PanelEdit_Modify,
PanelEdit_Destroy,
PanelEdit_Count,
};
OPERATION_RENDER_PROC(RenderNodeLister)
//
// Drag Panel Border Operation Mode
OPERATION_STATE_DEF(drag_panel_border_operation_state)
{
node_lister_operation_state* OpState = (node_lister_operation_state*)Operation.OpStateMemory;
panel* Panel;
v2 TopLeft = OpState->ListPosition;
v2 Dimension = v2{300, 30};
// Filter the lister
OpState->SearchLister.Filter = State->ActiveTextEntry.Buffer;
FilterSearchLister(&OpState->SearchLister);
// Display Search Lister
search_lister_result NodeListerResult = EvaluateSearchLister (RenderBuffer, TopLeft, Dimension,
MakeStringLiteral("Nodes List"),
OpState->SearchLister.SourceList,
OpState->SearchLister.FilteredIndexLUT,
OpState->SearchLister.FilteredListCount,
OpState->SearchLister.HotItem,
&State->ActiveTextEntry.Buffer,
State->ActiveTextEntry.CursorPosition,
State->Font, State->Interface, Mouse);
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerNextItem)
{
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
OpState->SearchLister.HotItem = GetNextFilteredItem(OpState->SearchLister);
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerPrevItem)
{
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
OpState->SearchLister.HotItem = GetPrevFilteredItem(OpState->SearchLister);
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeLister)
{
DeactivateCurrentOperationMode(&State->Modes);
}
FOLDHAUS_INPUT_COMMAND_PROC(SelectAndCloseNodeLister)
{
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
s32 FilteredNodeIndex = OpState->SearchLister.HotItem;
if (FilteredNodeIndex >= 0)
{
s32 NodeIndex = OpState->SearchLister.FilteredIndexLUT[FilteredNodeIndex];
PushNodeOnListFromSpecification(State->NodeList, (node_type)NodeIndex,
Mouse.Pos, State->Permanent);
}
CloseNodeLister(State, Event, Mouse);
}
input_command UniverseViewCommads [] = {
{ KeyCode_DownArrow, KeyCode_Invalid, Command_Began, NodeListerNextItem },
{ KeyCode_UpArrow, KeyCode_Invalid, Command_Began, NodeListerPrevItem },
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, SelectAndCloseNodeLister },
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, CloseNodeLister },
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseNodeLister },
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
// NOTE(Peter): InitialPanelBounds is the bounds of the panel we are modifying,
// it stores the value calculated when the operation mode is kicked off.
rect InitialPanelBounds;
panel_split_direction PanelEdgeDirection;
panel_edit_mode PanelEditMode;
};
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeLister)
OPERATION_RENDER_PROC(UpdateAndRenderDragPanelBorder)
{
operation_mode* AddNodeOperation = ActivateOperationModeWithCommands(&State->Modes, UniverseViewCommads);
drag_panel_border_operation_state* OpState = (drag_panel_border_operation_state*)Operation.OpStateMemory;
rect PanelBounds = OpState->InitialPanelBounds;
AddNodeOperation->Render = RenderNodeLister;
node_lister_operation_state* OpState = CreateOperationState(AddNodeOperation,
&State->Modes,
node_lister_operation_state);
if (OpState->PanelEditMode == PanelEdit_Modify)
{
OpState->SearchLister.SourceListCount = NodeSpecificationsCount;
OpState->SearchLister.SourceList = PushArray(&State->Modes.Arena, string, OpState->SearchLister.SourceListCount);
v4 EdgePreviewColor = v4{.3f, .3f, .3f, 1.f};
v2 EdgePreviewMin = {};
v2 EdgePreviewMax = {};
if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
{
for (s32 i = 0; i < OpState->SearchLister.SourceListCount; i++)
EdgePreviewMin = v2{PanelBounds.Min.x, Mouse.Pos.y};
EdgePreviewMax = v2{PanelBounds.Max.x, Mouse.Pos.y + 1};
}
else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
{
EdgePreviewMin = v2{Mouse.Pos.x, PanelBounds.Min.y};
EdgePreviewMax = v2{Mouse.Pos.x + 1, PanelBounds.Max.y};
}
PushRenderQuad2D(RenderBuffer, EdgePreviewMin, EdgePreviewMax, EdgePreviewColor);
}
else if (OpState->PanelEditMode == PanelEdit_Destroy)
{
rect PanelToDeleteBounds = {};
if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
{
r32 SplitY = GSLerp(PanelBounds.Min.y, PanelBounds.Max.y, OpState->Panel->SplitPercent);
if (Mouse.Pos.y > SplitY)
{
OpState->SearchLister.SourceList[i] = MakeString(
NodeSpecifications[i].Name,
NodeSpecifications[i].NameLength);
PanelToDeleteBounds = GetTopPanelBounds(OpState->Panel, PanelBounds);
}
else
{
PanelToDeleteBounds = GetBottomPanelBounds(OpState->Panel, PanelBounds);
}
}
OpState->SearchLister.Filter = MakeString(PushArray(&State->Modes.Arena, char, 64), 0, 64);
OpState->SearchLister.FilteredListMax = OpState->SearchLister.SourceListCount;
OpState->SearchLister.FilteredListCount = 0;
OpState->SearchLister.FilteredIndexLUT = PushArray(&State->Modes.Arena, s32, OpState->SearchLister.SourceListCount);
}
OpState->ListPosition = Mouse.Pos;
SetTextInputDestinationToString(&State->ActiveTextEntry, &OpState->SearchLister.Filter);
}
////////////////////////////////////////
//
// Node Color Picker
//
///////////////////////////////////////
struct color_picker_operation_state
{
v4* ValueAddr;
};
internal void
CloseColorPicker(app_state* State)
{
DeactivateCurrentOperationMode(&State->Modes);
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseColorPickerCommand)
{
CloseColorPicker(State);
}
OPERATION_RENDER_PROC(RenderColorPicker)
{
color_picker_operation_state* OpState = (color_picker_operation_state*)Operation.OpStateMemory;
b32 ShouldClose = EvaluateColorPicker(RenderBuffer, OpState->ValueAddr,
v2{200, 200}, State->Interface, Mouse);
if (ShouldClose)
{
CloseColorPicker(State);
else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
{
r32 SplitX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, OpState->Panel->SplitPercent);
if (Mouse.Pos.x > SplitX)
{
PanelToDeleteBounds = GetRightPanelBounds(OpState->Panel, PanelBounds);
}
else
{
PanelToDeleteBounds = GetLeftPanelBounds(OpState->Panel, PanelBounds);
}
}
v4 OverlayColor = v4{0, 0, 0, .3f};
PushRenderQuad2D(RenderBuffer, PanelToDeleteBounds.Min, PanelToDeleteBounds.Max, OverlayColor);
}
}
input_command ColorPickerCommands [] = {
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseColorPickerCommand },
};
internal void
OpenColorPicker(app_state* State, node_connection* Connection)
FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation)
{
operation_mode* ColorPickerMode = ActivateOperationModeWithCommands(&State->Modes, ColorPickerCommands);
ColorPickerMode->Render = RenderColorPicker;
drag_panel_border_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_panel_border_operation_state);
panel* Panel = OpState->Panel;
rect PanelBounds = OpState->InitialPanelBounds;
color_picker_operation_state* OpState = CreateOperationState(ColorPickerMode,
&State->Modes,
color_picker_operation_state);
OpState->ValueAddr = Connection->V4ValuePtr;
}
////////////////////////////////////////
//
// Node Field Text Edit
//
///////////////////////////////////////
FOLDHAUS_INPUT_COMMAND_PROC(EndNodeFieldTextEdit)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command NodeFieldTextEditCommands [] = {
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
};
internal void
BeginNodeFieldTextEdit(app_state* State, node_connection* Connection)
{
operation_mode* NodeFieldTextEditMode = ActivateOperationModeWithCommands(&State->Modes,
NodeFieldTextEditCommands);
SetTextInputDestinationToFloat(&State->ActiveTextEntry, Connection->R32ValuePtr);
}
////////////////////////////////////////
//
// Node Port Mouse Drag
//
///////////////////////////////////////
struct drag_node_port_operation_state
{
node_interaction Interaction;
};
OPERATION_RENDER_PROC(RenderDraggingNodePort)
{
drag_node_port_operation_state* OpState = (drag_node_port_operation_state*)Operation.OpStateMemory;
UpdateDraggingNodePort(Mouse.Pos, OpState->Interaction, State->NodeList,
State->NodeRenderSettings, RenderBuffer);
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNodePort)
{
drag_node_port_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_port_operation_state);
TryConnectNodes(OpState->Interaction, Mouse.Pos, State->NodeList, State->NodeRenderSettings);
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragNodePortInputCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNodePort },
};
internal void
BeginDraggingNodePort(app_state* State, node_interaction Interaction)
{
operation_mode* DragNodePortMode = ActivateOperationModeWithCommands(
&State->Modes,
DragNodePortInputCommands);
DragNodePortMode->Render = RenderDraggingNodePort;
drag_node_port_operation_state* OpState = CreateOperationState(DragNodePortMode,
&State->Modes,
drag_node_port_operation_state);
OpState->Interaction = Interaction;
}
////////////////////////////////////////
//
// Node Field Mouse Drag
//
///////////////////////////////////////
OPERATION_RENDER_PROC(RenderDragNodeField)
{
// TODO(Peter):
//UpdateDraggingNodeValue(Mouse.Pos, Mouse.OldPos, OpState->Interaction, State->NodeList, State->NodeRenderSettings, State);
}
internal void
BeginInteractWithNodeField(app_state* State, node_interaction Interaction)
{
// TODO(Peter):
}
////////////////////////////////////////
//
// Node Mouse Drag
//
///////////////////////////////////////
struct drag_node_operation_state
{
node_interaction Interaction;
};
OPERATION_RENDER_PROC(RenderDraggingNode)
{
drag_node_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_operation_state);
UpdateDraggingNode(Mouse.Pos, OpState->Interaction, State->NodeList,
State->NodeRenderSettings);
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNode)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragNodeInputCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNode },
};
internal void
BeginDraggingNode(app_state* State, node_interaction Interaction)
{
operation_mode* DragNodeMode = ActivateOperationModeWithCommands(
&State->Modes,
DragNodeInputCommands);
DragNodeMode->Render = RenderDraggingNode;
drag_node_operation_state* OpState = CreateOperationState(DragNodeMode,
&State->Modes,
drag_node_operation_state);
OpState->Interaction = Interaction;
}
////////////////////////////////////////
//
// Node View
//
///////////////////////////////////////
struct node_view_operation_state
{
s32 SelectedNodeHandle;
};
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseDragInteraction)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.DownPos, State->NodeRenderSettings);
if (Node)
if (OpState->PanelEditMode == PanelEdit_Modify)
{
node_interaction NewInteraction = GetNodeInteractionType(Node,
Mouse.Pos,
State->NodeRenderSettings);
if (IsDraggingNodePort(NewInteraction))
if (Panel->SplitDirection == PanelSplit_Horizontal)
{
BeginDraggingNodePort(State, NewInteraction);
r32 NewSplitY = Mouse.Pos.y;
if (NewSplitY <= PanelBounds.Min.y)
{
ConsolidatePanelsKeepOne(Panel, Panel->Top, &State->PanelSystem);
}
else if (NewSplitY >= PanelBounds.Max.y)
{
ConsolidatePanelsKeepOne(Panel, Panel->Bottom, &State->PanelSystem);
}
else
{
Panel->SplitPercent = (NewSplitY - PanelBounds.Min.y) / Height(PanelBounds);
}
}
else if(IsDraggingNodeValue(NewInteraction))
else if (Panel->SplitDirection == PanelSplit_Vertical)
{
// TODO(Peter): This probably wants to live in a mouse held action
// the first frame we realize we're held over a field, just transition to
// drag node field
//BeginInteractWithNodeField(State, NewInteraction, State->NodeRenderSettings);
r32 NewSplitX = Mouse.Pos.x;
if (NewSplitX <= PanelBounds.Min.x)
{
ConsolidatePanelsKeepOne(Panel, Panel->Right, &State->PanelSystem);
}
else if (NewSplitX >= PanelBounds.Max.x)
{
ConsolidatePanelsKeepOne(Panel, Panel->Left, &State->PanelSystem);
}
else
{
Panel->SplitPercent = (NewSplitX - PanelBounds.Min.x) / Width(PanelBounds);
}
}
else // IsDraggingNode
}
else // PanelEdit_Destroy
{
if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
{
OpState->SelectedNodeHandle = Node->Handle;
BeginDraggingNode(State, NewInteraction);
r32 SplitY = GSLerp(PanelBounds.Min.y, PanelBounds.Max.y, OpState->Panel->SplitPercent);
if (Mouse.Pos.y > SplitY)
{
ConsolidatePanelsKeepOne(Panel, Panel->Bottom, &State->PanelSystem);
}
else
{
ConsolidatePanelsKeepOne(Panel, Panel->Top, &State->PanelSystem);
}
}
else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
{
r32 SplitX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, OpState->Panel->SplitPercent);
if (Mouse.Pos.x > SplitX)
{
ConsolidatePanelsKeepOne(Panel, Panel->Left, &State->PanelSystem);
}
else
{
ConsolidatePanelsKeepOne(Panel, Panel->Right, &State->PanelSystem);
}
}
}
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragPanelBorderCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragPanelBorderOperation },
{ KeyCode_MouseRightButton, KeyCode_Invalid, Command_Ended, EndDragPanelBorderOperation },
};
internal void
BeginDragPanelBorder(panel* Panel, panel_edit_mode PanelEditMode, rect PanelBounds, panel_split_direction PanelEdgeDirection, mouse_state Mouse, app_state* State)
{
operation_mode* DragPanelBorder = ActivateOperationModeWithCommands(&State->Modes, DragPanelBorderCommands, UpdateAndRenderDragPanelBorder);
drag_panel_border_operation_state* OpState = CreateOperationState(DragPanelBorder, &State->Modes, drag_panel_border_operation_state);
OpState->Panel = Panel;
OpState->InitialPanelBounds = PanelBounds;
OpState->PanelEdgeDirection = PanelEdgeDirection;
OpState->PanelEditMode = PanelEditMode;
}
// ----------------
//
// Drag To Split Panel Operation
OPERATION_STATE_DEF(split_panel_operation_state)
{
panel* Panel;
// NOTE(Peter): InitialPanelBounds is the bounds of the panel we are modifying,
// it stores the value calculated when the operation mode is kicked off.
rect InitialPanelBounds;
};
OPERATION_RENDER_PROC(UpdateAndRenderSplitPanel)
{
split_panel_operation_state* OpState = (split_panel_operation_state*)Operation.OpStateMemory;
rect PanelBounds = OpState->InitialPanelBounds;
v4 EdgePreviewColor = v4{.3f, .3f, .3f, 1.f};
r32 MouseDeltaX = GSAbs(Mouse.Pos.x - Mouse.DownPos.x);
r32 MouseDeltaY = GSAbs(Mouse.Pos.y - Mouse.DownPos.y);
v2 EdgePreviewMin = {};
v2 EdgePreviewMax = {};
if (MouseDeltaY > MouseDeltaX) // Horizontal Split
{
EdgePreviewMin = v2{PanelBounds.Min.x, Mouse.Pos.y};
EdgePreviewMax = v2{PanelBounds.Max.x, Mouse.Pos.y + 1};
}
else // Vertical Split
{
EdgePreviewMin = v2{Mouse.Pos.x, PanelBounds.Min.y};
EdgePreviewMax = v2{Mouse.Pos.x + 1, PanelBounds.Max.y};
}
PushRenderQuad2D(RenderBuffer, EdgePreviewMin, EdgePreviewMax, EdgePreviewColor);
}
FOLDHAUS_INPUT_COMMAND_PROC(EndSplitPanelOperation)
{
split_panel_operation_state* OpState = GetCurrentOperationState(State->Modes, split_panel_operation_state);
panel* Panel = OpState->Panel;
rect PanelBounds = OpState->InitialPanelBounds;
r32 XDistance = GSAbs(Mouse.Pos.x - Mouse.DownPos.x);
r32 YDistance = GSAbs(Mouse.Pos.y - Mouse.DownPos.y);
if (XDistance > YDistance)
{
r32 XPercent = (Mouse.Pos.x - PanelBounds.Min.x) / Width(PanelBounds);
SplitPanelVertically(Panel, XPercent, PanelBounds, &State->PanelSystem);
}
else
{
OpState->SelectedNodeHandle = 0;
r32 YPercent = (Mouse.Pos.y - PanelBounds.Min.y) / Height(PanelBounds);
SplitPanelHorizontally(Panel, YPercent, PanelBounds, &State->PanelSystem);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseSelectInteraction)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.Pos, State->NodeRenderSettings);
if (Node)
{
node_interaction NewInteraction = GetNodeInteractionType(Node,
Mouse.Pos,
State->NodeRenderSettings);
if(IsDraggingNodeValue(NewInteraction))
{
node_connection* Connection = Node->Connections + NewInteraction.InputValue;
struct_member_type InputType = Connection->Type;
if (InputType == MemberType_r32)
{
BeginNodeFieldTextEdit(State, Connection);
}
else if (InputType == MemberType_v4)
{
OpenColorPicker(State, Connection);
}
}
}
}
OPERATION_RENDER_PROC(RenderNodeView)
{
node_view_operation_state* OpState = (node_view_operation_state*)Operation.OpStateMemory;
DEBUG_TRACK_FUNCTION;
MakeStringBuffer(NodeHeaderBuffer, 128);
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
node_list_iterator NodeIter = GetNodeListIterator(*State->NodeList);
while (NodeIteratorIsValid(NodeIter))
{
node_header* Node = NodeIter.At;
rect NodeBounds = CalculateNodeBounds(Node, State->NodeRenderSettings);
b32 DrawFields = PointIsInRect(Mouse.Pos, NodeBounds);
if (Node == SelectedNode)
{
PushRenderQuad2D(RenderBuffer, NodeBounds.Min - v2{2, 2}, NodeBounds.Max + v2{2, 2}, WhiteV4);
}
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.5f, .5f, .5f, 1.f});
// TODO(Peter): This is just for debug purposes. We can remove and go back to just having
// Node->Name in DrawString
string NodeName = GetNodeName(*Node);
PrintF(&NodeHeaderBuffer, "%.*s: %d", NodeName.Length, NodeName.Memory, Node->Handle);
DrawString(RenderBuffer, NodeHeaderBuffer, State->NodeRenderSettings.Font,
v2{NodeBounds.Min.x + 5, NodeBounds.Max.y - (State->NodeRenderSettings.Font->PixelHeight + NODE_HEADER_HEIGHT + 5)},
WhiteV4);
for (s32 Connection = 0; Connection < Node->ConnectionsCount; Connection++)
{
v4 PortColor = State->NodeRenderSettings.PortColors[Node->Connections[Connection].Type];
// Inputs
if (ConnectionIsInput(Node, Connection))
{
rect PortBounds = CalculateNodeInputPortBounds(Node, Connection, State->NodeRenderSettings);
DrawPort(RenderBuffer, PortBounds, PortColor);
//
// TODO(Peter): I don't like excluding OutputNode, feels too much like a special case
// but I don't want to get in to the meta programming right now.
// We should just generate a spec and struct member types for NodeType_OutputNode
//
// :ExcludingOutputNodeSpecialCase
//
if (Node->Type != NodeType_OutputNode && DrawFields)
{
node_specification Spec = NodeSpecifications[Node->Type];
node_struct_member Member = Spec.MemberList[Connection];
DrawString(RenderBuffer, MakeString(Member.Name),
State->NodeRenderSettings.Font,
v2{PortBounds.Min.x - 8, PortBounds.Min.y}, WhiteV4, Align_Right);
}
rect ValueBounds = CalculateNodeInputValueBounds(Node, Connection, State->NodeRenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
// NOTE(Peter): its way easier to draw the connection on the input port b/c its a 1:1 relationship,
// whereas output ports might have many connections, they really only know about the most recent one
// Not sure if this is a problem. We mostly do everything backwards here, starting at the
// most downstream node and working back up to find dependencies.
if (ConnectionHasUpstreamConnection(Node, Connection))
{
rect ConnectedPortBounds = GetBoundsOfPortConnectedToInput(Node, Connection, State->NodeList, State->NodeRenderSettings);
v2 InputCenter = CalculateRectCenter(PortBounds);
v2 OutputCenter = CalculateRectCenter(ConnectedPortBounds);
PushRenderLine2D(RenderBuffer, OutputCenter, InputCenter, 1, WhiteV4);
}
}
// Outputs
if (ConnectionIsOutput(Node, Connection))
{
rect PortBounds = CalculateNodeOutputPortBounds(Node, Connection, State->NodeRenderSettings);
DrawPort(RenderBuffer, PortBounds, PortColor);
if (DrawFields)
{
node_specification Spec = NodeSpecifications[Node->Type];
node_struct_member Member = Spec.MemberList[Connection];
DrawString(RenderBuffer, MakeString(Member.Name),
State->NodeRenderSettings.Font,
v2{PortBounds.Max.x + 8, PortBounds.Min.y}, WhiteV4);
}
rect ValueBounds = CalculateNodeOutputValueBounds(Node, Connection, State->NodeRenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
}
for (s32 Button = 0; Button < 3; Button++)
{
rect ButtonRect = CalculateNodeDragHandleBounds(NodeBounds, Button, State->NodeRenderSettings);
PushRenderQuad2D(RenderBuffer, ButtonRect.Min, ButtonRect.Max, DragButtonColors[Button]);
}
}
Next(&NodeIter);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewDeleteNode)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
if (OpState->SelectedNodeHandle > 0)
{
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
FreeNodeOnList(State->NodeList, SelectedNode);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeView)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command NodeViewCommands [] = {
{ KeyCode_Tab, KeyCode_Invalid, Command_Began, CloseNodeView},
{ KeyCode_A, KeyCode_Invalid, Command_Began, OpenNodeLister},
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, NodeViewBeginMouseDragInteraction},
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, NodeViewBeginMouseSelectInteraction},
{ KeyCode_X, KeyCode_Invalid, Command_Began, NodeViewDeleteNode},
input_command SplitPanelCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndSplitPanelOperation },
};
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeView)
internal void
BeginSplitPanelOperation(panel* Panel, rect PanelBounds, mouse_state Mouse, app_state* State)
{
operation_mode* NodeViewMode = ActivateOperationModeWithCommands(&State->Modes, NodeViewCommands);
NodeViewMode->Render = RenderNodeView;
node_view_operation_state* OpState = CreateOperationState(NodeViewMode,
&State->Modes,
node_view_operation_state);
OpState->SelectedNodeHandle = 0;
operation_mode* SplitPanel = ActivateOperationModeWithCommands(&State->Modes, SplitPanelCommands, UpdateAndRenderSplitPanel);
split_panel_operation_state* OpState = CreateOperationState(SplitPanel, &State->Modes, split_panel_operation_state);
OpState->Panel = Panel;
OpState->InitialPanelBounds = PanelBounds;
}
////////////////////////////////////////
//
// 3D View Mouse Rotate
//
///////////////////////////////////////
struct mouse_rotate_view_operation_state
// ----------------
#define PANEL_EDGE_CLICK_MAX_DISTANCE 6
internal b32
HandleMouseDownPanelInteractionOrRecurse(panel* Panel, panel_edit_mode PanelEditMode, rect PanelBounds, mouse_state Mouse, app_state* State)
{
v4 CameraStartPos;
};
b32 HandledMouseInput = false;
OPERATION_RENDER_PROC(Update3DViewMouseRotate)
{
mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory;
if (Panel->SplitDirection == PanelSplit_NoSplit
&& PointIsInRange(Mouse.DownPos, PanelBounds.Min, PanelBounds.Min + v2{25, 25}))
{
BeginSplitPanelOperation(Panel, PanelBounds, Mouse, State);
HandledMouseInput = true;
}
else if (Panel->SplitDirection == PanelSplit_Horizontal)
{
r32 SplitY = GSLerp(PanelBounds.Min.y, PanelBounds.Max.y, Panel->SplitPercent);
r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.y - SplitY);
if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
{
BeginDragPanelBorder(Panel, PanelEditMode, PanelBounds, PanelSplit_Horizontal, Mouse, State);
HandledMouseInput = true;
}
else
{
rect TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
rect BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
if (PointIsInRect(Mouse.DownPos, BottomPanelBounds))
{
HandleMouseDownPanelInteractionOrRecurse(&Panel->Bottom->Panel, PanelEditMode, BottomPanelBounds, Mouse, State);
}
if (PointIsInRect(Mouse.DownPos, TopPanelBounds))
{
HandleMouseDownPanelInteractionOrRecurse(&Panel->Top->Panel, PanelEditMode, TopPanelBounds, Mouse, State);
}
}
}
else if (Panel->SplitDirection == PanelSplit_Vertical)
{
r32 SplitX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, Panel->SplitPercent);
r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.x - SplitX);
if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
{
BeginDragPanelBorder(Panel, PanelEditMode, PanelBounds, PanelSplit_Vertical, Mouse, State);
HandledMouseInput = true;
}
else
{
rect LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
rect RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
if (PointIsInRect(Mouse.DownPos, LeftPanelBounds))
{
HandleMouseDownPanelInteractionOrRecurse(&Panel->Left->Panel, PanelEditMode, LeftPanelBounds, Mouse, State);
}
if (PointIsInRect(Mouse.DownPos, RightPanelBounds))
{
HandleMouseDownPanelInteractionOrRecurse(&Panel->Right->Panel, PanelEditMode, RightPanelBounds, Mouse, State);
}
}
}
v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos;
m44 XRotation = GetXRotation(-TotalDeltaPos.y * State->PixelsToWorldScale);
m44 YRotation = GetYRotation(TotalDeltaPos.x * State->PixelsToWorldScale);
m44 Combined = XRotation * YRotation;
State->Camera.Position = V3(Combined * OpState->CameraStartPos);
return HandledMouseInput;
}
FOLDHAUS_INPUT_COMMAND_PROC(End3DViewMouseRotate)
internal b32
HandleMousePanelInteraction(panel_system* PanelSystem, rect WindowBounds, mouse_state Mouse, app_state* State)
{
DeactivateCurrentOperationMode(&State->Modes);
b32 HandledMouseInput = false;
panel* FirstPanel = &PanelSystem->Panels[0].Panel;
if (MouseButtonTransitionedDown(Mouse.LeftButtonState))
{
HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, PanelEdit_Modify, WindowBounds, Mouse, State);
}
else if (MouseButtonTransitionedDown(Mouse.RightButtonState))
{
HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, PanelEdit_Destroy, WindowBounds, Mouse, State);
}
return HandledMouseInput;
}
input_command MouseRotateViewCommands [] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate},
};
FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate)
internal void
DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, mouse_state Mouse, render_command_buffer* RenderBuffer)
{
operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands);
RotateViewMode->Render = Update3DViewMouseRotate;
r32 MouseLeftEdgeDistance = GSAbs(Mouse.Pos.x - PanelMin.x);
r32 MouseRightEdgeDistance = GSAbs(Mouse.Pos.x - PanelMax.x);
r32 MouseTopEdgeDistance = GSAbs(Mouse.Pos.y - PanelMax.y);
r32 MouseBottomEdgeDistance = GSAbs(Mouse.Pos.y - PanelMin.y);
mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode,
&State->Modes,
mouse_rotate_view_operation_state);
OpState->CameraStartPos = V4(State->Camera.Position, 1);
PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color);
v4 HighlightColor = v4{.3f, .3f, .3f, 1.f};
r32 HighlightThickness = 1;
if (MouseLeftEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
{
v2 LeftEdgeMin = PanelMin;
v2 LeftEdgeMax = v2{PanelMin.x + HighlightThickness, PanelMax.y};
PushRenderQuad2D(RenderBuffer, LeftEdgeMin, LeftEdgeMax, HighlightColor);
}
else if (MouseRightEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
{
v2 RightEdgeMin = v2{PanelMax.x - HighlightThickness, PanelMin.y};
v2 RightEdgeMax = PanelMax;
PushRenderQuad2D(RenderBuffer, RightEdgeMin, RightEdgeMax, HighlightColor);
}
else if (MouseTopEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
{
v2 TopEdgeMin = v2{PanelMin.x, PanelMax.y - HighlightThickness};
v2 TopEdgeMax = PanelMax;
PushRenderQuad2D(RenderBuffer, TopEdgeMin, TopEdgeMax, HighlightColor);
}
else if (MouseBottomEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
{
v2 BottomEdgeMin = PanelMin;
v2 BottomEdgeMax = v2{PanelMax.x, PanelMin.y + HighlightThickness};
PushRenderQuad2D(RenderBuffer, BottomEdgeMin, BottomEdgeMax, HighlightColor);
}
}
internal void
DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, rect FooterBounds, interface_config Interface, mouse_state Mouse)
{
PushRenderQuad2D(RenderBuffer, FooterBounds.Min, v2{FooterBounds.Max.x, FooterBounds.Min.y + 25}, v4{.5f, .5f, .5f, 1.f});
PushRenderQuad2D(RenderBuffer, FooterBounds.Min, FooterBounds.Min + v2{25, 25}, WhiteV4);
v2 PanelSelectButtonMin = FooterBounds.Min + v2{30, 1};
v2 PanelSelectButtonMax = PanelSelectButtonMin + v2{100, 23};
if (Panel->PanelSelectionMenuOpen)
{
v2 ButtonDimension = v2{100, 25};
v2 ButtonMin = v2{PanelSelectButtonMin.x, FooterBounds.Max.y};
v2 MenuMin = ButtonMin;
v2 MenuMax = v2{ButtonMin.x + ButtonDimension.x, ButtonMin.y + (ButtonDimension.y * GlobalPanelDefsCount)};
if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
&& !PointIsInRange(Mouse.DownPos, MenuMin, MenuMax))
{
Panel->PanelSelectionMenuOpen = false;
}
for (s32 i = 0; i < GlobalPanelDefsCount; i++)
{
panel_definition Def = GlobalPanelDefs[i];
string DefName = MakeString(Def.PanelName, Def.PanelNameLength);
button_result DefinitionButton = EvaluateButton(RenderBuffer,
ButtonMin, ButtonMin + ButtonDimension,
DefName, Interface, Mouse);
if (DefinitionButton.Pressed)
{
SetPanelDefinition(Panel, i);
Panel->PanelSelectionMenuOpen = false;
}
ButtonMin.y += ButtonDimension.y;
}
}
button_result ButtonResult = EvaluateButton(RenderBuffer,
PanelSelectButtonMin,
PanelSelectButtonMax,
MakeStringLiteral("Select"), Interface, Mouse);
if (ButtonResult.Pressed)
{
Panel->PanelSelectionMenuOpen = !Panel->PanelSelectionMenuOpen;
}
}
internal void
RenderPanel(panel* Panel, rect PanelBounds, rect WindowBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
{
Assert(Panel->PanelDefinitionIndex >= 0);
rect FooterBounds = rect{
PanelBounds.Min,
v2{PanelBounds.Max.x, PanelBounds.Min.y + 25},
};
rect PanelViewBounds = rect{
v2{PanelBounds.Min.x, FooterBounds.Max.y},
PanelBounds.Max,
};
panel_definition Definition = GlobalPanelDefs[Panel->PanelDefinitionIndex];
Definition.Render(*Panel, PanelViewBounds, RenderBuffer, State, Context, Mouse);
PushRenderOrthographic(RenderBuffer, WindowBounds.Min.x, WindowBounds.Min.y, WindowBounds.Max.x, WindowBounds.Max.y);
DrawPanelFooter(Panel, RenderBuffer, FooterBounds, State->Interface, Mouse);
}
internal void
DrawAllPanels(panel_layout PanelLayout, render_command_buffer* RenderBuffer, mouse_state Mouse, app_state* State, context Context)
{
for (u32 i = 0; i < PanelLayout.PanelsCount; i++)
{
panel_with_layout PanelWithLayout = PanelLayout.Panels[i];
panel* Panel = PanelWithLayout.Panel;
rect PanelBounds = PanelWithLayout.Bounds;
RenderPanel(Panel, PanelBounds, State->WindowBounds, RenderBuffer, State, Context, Mouse);
v4 BorderColor = v4{0, 0, 0, 1};
PushRenderOrthographic(RenderBuffer, State->WindowBounds.Min.x, State->WindowBounds.Min.y, State->WindowBounds.Max.x, State->WindowBounds.Max.y);
DrawPanelBorder(*Panel, PanelBounds.Min, PanelBounds.Max, BorderColor, Mouse, RenderBuffer);
}
}

View File

@ -1,5 +1,6 @@
#ifndef GS_MEMORY_H
#if 0
#ifndef GS_LANGUAGE_H
typedef uint8_t u8;
@ -230,6 +231,7 @@ ClearArenaToSnapshot (memory_arena* Arena, arena_snapshot Snapshot)
Assert(RegionCursor == Snapshot.CurrentRegion);
RegionCursor->Used = Snapshot.UsedAtSnapshot;
}
#endif
//
// Basic Memory Arena
@ -253,6 +255,7 @@ CreateMemoryArena (u8* Base, u32 Size)
return Result;
}
#define PushArrayOnStaticArena(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length)
static u8*
PushSize_ (static_memory_arena* Arena, u32 Size)
{
@ -262,80 +265,5 @@ PushSize_ (static_memory_arena* Arena, u32 Size)
return Result;
}
//
// Tracked Array Implementation
//
#define ARRAY_CHECKSUM 0x51bada7b
struct array_header_
{
u32 Size;
s32 ElementMax;
s32 ElementCount;
s32 ElementSize;
u32 Checksum;
};
#define gs_PushArray(arena, type, size) (type*)gs_PushArray_(arena, sizeof(type), size)
static u8*
gs_PushArray_ (memory_arena* Arena, u32 StepSize, u32 Count)
{
u32 ArrayFootprint = sizeof(array_header_) + (StepSize * Count);
array_header_* Header = (array_header_*)PushSize_(Arena, ArrayFootprint);
array_header_* Body = Header + 1;
u8* Result = (u8*)(Body);
Header->Size = Count * StepSize;
Header->ElementMax = Count;
Header->ElementSize = StepSize;
Header->ElementCount = 0;
Header->Checksum = ARRAY_CHECKSUM;
return Result;
}
#define gs_ArrayHeader_(array) (((array_header_*)array) - 1)
#ifdef DEBUG
#define gs_ArrayCheck(array) Assert(!array || gs_ArrayHeader_(array)->Checksum == ARRAY_CHECKSUM)
#else
#define gs_ArrayCheck(array)
#endif
#define gs_ArrayCount(array) gs_ArrayCount_((u8*)array)
static s32
gs_ArrayCount_ (u8* Base)
{
gs_ArrayCheck(Base);
return gs_ArrayHeader_(Base)->ElementCount;
}
#define gs_ArrayMax(array) gs_ArrayMax_((u8*)array)
static s32
gs_ArrayMax_ (u8* Base)
{
gs_ArrayCheck(Base);
return gs_ArrayHeader_(Base)->ElementMax;
}
#define gs_ArrayAdd(array) ( gs_PushArrayElement_((u8*)array), (array) + (gs_ArrayCount(array) - 1) )
#define gs_ArrayPush(array, ele) *( gs_ArrayAdd(array) ) = (ele)
static void*
gs_PushArrayElement_ (u8* Base)
{
gs_ArrayCheck(Base);
Assert(gs_ArrayHeader_(Base)->ElementCount + 1 <= gs_ArrayHeader_(Base)->ElementMax);
void* Result = (void*)(Base + (gs_ArrayHeader_(Base)->ElementCount * gs_ArrayHeader_(Base)->ElementSize));
gs_ArrayHeader_(Base)->ElementCount++;
return Result;
}
#define GS_MEMORY_H
#endif // GS_MEMORY_H

View File

@ -1,5 +1,7 @@
typedef struct operation_mode operation_mode;
#define OPERATION_STATE_DEF(name) struct name
#define OPERATION_RENDER_PROC(name) void name(app_state* State, render_command_buffer* RenderBuffer, operation_mode Operation, mouse_state Mouse)
typedef OPERATION_RENDER_PROC(operation_render_proc);
@ -23,26 +25,30 @@ struct operation_mode_system
};
internal operation_mode*
ActivateOperationMode (operation_mode_system* System)
ActivateOperationMode (operation_mode_system* System, operation_render_proc* RenderProc)
{
operation_mode* Result = 0;
Assert(System->ActiveModesCount < OPERATION_MODES_MAX);
s32 ModeIndex = System->ActiveModesCount++;
System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(System->Arena);
System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena);
operation_mode NewMode = {};
System->ActiveModes[ModeIndex] = NewMode;
return &System->ActiveModes[ModeIndex];
Result = &System->ActiveModes[ModeIndex];
Result->Render = RenderProc;
return Result;
}
#define ActivateOperationModeWithCommands(sys, cmds) \
ActivateOperationModeWithCommands_(sys, cmds, (s32)(sizeof(cmds) / sizeof(cmds[0])));
#define ActivateOperationModeWithCommands(sys, cmds, render) \
ActivateOperationModeWithCommands_(sys, cmds, (s32)(sizeof(cmds) / sizeof(cmds[0])), render);
internal operation_mode*
ActivateOperationModeWithCommands_(operation_mode_system* System, input_command* Commands, s32 CommandsCount)
ActivateOperationModeWithCommands_(operation_mode_system* System, input_command* Commands, s32 CommandsCount, operation_render_proc* RenderProc)
{
operation_mode* NewMode = ActivateOperationMode(System);
operation_mode* NewMode = ActivateOperationMode(System, RenderProc);
InitializeInputCommandRegistry(&NewMode->Commands, CommandsCount, &System->Arena);
for (s32 i = 0; i < CommandsCount; i++)
@ -66,7 +72,7 @@ DeactivateCurrentOperationMode (operation_mode_system* System)
(stateType*)CreateOperationState_(mode, modeSystem, sizeof(stateType))
#define GetCurrentOperationState(modeSystem, stateType) \
(stateType*)State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].OpStateMemory;
(stateType*)(modeSystem).ActiveModes[(modeSystem).ActiveModesCount - 1].OpStateMemory;
internal u8*

360
src/foldhaus_panel.h Normal file
View File

@ -0,0 +1,360 @@
/*
File: foldhaus_panel.cpp
Description: a system for laying out panels on a screen
Author: Peter Slattery
Creation Date: 2019-12-26
Usage:
Include this file in ONE file in your project.
Define SetPanelDefinitionExternal
*/
typedef struct panel panel;
enum panel_split_direction
{
PanelSplit_NoSplit,
PanelSplit_Horizontal,
PanelSplit_Vertical,
PanelSplit_Count,
};
typedef struct panel_entry panel_entry;
struct panel
{
s32 PanelDefinitionIndex;
panel_split_direction SplitDirection;
r32 SplitPercent;
// TODO(Peter): This REALLY doesn't want to live here
// Probably belongs in a more generalized PanelInterfaceState or something
b32 PanelSelectionMenuOpen;
union{
panel_entry* Left;
panel_entry* Top;
};
union{
panel_entry* Right;
panel_entry* Bottom;
};
};
struct free_panel
{
panel_entry* Next;
};
struct panel_entry
{
panel Panel;
free_panel Free;
};
#define PANELS_MAX 16
struct panel_system
{
panel_entry Panels[PANELS_MAX];
u32 PanelsUsed;
panel_entry FreeList;
};
// NOTE(Peter): This representation is used to let external code render and interact
// with panels. It shouldn't be stored across frame boundaries as the pointers to
// Panel's are liable to change.
struct panel_with_layout
{
panel* Panel;
rect Bounds;
};
struct panel_layout
{
panel_with_layout* Panels;
u32 PanelsCount;
u32 PanelsMax;
};
internal void SetPanelDefinitionExternal(panel* Panel, s32 OldPanelDefinitionIndex, s32 NewPanelDefinitionIndex);
/////////////////////////////////
//
// Book-Keeping
//
/////////////////////////////////
internal void
InitializePanelSystem(panel_system* PanelSystem)
{
PanelSystem->FreeList.Free.Next = &PanelSystem->FreeList;
}
internal panel_entry*
TakeNewPanelEntry(panel_system* PanelSystem)
{
panel_entry* FreeEntry = 0;
if (PanelSystem->FreeList.Free.Next != &PanelSystem->FreeList)
{
FreeEntry = PanelSystem->FreeList.Free.Next;
PanelSystem->FreeList.Free.Next = FreeEntry->Free.Next;
}
else
{
Assert(PanelSystem->PanelsUsed < PANELS_MAX);
FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++;
}
return FreeEntry;
}
internal panel*
TakeNewPanel(panel_system* PanelSystem)
{
panel* Result = 0;
panel_entry* FreeEntry = TakeNewPanelEntry(PanelSystem);
Result = &FreeEntry->Panel;
*Result = {0};
Result->PanelDefinitionIndex = -1;
return Result;
}
internal void
FreePanelEntry(panel_entry* Entry, panel_system* PanelSystem)
{
Assert(Entry >= PanelSystem->Panels && Entry <= PanelSystem->Panels + PANELS_MAX);
Entry->Panel = {0};
Entry->Free.Next = PanelSystem->FreeList.Free.Next;
PanelSystem->FreeList.Free.Next = Entry;
}
internal void
FreePanelEntryRecursive(panel_entry* Entry, panel_system* PanelSystem)
{
if (Entry->Panel.SplitDirection != PanelSplit_NoSplit)
{
FreePanelEntryRecursive(Entry->Panel.Left, PanelSystem);
FreePanelEntryRecursive(Entry->Panel.Right, PanelSystem);
}
FreePanelEntry(Entry, PanelSystem);
}
internal void
FreePanelAtIndex(s32 Index, panel_system* PanelSystem)
{
Assert(Index > 0 && Index < (s32)PanelSystem->PanelsUsed);
panel_entry* EntryToFree = PanelSystem->Panels + Index;
EntryToFree->Free.Next = PanelSystem->FreeList.Free.Next;
PanelSystem->FreeList.Free.Next = EntryToFree;
}
internal void
SplitPanelVertically(panel* Parent, r32 Percent, rect ParentBounds, panel_system* PanelSystem)
{
r32 SplitX = GSLerp(ParentBounds.Min.x, ParentBounds.Max.x, Percent);
if (SplitX > ParentBounds.Min.x && SplitX < ParentBounds.Max.x)
{
Parent->SplitDirection = PanelSplit_Vertical;
Parent->SplitPercent = Percent;
Parent->Left = TakeNewPanelEntry(PanelSystem);
Parent->Left->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex;
Parent->Right = TakeNewPanelEntry(PanelSystem);
Parent->Right->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex;
}
}
internal void
SplitPanelHorizontally(panel* Parent, r32 Percent, rect ParentBounds, panel_system* PanelSystem)
{
r32 SplitY = GSLerp(ParentBounds.Min.y, ParentBounds.Max.y, Percent);
if (SplitY > ParentBounds.Min.y && SplitY < ParentBounds.Max.y)
{
Parent->SplitDirection = PanelSplit_Horizontal;
Parent->SplitPercent = Percent;
Parent->Bottom = TakeNewPanelEntry(PanelSystem);
Parent->Bottom->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex;
Parent->Top = TakeNewPanelEntry(PanelSystem);
Parent->Top->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex;
}
}
internal void
ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_system* PanelSystem)
{
panel_entry* LeftChild = Parent->Left;
panel_entry* RightChild = Parent->Right;
panel_entry* PanelEntryToDestroy = PanelEntryToKeep == LeftChild ? RightChild : LeftChild;
*Parent = PanelEntryToKeep->Panel;
FreePanelEntry(PanelEntryToKeep, PanelSystem);
FreePanelEntryRecursive(PanelEntryToDestroy, PanelSystem);
}
internal void
SetPanelDefinition(panel* Panel, s32 NewDefinitionIndex)
{
s32 OldDefinitionIndex = Panel->PanelDefinitionIndex;
Panel->PanelDefinitionIndex = NewDefinitionIndex;
SetPanelDefinitionExternal(Panel, OldDefinitionIndex, NewDefinitionIndex);
}
/////////////////////////////////
//
// Rendering And Interaction
//
/////////////////////////////////
internal rect
GetTopPanelBounds(panel* Panel, rect PanelBounds)
{
rect Result = {};
Result.Min = v2{
PanelBounds.Min.x,
GSLerp(PanelBounds.Min.y, PanelBounds.Max.y, Panel->SplitPercent)
};
Result.Max = PanelBounds.Max;
return Result;
}
internal rect
GetBottomPanelBounds(panel* Panel, rect PanelBounds)
{
rect Result = {};
Result.Min = PanelBounds.Min;
Result.Max = v2{
PanelBounds.Max.x,
GSLerp(PanelBounds.Min.y, PanelBounds.Max.y, Panel->SplitPercent)
};
return Result;
}
internal rect
GetRightPanelBounds(panel* Panel, rect PanelBounds)
{
rect Result = {};
Result.Min = v2{
GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, Panel->SplitPercent),
PanelBounds.Min.y
};
Result.Max = PanelBounds.Max;
return Result;
}
internal rect
GetLeftPanelBounds(panel* Panel, rect PanelBounds)
{
rect Result = {};
Result.Min = PanelBounds.Min;
Result.Max = v2{
GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, Panel->SplitPercent),
PanelBounds.Max.y
};
return Result;
}
internal void
LayoutPanel(panel* Panel, rect PanelBounds, panel_layout* Layout)
{
if (Panel->SplitDirection == PanelSplit_NoSplit)
{
panel_with_layout* WithLayout = Layout->Panels + Layout->PanelsCount++;
WithLayout->Panel = Panel;
WithLayout->Bounds = PanelBounds;
}
else if (Panel->SplitDirection == PanelSplit_Horizontal)
{
rect TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
rect BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
LayoutPanel(&Panel->Top->Panel, TopPanelBounds, Layout);
LayoutPanel(&Panel->Bottom->Panel, BottomPanelBounds, Layout);
}
else if (Panel->SplitDirection == PanelSplit_Vertical)
{
rect LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
rect RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
LayoutPanel(&Panel->Left->Panel, LeftPanelBounds, Layout);
LayoutPanel(&Panel->Right->Panel, RightPanelBounds, Layout);
}
}
internal panel_layout
GetPanelLayout(panel_system* System, rect WindowBounds, memory_arena* Storage)
{
panel_layout Result = {};
Result.PanelsMax = System->PanelsUsed;
Result.Panels = PushArray(Storage, panel_with_layout, Result.PanelsMax);
LayoutPanel(&System->Panels[0].Panel, WindowBounds, &Result);
return Result;
}
struct panel_and_bounds
{
panel* Panel;
rect Bounds;
};
internal panel_and_bounds
GetPanelContainingPoint(v2 Point, panel* Panel, rect PanelBounds)
{
panel_and_bounds Result = {0};
if (Panel->SplitDirection == PanelSplit_NoSplit)
{
Result.Panel = Panel;
Result.Bounds = PanelBounds;
}
else if (Panel->SplitDirection == PanelSplit_Horizontal)
{
rect TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
rect BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
if (PointIsInRange(Point, TopPanelBounds.Min, TopPanelBounds.Max))
{
Result = GetPanelContainingPoint(Point, &Panel->Top->Panel, TopPanelBounds);
}
else if (PointIsInRange(Point, BottomPanelBounds.Min, BottomPanelBounds.Max))
{
Result = GetPanelContainingPoint(Point, &Panel->Bottom->Panel, BottomPanelBounds);
}
}
else if (Panel->SplitDirection == PanelSplit_Vertical)
{
rect LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
rect RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
if (PointIsInRange(Point, LeftPanelBounds.Min, LeftPanelBounds.Max))
{
Result = GetPanelContainingPoint(Point, &Panel->Left->Panel, LeftPanelBounds);
}
else if (PointIsInRange(Point, RightPanelBounds.Min, RightPanelBounds.Max))
{
Result = GetPanelContainingPoint(Point, &Panel->Right->Panel, RightPanelBounds);
}
}
return Result;
}
internal panel_and_bounds
GetPanelContainingPoint(v2 Point, panel_system* PanelSystem, rect WindowBounds)
{
panel_and_bounds Result = {0};
if (PanelSystem->PanelsUsed > 0)
{
Result = GetPanelContainingPoint(Point, &PanelSystem->Panels[0].Panel, WindowBounds);
}
return Result;
}

View File

@ -1,8 +1,13 @@
#include "gs_language.h"
#define GS_LANGUAGE_NO_PROFILER_DEFINES
#include <gs_language.h>
#include "gs_platform.h"
#include "gs_array.h"
#include "foldhaus_memory.h"
#include "gs_string.h"
#define GS_MEMORY_TRACK_ALLOCATIONS
#define GS_MEMORY_NO_STD_LIBS
#include <gs_memory_arena.h>
#include <gs_string.h>
#include "foldhaus_debug.h"
global_variable debug_services* GlobalDebugServices;
@ -10,7 +15,7 @@ global_variable debug_services* GlobalDebugServices;
global_variable platform_alloc* GSAlloc;
global_variable platform_free* GSFree;
#include "gs_vector_matrix.h"
#include <gs_vector_matrix.h>
#include "gs_input.h"
@ -117,8 +122,7 @@ struct context
u32 MemorySize;
b32 WindowIsVisible;
r32 WindowWidth;
r32 WindowHeight;
rect WindowBounds;
r32 DeltaTime;
// Application Services
@ -132,6 +136,7 @@ struct context
platform_alloc* PlatformAlloc;
platform_free* PlatformFree;
platform_realloc* PlatformRealloc;
platform_read_entire_file* PlatformReadEntireFile;
platform_write_entire_file* PlatformWriteEntireFile;
platform_get_file_path* PlatformGetFilePath;
@ -139,7 +144,6 @@ struct context
platform_get_font_info* PlatformGetFontInfo;
platform_draw_font_codepoint* PlatformDrawFontCodepoint;
platform_get_socket_handle* PlatformGetSocketHandle;
platform_get_send_address* PlatformGetSendAddress;
platform_set_socket_option* PlatformSetSocketOption;
platform_send_to* PlatformSendTo;
platform_close_socket* PlatformCloseSocket;

View File

@ -1,3 +1,9 @@
internal b32
NamePassesFilter (string Target, string Filter)
{
return (Filter.Length == 0 || StringContainsStringCaseInsensitive(Target, Filter));
}
internal void
FilterSearchLister (search_lister* SearchLister)
{
@ -9,7 +15,7 @@ FilterSearchLister (search_lister* SearchLister)
for (s32 i = 0; i < SearchLister->SourceListCount; i++)
{
string* NameString = SearchLister->SourceList + i;
if (SpecificationPassesFilter(*NameString, SearchLister->Filter))
if (NamePassesFilter(*NameString, SearchLister->Filter))
{
SearchLister->FilteredIndexLUT[SearchLister->FilteredListCount++] = i;
}

View File

@ -47,7 +47,6 @@ node_struct_member MemberList_sin_wave_data[] = {
node_struct_member MemberList_multiply_patterns_data[] = {
{ MemberType_NODE_COLOR_BUFFER, "ALEDs", (u64)&((multiply_patterns_data*)0)->ALEDs, IsInputMember },
{ MemberType_NODE_COLOR_BUFFER, "BLEDs", (u64)&((multiply_patterns_data*)0)->BLEDs, IsInputMember },
{ MemberType_NODE_COLOR_BUFFER, "ResultLEDs", (u64)&((multiply_patterns_data*)0)->ResultLEDs, IsOutputMember},
};
@ -83,8 +82,8 @@ node_specification NodeSpecifications[] = {
{ NodeType_VectorValue, "VectorValue", 11, MemberList_vector_data, 32, 5, false},
{ NodeType_MultiplyNodeProc, "MultiplyNodeProc", 16, MemberList_multiply_data, 12, 3, false},
{ NodeType_AddNodeProc, "AddNodeProc", 11, MemberList_add_data, 48, 3, false},
{ NodeType_SinWave, "SinWave", 7, MemberList_sin_wave_data, 20, 4, false},
{ NodeType_MultiplyPatterns, "MultiplyPatterns", 16, MemberList_multiply_patterns_data, 60, 3, false},
{ NodeType_SinWave, "SinWave", 7, MemberList_sin_wave_data, 16, 4, false},
{ NodeType_MultiplyPatterns, "MultiplyPatterns", 16, MemberList_multiply_patterns_data, 40, 2, false},
{ NodeType_OutputNode, "OutputNode", 10, MemberList_output_node_data, 20, 1, false},
{ NodeType_SolidColorProc, "SolidColorProc", 14, MemberList_solid_color_data, 36, 2, false},
{ NodeType_VerticalColorFadeProc, "VerticalColorFadeProc", 21, MemberList_vertical_color_fade_data, 44, 4, false},
@ -93,11 +92,11 @@ node_specification NodeSpecifications[] = {
s32 NodeSpecificationsCount = 10;
internal void CallNodeProc(node_header* Node, u8* Data, led* LEDs, s32 LEDsCount, r32 DeltaTime)
{
node_specification Spec = NodeSpecifications[Node->Type];
switch (Spec.Type)
{
case NodeType_FloatValue: { FloatValue((float_value_data*)Data, DeltaTime); } break;
{
node_specification Spec = NodeSpecifications[Node->Type];
switch (Spec.Type)
{
case NodeType_FloatValue: { FloatValue((float_value_data*)Data, DeltaTime); } break;
case NodeType_VectorValue: { VectorValue((vector_data*)Data, DeltaTime); } break;
case NodeType_MultiplyNodeProc: { MultiplyNodeProc((multiply_data*)Data, DeltaTime); } break;
case NodeType_AddNodeProc: { AddNodeProc((add_data*)Data, DeltaTime); } break;

View File

@ -0,0 +1,18 @@
struct panel_definition
{
char* PanelName;
s32 PanelNameLength;
panel_init_proc* Init;
panel_cleanup_proc* Cleanup;
panel_render_proc* Render;
input_command* InputCommands;
};
global_variable s32 GlobalPanelDefsCount = 5;
global_variable panel_definition GlobalPanelDefs[] = {
{ "Sculpture View", 14, SculptureView_Init, SculptureView_Cleanup, SculptureView_Render, SculptureView_Commands},
{ "Animation Timeline", 18, AnimationTimeline_Init, AnimationTimeline_Cleanup, AnimationTimeline_Render, AnimationTimeline_Commands },
{ "DMX View", 8, DMXView_Init, DMXView_Cleanup, DMXView_Render, 0 },
{ "Profiler", 8, ProfilerView_Init, ProfilerView_Cleanup, ProfilerView_Render, 0 },
{ "Hierarchy", 9, HierarchyView_Init, HierarchyView_Cleanup, HierarchyView_Render, 0 },
};

229
src/gs_array.h Normal file
View File

@ -0,0 +1,229 @@
struct free_list
{
free_list* Next;
s32 Index;
};
struct bucket_index
{
s32 Bucket;
s32 IndexInBucket;
};
inline bucket_index
GetBucketIndexForIndex (s32 Index, s32 BucketSize)
{
bucket_index Result = {};
Result.Bucket = Index / BucketSize;
Result.IndexInBucket = Index % BucketSize;
return Result;
};
struct array_entry_handle
{
s32 Generation;
s32 Index;
};
// NOTE(Peter): This is a total bastardization of the preprocessor but it works and is
// easier than writing a metaprogramming system at the moment.
// TODO(Peter): Write a metaprogramming version of this that is actually easy to debug
#define TYPEDEF_ARRAY(element_type) \
struct element_type##_array_entry { \
s32 Generation; \
union \
{ \
element_type Entry; \
free_list Free; \
}; \
}; \
\
struct element_type##_array \
{ \
element_type##_array_entry** Buckets; \
s32 BucketSize; \
s32 BucketCount; \
s32 Used; \
free_list FreeList; \
}; \
\
internal void \
GrowBuffer(element_type##_array* Buffer) \
{ \
s32 NewBucketSize = sizeof(element_type##_array_entry) * Buffer->BucketSize; \
element_type##_array_entry* NewBucket = (element_type##_array_entry*)malloc(NewBucketSize); \
GSZeroMemory((u8*)NewBucket, NewBucketSize); \
\
s32 NewBucketIndex = Buffer->BucketCount++; \
if (!Buffer->Buckets) \
{ \
Buffer->Buckets = (element_type##_array_entry**)malloc(sizeof(element_type##_array_entry*)); \
} \
else \
{ \
Buffer->Buckets = (element_type##_array_entry**)realloc(Buffer->Buckets, sizeof(element_type##_array_entry*) * Buffer->BucketCount); \
} \
Buffer->Buckets[NewBucketIndex] = NewBucket; \
} \
\
internal element_type##_array_entry* \
GetEntryAtIndex (s32 Index, element_type##_array Buffer) \
{ \
bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize); \
element_type##_array_entry* Entry = Buffer.Buckets[BucketIndex.Bucket] + BucketIndex.IndexInBucket; \
return Entry; \
} \
\
internal array_entry_handle \
PushElement (element_type Data, element_type##_array* Buffer) \
{ \
array_entry_handle Result = {}; \
\
if (Buffer->FreeList.Next != &Buffer->FreeList) \
{ \
free_list* FreeList = Buffer->FreeList.Next; \
element_type##_array_entry* Entry = GetEntryAtIndex(FreeList->Index, *Buffer); \
Buffer->FreeList.Next = Entry->Free.Next; \
\
Result.Index = Entry->Free.Index; \
Result.Generation = Entry->Generation; \
Entry->Entry = Data; \
\
++Buffer->Used; \
} \
else \
{ \
if (Buffer->Used >= Buffer->BucketSize * Buffer->BucketCount) \
{ \
GrowBuffer(Buffer); \
} \
\
s32 Index = Buffer->Used++; \
s32 BucketIndex = Index / Buffer->BucketSize; \
s32 IndexInBucket = Index % Buffer->BucketSize; \
\
Buffer->Buckets[BucketIndex][IndexInBucket].Entry = Data; \
Result.Index = Index; \
Result.Generation = Buffer->Buckets[BucketIndex][IndexInBucket].Generation; \
} \
\
return Result; \
} \
\
internal element_type* \
GetElementAtIndex (s32 Index, element_type##_array Buffer) \
{ \
Assert(Index < Buffer.Used); \
element_type##_array_entry* Entry = GetEntryAtIndex(Index, Buffer); \
element_type* Result = &Entry->Entry; \
return Result; \
} \
\
internal element_type* \
GetElementWithHandle (array_entry_handle Handle, element_type##_array Buffer) \
{ \
element_type* Result = 0; \
\
element_type##_array_entry* Entry = GetEntryAtIndex(Handle.Index, Buffer); \
\
if (Entry->Generation == Handle.Generation) \
{ \
Result = &Entry->Entry; \
} \
\
return Result; \
} \
\
internal void \
RemoveElementAtIndex (s32 Index, element_type##_array* Buffer) \
{ \
Assert(Index < Buffer->Used); \
\
element_type##_array_entry* Entry = GetEntryAtIndex(Index, *Buffer); \
++Entry->Generation; \
Entry->Free.Index = Index; \
\
Entry->Free.Next = Buffer->FreeList.Next; \
Buffer->FreeList.Next = &Entry->Free; \
\
} \
// END OF CRAZY MACRO
#define TYPEDEF_CONTIGUOUS_ARRAY(element_type) \
struct element_type##_contiguous_array \
{ \
element_type** Buckets; \
s32 BucketSize; \
s32 BucketCount; \
s32 Used; \
}; \
\
internal void \
GrowBuffer(element_type##_contiguous_array* Buffer) \
{ \
s32 NewBucketSize = sizeof(element_type) * Buffer->BucketSize; \
element_type* NewBucket = (element_type*)malloc(NewBucketSize); \
GSZeroMemory((u8*)NewBucket, NewBucketSize); \
\
s32 NewBucketIndex = Buffer->BucketCount++; \
if (!Buffer->Buckets) \
{ \
Buffer->Buckets = (element_type**)malloc(sizeof(element_type*)); \
} \
else \
{ \
Buffer->Buckets = (element_type**)realloc(Buffer->Buckets, sizeof(element_type*) * Buffer->BucketCount); \
} \
Buffer->Buckets[NewBucketIndex] = NewBucket; \
} \
\
internal element_type* \
GetElementAtIndex (s32 Index, element_type##_contiguous_array Buffer) \
{ \
element_type* Entry = 0; \
if (Index <= Buffer.Used) \
{ \
bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize); \
Entry = Buffer.Buckets[BucketIndex.Bucket] + BucketIndex.IndexInBucket; \
} \
return Entry; \
} \
\
internal s32 \
PushElement (element_type Data, element_type##_contiguous_array* Buffer) \
{ \
s32 Result = -1; \
\
if (Buffer->Used >= Buffer->BucketSize * Buffer->BucketCount) \
{ \
GrowBuffer(Buffer); \
} \
\
s32 Index = Buffer->Used++; \
s32 BucketIndex = Index / Buffer->BucketSize; \
s32 IndexInBucket = Index % Buffer->BucketSize; \
\
Buffer->Buckets[BucketIndex][IndexInBucket] = Data; \
Result = Index; \
return Result; \
} \
\
internal void \
RemoveElementAtIndex (s32 Index, element_type##_contiguous_array* Buffer) \
{ \
Assert(Index < Buffer->Used); \
\
bucket_index IndexToRemove = GetBucketIndexForIndex(Index, Buffer->BucketSize); \
bucket_index LastIndex = GetBucketIndexForIndex(Buffer->Used - 1, Buffer->BucketSize); \
element_type ValueAtLastIndex = Buffer->Buckets[LastIndex.Bucket][LastIndex.IndexInBucket]; \
Buffer->Buckets[IndexToRemove.Bucket][IndexToRemove.IndexInBucket] = ValueAtLastIndex; \
--Buffer->Used; \
} \
// END OF CRAZY MACRO
TYPEDEF_ARRAY(array_entry_handle);
TYPEDEF_CONTIGUOUS_ARRAY(array_entry_handle);

1
src/gs_hashtable.h Normal file
View File

@ -0,0 +1 @@
//

View File

@ -105,5 +105,7 @@ MouseButtonTransitionedUp (b32 MouseButtonState)
internal b32
MouseButtonHeldDown (b32 MouseButtonState)
{
return (KeyWasDown(MouseButtonState) && KeyIsDown(MouseButtonState));
b32 WasDown = KeyWasDown(MouseButtonState);
b32 IsDown = KeyIsDown(MouseButtonState);
return (WasDown && IsDown);
}

View File

@ -1,423 +0,0 @@
#ifndef GS_LANGUAGE_H
#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__)
#include <windows.h>
#include <intrin.h>
// TODO(Peter): Get rid of stdio and math.h
#include <stdio.h>
#include <math.h>
#elif defined(__APPLE__) && defined(__MAC__)
// TODO(Peter):
#else // Std lib
#include <stdlib.h>
#endif
#define internal static
#define local_persist static
#define global_variable static
#if !defined(GS_TYPES)
#define GSINT64(s) (s) ## L
#define GSUINT64(s) (s) ## UL
typedef signed char b8;
typedef short int b16;
typedef int b32;
typedef long long int b64;
typedef unsigned char u8;
typedef unsigned short int u16;
typedef unsigned int u32;
typedef unsigned long long int u64;
typedef signed char s8;
typedef short int s16;
typedef int s32;
typedef long long int s64;
typedef float r32;
typedef double r64;
#define INT8_MIN (-128)
#define INT16_MIN (-32767-1)
#define INT32_MIN (-2147483647-1)
#define INT64_MIN (-GSINT64(9223372036854775807)-1)
#define INT8_MAX (127)
#define INT16_MAX (32767)
#define INT32_MAX (2147483647)
#define INT64_MAX (GSINT64(9223372036854775807))
#define UINT8_MAX (255)
#define UINT16_MAX (65535)
#define UINT32_MAX (4294967295U)
#define UINT64_MAX (GSUINT64(18446744073709551615))
#define FLOAT_MIN (1.175494351e-38F)
#define FLOAT_MAX (3.402823466e+38F)
#define DOUBLE_MIN (2.2250738585072014e-308)
#define DOUBLE_MAX (1.7976931348623158e+308)
#define Kilobytes(Value) ((Value) * 1024)
#define Megabytes(Value) (Kilobytes(Value) * 1024)
#define Gigabytes(Value) (Megabytes(Value) * 1024)
#define Terabytes(Value) (Gigabytes(Value) * 1024)
#define PI 3.14159265359
#define PI_OVER_180 0.01745329251f
#define GS_TYPES
#endif
#ifdef DEBUG
static void DebugPrint(char* Format, ...);
#if !defined(Assert)
// NOTE(peter): this writes to address 0 which is always illegal and will cause a crash
#define Assert(expression) if(!(expression)){ *((int *)0) = 5; }
#endif
#define DEBUG_IF(condition) if (condition)
#define InvalidCodePath Assert(0)
#define InvalidDefaultCase default: { Assert(0); }
#define DebugBreak __debugbreak()
#define STBI_ASSERT(x) Assert(x)
#ifdef GS_TEST_SUTE
#define TestClean(v, c) SuccessCount += Test(v, c, &TestCount)
internal s32
Test(b32 Result, char* Description, s32* Count)
{
char* Passed = (Result ? "Success" : "Failed");
if (!Result)
DebugPrint("%s:\n................................................%s\n\n", Description, Passed);
*Count = *Count + 1;
return (Result ? 1 : 0);
}
#endif // GS_TEST_SUTE
#else
#define Assert(expression)
#define InvalidCodePath
#define DEBUG_IF(condition)
//#define DEBUG_TRACK_SCOPE(a)
#endif // DEBUG
#ifndef GS_LANGUAGE_MATH
#define GSZeroStruct(data) GSZeroMemory_((u8*)(&(data)), sizeof(data))
#define GSZeroMemory(mem, size) GSZeroMemory_((u8*)(mem), (size))
static void
GSZeroMemory_ (u8* Memory, s32 Size)
{
for (int i = 0; i < Size; i++) { Memory[i] = 0; }
}
#define GSMemCopy(from, to, size) GSMemCopy_((u8*)from, (u8*)to, size)
static void
GSMemCopy_ (u8* From, u8* To, s32 Size)
{
for (int i = 0; i < Size; i++) { To[i] = From[i]; }
}
#define GSMemSet(buffer, value, size) GSMemSet_((u8*)buffer, value, size)
internal void
GSMemSet_ (u8* Buffer, u8 Value, s32 Length)
{
u8* Cursor = Buffer;
for (s32 i = 0; i < Length; i++)
{
*Cursor++ = Value;
}
}
#define GSMinDef(type) static type GSMin(type A, type B) { return (A < B ? A : B); }
GSMinDef(s8)
GSMinDef(s16)
GSMinDef(s32)
GSMinDef(s64)
GSMinDef(u8)
GSMinDef(u16)
GSMinDef(u32)
GSMinDef(u64)
GSMinDef(r32)
GSMinDef(r64)
#undef GSMinDef
#define GSMaxDef(type) static type GSMax(type A, type B) { return (A > B ? A : B); }
GSMaxDef(s8)
GSMaxDef(s16)
GSMaxDef(s32)
GSMaxDef(s64)
GSMaxDef(u8)
GSMaxDef(u16)
GSMaxDef(u32)
GSMaxDef(u64)
GSMaxDef(r32)
GSMaxDef(r64)
#undef GSMaxDef
inline b32 XOR(b32 A, b32 B)
{
b32 Result = (A == !B);
return Result;
}
#define GSClampDef(type) static type GSClamp(type Min, type V, type Max) { \
type Result = V; \
if (V < Min) { Result = Min; } \
if (V > Max) { Result = Max; } \
return Result; \
}
GSClampDef(s8)
GSClampDef(s16)
GSClampDef(s32)
GSClampDef(s64)
GSClampDef(u8)
GSClampDef(u16)
GSClampDef(u32)
GSClampDef(u64)
GSClampDef(r32)
GSClampDef(r64)
#undef GSClampDef
#define GSClamp01Def(type) static type GSClamp01(type V) { \
type Min = 0; type Max = 1; \
type Result = V; \
if (V < Min) { Result = Min; } \
if (V > Max) { Result = Max; } \
return Result; \
}
GSClamp01Def(r32)
GSClamp01Def(r64)
#undef GSClamp01Def
#define GSAbsDef(type) static type GSAbs(type A) { return (A < 0 ? -A : A); }
GSAbsDef(s8)
GSAbsDef(s16)
GSAbsDef(s32)
GSAbsDef(s64)
GSAbsDef(r32)
GSAbsDef(r64)
#undef GSAbsDef
#define GSPowDef(type) static type GSPow(type N, s32 Power) { \
type Result = N; \
for(s32 i = 1; i < Power; i++) { Result *= N; } \
return Result; \
}
GSPowDef(s8)
GSPowDef(s16)
GSPowDef(s32)
GSPowDef(s64)
GSPowDef(u8)
GSPowDef(u16)
GSPowDef(u32)
GSPowDef(u64)
GSPowDef(r32)
GSPowDef(r64)
#undef GSPowDef
#define GSLerpDef(type) type GSLerp(type A, type B, type Percent) { return (A * (1.0f - Percent))+(B * Percent);}
GSLerpDef(r32)
GSLerpDef(r64)
#undef GSLerpDef
static r32 GSSqrt(r32 V)
{
r32 Result = _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V)));
return Result;
}
#if 0
// TODO(Peter): Need a way to split the input into two f32's to supply to _mm_sqrt_sd
static r64 GSSqrt(r64 V)
{
r64 Result = _mm_cvtsd_f64(_mm_sqrt_sd(_mm_set_sd(V)));
return Result;
}
#endif
static r32 DegreesToRadians (r32 Degrees) { return Degrees * PI_OVER_180; }
static r64 DegreesToRadians (r64 Degrees) { return Degrees * PI_OVER_180; }
#define GSIsPowerOfTwoDef(type) static type IsPowerOfTwo(type V) { return (V & (V - 1)) == 0; }
GSIsPowerOfTwoDef(u8);
GSIsPowerOfTwoDef(u16);
GSIsPowerOfTwoDef(u32);
GSIsPowerOfTwoDef(u64);
#undef GSIsPowerOfTwoDef
#define GSIsOddDef(type) inline type IsOdd(type V) { return (V & 1); }
GSIsOddDef(u8);
GSIsOddDef(u16);
GSIsOddDef(u32);
GSIsOddDef(u64);
GSIsOddDef(s8);
GSIsOddDef(s16);
GSIsOddDef(s32);
GSIsOddDef(s64);
#undef GSIsOddDef
#define GSIntDivideRoundUpDef(type) static type IntegerDivideRoundUp (type A, type B) { r32 Result = (r32)A / (r32)B; Result += .99999f; return (type)Result; }
GSIntDivideRoundUpDef(u8);
GSIntDivideRoundUpDef(u16);
GSIntDivideRoundUpDef(u32);
GSIntDivideRoundUpDef(u64);
GSIntDivideRoundUpDef(s8);
GSIntDivideRoundUpDef(s16);
GSIntDivideRoundUpDef(s32);
GSIntDivideRoundUpDef(s64);
#undef GSIntDivideRoundUpDef
#define GSRemapDef(type) \
static type GSRemap(type Value, type OldMin, type OldMax, type NewMin, type NewMax) { \
type Result = (Value - OldMin) / (OldMax - OldMin); \
Result = (Result * (NewMax - NewMin)) + NewMin; \
return Result; \
}
GSRemapDef(u8);
GSRemapDef(u16);
GSRemapDef(u32);
GSRemapDef(u64);
GSRemapDef(s8);
GSRemapDef(s16);
GSRemapDef(s32);
GSRemapDef(s64);
GSRemapDef(r32);
GSRemapDef(r64);
#undef GSRemapDef
#define GSTrigFunctionDef(name, type, func) static type name(type V) { return func(V); }
GSTrigFunctionDef(GSSin, r32, sinf);
GSTrigFunctionDef(GSSin, r64, sin);
GSTrigFunctionDef(GSCos, r32, cosf);
GSTrigFunctionDef(GSCos, r64, cos);
GSTrigFunctionDef(GSTan, r32, tanf);
GSTrigFunctionDef(GSTan, r64, tan);
#undef GSTrigFunctionDef
static u8
RoundToNearestPowerOfTwo (u8 V)
{
u8 Result = 0;
if (IsPowerOfTwo(V))
{
Result = V;
}
else
{
Result = V - 1;
Result |= Result >> 1;
Result |= Result >> 2;
Result |= Result >> 4;
Result += 1;
}
return Result;
}
static u16
RoundToNearestPowerOfTwo (u16 V)
{
u16 Result = 0;
if (IsPowerOfTwo(V))
{
Result = V;
}
else
{
Result = V - 1;
Result |= Result >> 1;
Result |= Result >> 2;
Result |= Result >> 4;
Result |= Result >> 8;
Result += 1;
}
return Result;
}
static u32
RoundToNearestPowerOfTwo (u32 V)
{
u32 Result = 0;
if (IsPowerOfTwo(V))
{
Result = V;
}
else
{
Result = V - 1;
Result |= Result >> 1;
Result |= Result >> 2;
Result |= Result >> 4;
Result |= Result >> 8;
Result |= Result >> 16;
Result += 1;
}
return Result;
}
static u64
RoundToNearestPowerOfTwo (u64 V)
{
u64 Result = 0;
if (IsPowerOfTwo(V))
{
Result = V;
}
else
{
Result = V - 1;
Result |= Result >> 1;
Result |= Result >> 2;
Result |= Result >> 4;
Result |= Result >> 8;
Result |= Result >> 16;
Result |= Result >> 32;
Result += 1;
}
return Result;
}
#define GS_LANGUAGE_MATH
#endif // GS_LANGUAGE_MATH
static u32
HostToNetU32(u32 In)
{
unsigned char *s = (unsigned char *)&In;
u32 Result = (u32)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
return Result;
}
static u16
HostToNetU16(u16 In)
{
unsigned char *s = (unsigned char *)&In;
u16 Result = (u16)(s[0] << 8 | s[1]);
return Result;
}
#define GS_LANGUAGE_H
#endif

View File

@ -1,461 +0,0 @@
#ifndef GS_MEMORY_H
#define ArenaZeroStruct(data_ptr) ArenaZeroStruct_((u8*)data_ptr, sizeof(*data_ptr))
inline void
ArenaZeroStruct_ (u8* Base, s32 Size)
{
u8* Iter = Base;
for (s32 i = 0; i < Size; i++) { *Iter++ = 0; }
}
struct grow_arena_result
{
u8* Base;
s32 Size;
};
#define GROW_ARENA_MEMORY(name) grow_arena_result name(s32 Size)
typedef GROW_ARENA_MEMORY(grow_arena_memory);
#define FREE_ARENA_MEMORY(name) b32 name(u8* Base, s32 Size)
typedef FREE_ARENA_MEMORY(free_arena_memory);
struct memory_region_header
{
memory_region_header* Prev;
s32 Size;
s32 Used;
u8* Base;
};
inline b32
RegionCanFitSize (memory_region_header* Header, s32 Size)
{
b32 Result = (Header->Used + Size) <= Header->Size;
return Result;
}
inline b32
AddressIsInRegion (memory_region_header* Header, u8* Address)
{
b32 Result = (Header->Base <= Address) && (Header->Base + Header->Used > Address);
return Result;
}
#ifndef DEFAULT_MEMORY_ALIGNMENT
#define DEFAULT_MEMORY_ALIGNMENT (2 * sizeof(void*))
#endif
b32 GSMemIsPowerOfTwo (u64 Address)
{
return (Address & (Address - 1)) == 0;
}
u64 AlignForward (u64 Base, u64 Align)
{
u64 P, A, Modulo;
Assert(GSMemIsPowerOfTwo(Align));
P = Base;
A = Align;
Modulo = P & (A - 1);
if (Modulo != 0)
{
P = P + (A - Modulo);
}
return P;
}
//////////////////////////////
// Heap Memory
//////////////////////////////
// heap_memory_arena
// a growable memory arena that has two ways to interact with it: push and clear.
// Push: returns a free region of continguous memory. If the arenas GrowArenaProc function is set, this may
// get called in order to obtain enough free memory to fulfil the push request
// Clear: clears the entire memory arena. If the arena has been grown at any point, those subsequent
// regions of memory will be freed back to the system.
struct heap_memory_arena
{
memory_region_header* CurrentRegion;
s32 RegionMemorySize;
grow_arena_memory* GrowArenaProc;
free_arena_memory* FreeArenaMemoryProc;
};
static void
GrowHeapArena (heap_memory_arena* Arena, s32 RequestedSize)
{
if (Arena->GrowArenaProc)
{
Assert(Arena->RegionMemorySize > 0);
s32 GrowthSize = GSMax(RequestedSize, Arena->RegionMemorySize);
grow_arena_result NewMemory = Arena->GrowArenaProc(GrowthSize + sizeof(memory_region_header));
Assert(NewMemory.Size > 0);
memory_region_header* Header = (memory_region_header*)NewMemory.Base;
Header->Base = (u8*)NewMemory.Base + sizeof(memory_region_header);
Header->Size = NewMemory.Size - sizeof(memory_region_header);
Header->Used = 0;
Header->Prev = Arena->CurrentRegion;
Arena->CurrentRegion = Header;
}
else
{
InvalidCodePath;
}
}
#define PushStruct(arena, type) (type*)PushSize_(arena, sizeof(type))
#define PushArray(arena, type, count) (type*)PushSize_(arena, sizeof(type)*count)
static u8*
PushSize_ (heap_memory_arena* Arena, s32 Size)
{
if (!Arena->CurrentRegion) { GrowHeapArena(Arena, Size); }
u8* CurrPointer = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used;
u64 Offset = AlignForward((u64)CurrPointer, DEFAULT_MEMORY_ALIGNMENT);
Offset -= (u64)(Arena->CurrentRegion->Base + Arena->CurrentRegion->Used);
if (!RegionCanFitSize(Arena->CurrentRegion, Size + Offset))
{
// TODO(Peter): There might be empty space in the current region, its just not big enough for the
// requested size. We should search backwards to see if there is enough space in a previous region
// before growing the arena.
GrowHeapArena(Arena, Size + Offset);
}
u8* Result = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used + Offset;
Arena->CurrentRegion->Used += Size + Offset;
GSZeroMemory(Result, Size);
return Result;
}
static void
InitHeapMemoryArena (heap_memory_arena* Arena, s32 RegionMemorySize,
grow_arena_memory* GrowProc, free_arena_memory* FreeProc)
{
ArenaZeroStruct(Arena);
Arena->RegionMemorySize = RegionMemorySize;
Arena->GrowArenaProc = GrowProc;
Arena->FreeArenaMemoryProc = FreeProc;
}
static void
InitHeapMemoryArena (heap_memory_arena* Arena, u8* Base, s32 Size,
s32 RegionMemorySize = 0,
grow_arena_memory* GrowProc = 0,
free_arena_memory* FreeProc = 0)
{
Assert(Size > sizeof(memory_region_header));
Arena->CurrentRegion = (memory_region_header*)Base;
Arena->CurrentRegion->Base = Base + sizeof(memory_region_header);
Arena->CurrentRegion->Size = Size - sizeof(memory_region_header);
Arena->CurrentRegion->Used = 0;
Arena->CurrentRegion->Prev = 0;
Arena->RegionMemorySize = RegionMemorySize;
Arena->GrowArenaProc = GrowProc;
Arena->FreeArenaMemoryProc = FreeProc;
}
static void
ClearHeapMemoryArena (heap_memory_arena* Arena)
{
if (!Arena->CurrentRegion) { return; }
memory_region_header* CurrentHead = Arena->CurrentRegion;
if (CurrentHead->Prev)
{
Assert(Arena->FreeArenaMemoryProc);
while(CurrentHead->Prev)
{
memory_region_header* PrevHead = CurrentHead->Prev;
Arena->FreeArenaMemoryProc((u8*)CurrentHead, CurrentHead->Size + sizeof(memory_region_header));
CurrentHead = PrevHead;
}
Arena->CurrentRegion = CurrentHead;
}
Arena->CurrentRegion->Used = 0;
}
//////////////////////////////
// Stack Memory
//////////////////////////////
struct stack_memory_region
{
stack_memory_region* Prev;
};
// stack_memory_arena
// Push: returns a free region of continguous memory. If the arenas GrowArenaProc function is set, this may
// get called in order to obtain enough free memory to fulfil the push request
// Pop: frees the last region allocated on the stack, returning it to the region of memory available to
// be used.
// Clear: clears the entire memory arena. If the arena has been grown at any point, those subsequent
// regions of memory will be freed back to the system.
struct stack_memory_arena
{
memory_region_header* CurrentRegion;
stack_memory_region* UsedList;
s32 RegionMemorySize;
grow_arena_memory* GrowArenaProc;
free_arena_memory* FreeArenaMemoryProc;
};
static u8*
PushSize_ (stack_memory_arena* Arena, s32 Size)
{
if (!Arena->CurrentRegion ||
!RegionCanFitSize(Arena->CurrentRegion, Size))
{
if (Arena->GrowArenaProc)
{
Assert(Arena->RegionMemorySize > 0);
grow_arena_result NewMemory = Arena->GrowArenaProc(Arena->RegionMemorySize + sizeof(memory_region_header));
Assert(NewMemory.Size > 0);
memory_region_header* Header = (memory_region_header*)NewMemory.Base;
Header->Base = (u8*)NewMemory.Base + sizeof(memory_region_header);
Header->Size = NewMemory.Size - sizeof(memory_region_header);
Header->Used = 0;
Header->Prev = Arena->CurrentRegion;
Arena->CurrentRegion = Header;
}
else
{
InvalidCodePath;
}
}
u8* Region = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used;
stack_memory_region* UsedListHeader = (stack_memory_region*)Region;
UsedListHeader->Prev = Arena->UsedList;
Arena->UsedList = UsedListHeader;
u8* Result = Region + sizeof(stack_memory_region);
Arena->CurrentRegion->Used += Size + sizeof(stack_memory_region);
return Result;
}
// NOTE(Peter): Returns size available after the Pop operation
static s32
PopLast (stack_memory_arena* Arena)
{
s32 Result = Arena->CurrentRegion->Size - Arena->CurrentRegion->Used;
if (Arena->UsedList)
{
u8* LastHead = (u8*)Arena->UsedList;
if (!AddressIsInRegion(Arena->CurrentRegion, LastHead) &&
Arena->FreeArenaMemoryProc)
{
memory_region_header* PrevHeader = Arena->CurrentRegion->Prev;
Arena->FreeArenaMemoryProc((u8*)Arena->CurrentRegion,
Arena->CurrentRegion->Size + sizeof(memory_region_header));
Arena->CurrentRegion = PrevHeader;
}
Assert(LastHead >= Arena->CurrentRegion->Base &&
LastHead <= Arena->CurrentRegion->Base + Arena->CurrentRegion->Size);
stack_memory_region* PrevAlloc = Arena->UsedList->Prev;
s32 SizeUsed = LastHead - Arena->CurrentRegion->Base;
Arena->CurrentRegion->Used = SizeUsed;
Result = Arena->CurrentRegion->Size - Arena->CurrentRegion->Used;
Arena->UsedList = PrevAlloc;
}
return Result;
}
static void
InitStackMemoryArena (stack_memory_arena* Arena, s32 RegionMemorySize,
grow_arena_memory* GrowProc, free_arena_memory* FreeProc)
{
ArenaZeroStruct(Arena);
Arena->RegionMemorySize = RegionMemorySize;
Arena->GrowArenaProc = GrowProc;
Arena->FreeArenaMemoryProc = FreeProc;
}
static void
InitStackMemoryArena (stack_memory_arena* Arena, u8* Base, s32 Size,
s32 RegionMemorySize = 0,
grow_arena_memory* GrowProc = 0,
free_arena_memory* FreeProc = 0)
{
Assert(Size > sizeof(memory_region_header));
Arena->CurrentRegion = (memory_region_header*)Base;
Arena->CurrentRegion->Base = Base + sizeof(memory_region_header);
Arena->CurrentRegion->Size = Size - sizeof(memory_region_header);
Arena->CurrentRegion->Used = 0;
Arena->CurrentRegion->Prev = 0;
Arena->RegionMemorySize = RegionMemorySize;
Arena->GrowArenaProc = GrowProc;
Arena->FreeArenaMemoryProc = FreeProc;
}
static void
ClearStackMemoryArena (stack_memory_arena* Arena)
{
if (!Arena->CurrentRegion) { return; }
memory_region_header* CurrentHead = Arena->CurrentRegion;
if (CurrentHead->Prev)
{
Assert(Arena->FreeArenaMemoryProc);
while(CurrentHead->Prev)
{
memory_region_header* PrevHead = CurrentHead->Prev;
Arena->FreeArenaMemoryProc((u8*)CurrentHead, CurrentHead->Size + sizeof(memory_region_header));
CurrentHead = PrevHead;
}
Arena->CurrentRegion = CurrentHead;
}
Arena->CurrentRegion->Used = 0;
Arena->UsedList = 0;
}
//////////////////////////////
// Pool Memory
//////////////////////////////
struct chunk_header
{
chunk_header* Prev;
};
struct pool_memory_arena
{
memory_region_header* CurrentRegion;
s32 ChunkSize;
chunk_header* FreeList;
s32 RegionMemorySize;
grow_arena_memory* GrowArenaProc;
free_arena_memory* FreeArenaMemoryProc;
};
struct chunk_result
{
s32 Size;
u8* Base;
};
static chunk_result
PushChunk (pool_memory_arena* Arena)
{
chunk_result Result = {};
if (Arena->FreeList)
{
Result.Base = (u8*)Arena->FreeList;
Result.Size = Arena->ChunkSize;
Arena->FreeList = Arena->FreeList->Prev;
}
else
{
if (!RegionCanFitSize(Arena->CurrentRegion, Arena->ChunkSize))
{
if (Arena->GrowArenaProc)
{
grow_arena_result NewMemory = Arena->GrowArenaProc(Arena->RegionMemorySize + sizeof(memory_region_header));
Assert(NewMemory.Size > 0);
memory_region_header* Header = (memory_region_header*)NewMemory.Base;
Header->Base = (u8*)NewMemory.Base + sizeof(memory_region_header);
Header->Size = NewMemory.Size - sizeof(memory_region_header);
Header->Used = 0;
Header->Prev = Arena->CurrentRegion;
Arena->CurrentRegion = Header;
}
else
{
InvalidCodePath;
}
}
Result.Base = Arena->CurrentRegion->Base + Arena->CurrentRegion->Used;
Result.Size = Arena->ChunkSize;
Arena->CurrentRegion->Used += Arena->ChunkSize;
}
return Result;
}
static void
FreeChunk (pool_memory_arena* Arena, u8* Base, s32 Size)
{
Assert(Arena->ChunkSize == Size);
chunk_header* Header = (chunk_header*)Base;
Header->Prev = Arena->FreeList;
Arena->FreeList = Header;
}
static void
InitPoolMemoryArena (pool_memory_arena* Arena, s32 ChunkSize, s32 ChunksPerRegion,
grow_arena_memory* GrowProc, free_arena_memory* FreeProc)
{
Assert(ChunkSize > sizeof(chunk_header));
ArenaZeroStruct(Arena);
Arena->ChunkSize = ChunkSize;
Arena->RegionMemorySize = ChunkSize * ChunksPerRegion;
Arena->GrowArenaProc = GrowProc;
Arena->FreeArenaMemoryProc = FreeProc;
}
static void
InitStackMemoryArena (pool_memory_arena* Arena, u8* Base, s32 Size,
s32 ChunkSize, s32 ChunksPerRegion,
grow_arena_memory* GrowProc = 0,
free_arena_memory* FreeProc = 0)
{
Assert(Size > sizeof(memory_region_header));
Assert(Size % ChunkSize == ChunksPerRegion);
Arena->CurrentRegion = (memory_region_header*)Base;
Arena->CurrentRegion->Base = Base + sizeof(memory_region_header);
Arena->CurrentRegion->Size = Size - sizeof(memory_region_header);
Arena->CurrentRegion->Used = 0;
Arena->CurrentRegion->Prev = 0;
Arena->ChunkSize = ChunkSize;
Arena->RegionMemorySize = ChunkSize * ChunksPerRegion;
Arena->GrowArenaProc = GrowProc;
Arena->FreeArenaMemoryProc = FreeProc;
}
#define GS_MEMORY_H
#endif // GS_MEMORY_H

View File

@ -75,6 +75,9 @@ typedef PLATFORM_ALLOC(platform_alloc);
#define PLATFORM_FREE(name) b32 name(u8* Base, s32 Size)
typedef PLATFORM_FREE(platform_free);
#define PLATFORM_REALLOC(name) u8* name(u8* Base, u32 OldSize, u32 NewSize)
typedef PLATFORM_REALLOC(platform_realloc);
#define PLATFORM_READ_ENTIRE_FILE(name) platform_memory_result name(char* Path)
typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file);
@ -87,6 +90,13 @@ typedef PLATFORM_GET_FILE_PATH(platform_get_file_path);
#define PLATFORM_GET_GPU_TEXTURE_HANDLE(name) s32 name(u8* Memory, s32 Width, s32 Height)
typedef PLATFORM_GET_GPU_TEXTURE_HANDLE(platform_get_gpu_texture_handle);
struct platform_network_address
{
s32 Family;
u16 Port;
u32 Address;
};
typedef s32 platform_socket_handle;
typedef s32 platform_network_address_handle;
@ -99,7 +109,7 @@ typedef PLATFORM_GET_SEND_ADDRESS_HANDLE(platform_get_send_address);
#define PLATFORM_SET_SOCKET_OPTION(name) s32 name(platform_socket_handle SocketHandle, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength)
typedef PLATFORM_SET_SOCKET_OPTION(platform_set_socket_option);
#define PLATFORM_SEND_TO(name) s32 name(platform_socket_handle SocketHandle, platform_network_address_handle AddressHandle, const char* Buffer, s32 BufferLength, s32 Flags)
#define PLATFORM_SEND_TO(name) s32 name(platform_socket_handle SocketHandle, platform_network_address Address, const char* Buffer, s32 BufferLength, s32 Flags)
typedef PLATFORM_SEND_TO(platform_send_to);
#define PLATFORM_CLOSE_SOCKET(name) void name(platform_socket_handle SocketHandle)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,7 @@ internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer
internal PLATFORM_ALLOC(Win32Alloc);
internal PLATFORM_FREE(Win32Free);
internal PLATFORM_REALLOC(Win32Realloc);
// File IO
internal PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile);
@ -610,6 +611,17 @@ PLATFORM_FREE(Win32Free)
return Result;
}
PLATFORM_REALLOC(Win32Realloc)
{
u8* NewMemory = Win32BasicAlloc(NewSize);
if (Base)
{
GSMemCopy(Base, NewMemory, OldSize);
Win32Free(Base, OldSize);
}
return NewMemory;
}
// File IO
PLATFORM_READ_ENTIRE_FILE(Win32ReadEntireFile)
{

View File

@ -8,12 +8,14 @@ enum string_alignment
internal void
DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, v4 Color)
{
r32 MaxX = MinX + (CodepointInfo.Width);
r32 MaxY = MinY + (CodepointInfo.Height);
s32 AlignedMinX = (s32)(MinX);
s32 AlignedMinY = (s32)(MinY);
s32 AlignedMaxX = AlignedMinX + (CodepointInfo.Width);
s32 AlignedMaxY = AlignedMinY + (CodepointInfo.Height);
PushQuad2DOnBatch(BatchConstructor,
v2{MinX, MinY}, v2{MaxX, MinY},
v2{MaxX, MaxY}, v2{MinX, MaxY},
v2{(r32)AlignedMinX, (r32)AlignedMinY}, v2{(r32)AlignedMaxX, (r32)AlignedMinY},
v2{(r32)AlignedMaxX, (r32)AlignedMaxY}, v2{(r32)AlignedMinX, (r32)AlignedMaxY},
CodepointInfo.UVMin, CodepointInfo.UVMax,
Color);
}
@ -432,140 +434,54 @@ enum selection_state
Selection_Deselected,
};
struct scroll_list_result
struct interface_list
{
s32 IndexSelected;
s32 StartIndex;
selection_state Selection;
rect ListBounds;
v2 ListElementDimensions;
v2 ElementLabelIndent;
v4 TextColor;
v4* LineBGColors;
s32 LineBGColorsCount;
v4 LineBGHoverColor;
s32 ListElementsCount;
};
internal scroll_list_result
DrawOptionsList(render_command_buffer* RenderBuffer, v2 Min, v2 Max,
string* Options, s32 OptionsCount,
s32 Start, interface_config Config, mouse_state Mouse)
internal rect
DrawListElementBackground(interface_list* List, mouse_state Mouse, render_command_buffer* RenderBuffer)
{
scroll_list_result Result = {};
Result.IndexSelected = -1;
Result.StartIndex = Start;
Result.Selection = Selection_None;
rect LineBounds = {};
LineBounds.Min = v2{
List->ListBounds.Min.x,
List->ListBounds.Max.y - (List->ListElementDimensions.y * (List->ListElementsCount + 1))
};
LineBounds.Max = LineBounds.Min + List->ListElementDimensions;
r32 OptionHeight = NewLineYOffset(*Config.Font) + (2 * Config.Margin.y);
r32 OptionOffset = OptionHeight + Config.Margin.y;
s32 OptionsToDisplay = ((Max.y - Min.y) / OptionHeight) - 2;
OptionsToDisplay = GSMin(OptionsToDisplay, (OptionsCount - Start));
v2 ButtonMin = v2{Min.x, Max.y - OptionHeight};
v2 ButtonMax = v2{Max.x, Max.y};
string* OptionCursor = Options + Start;
for (s32 i = 0; i < OptionsToDisplay; i++)
v4 Color = List->LineBGColors[List->ListElementsCount % List->LineBGColorsCount];
if (PointIsInRange(Mouse.Pos, LineBounds.Min, LineBounds.Max))
{
button_result Button = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
*OptionCursor,
Config, Mouse);
if (Button.Pressed)
{
Result.IndexSelected = Start + i;
Result.Selection = Selection_Selected;
}
OptionCursor++;
ButtonMin.y -= OptionOffset;
ButtonMax.y -= OptionOffset;
Color = List->LineBGHoverColor;
}
r32 HalfWidthWithMargin = ((Max.x - Min.x) / 2.0f) - Config.Margin.x;
string DownArrowString = MakeStringLiteral(" v ");
string UpArrowString = MakeStringLiteral(" ^ ");
button_result Down = EvaluateButton(RenderBuffer, Min, v2{Min.x + HalfWidthWithMargin, Min.y + OptionHeight},
DownArrowString, Config, Mouse);
button_result Up = EvaluateButton(RenderBuffer, v2{Min.x + HalfWidthWithMargin + Config.Margin.x, Min.y},
v2{Max.x, Min.y + OptionHeight},
UpArrowString, Config, Mouse);
if (Down.Pressed)
{
Result.StartIndex += 1;
}
if (Up.Pressed)
{
Result.StartIndex -= 1;
}
Result.StartIndex = GSClamp(0, Result.StartIndex, OptionsCount);
return Result;
PushRenderQuad2D(RenderBuffer, LineBounds.Min, LineBounds.Max, Color);
return LineBounds;
}
internal scroll_list_result
DrawSelectableOptionsList(render_command_buffer* RenderBuffer, v2 Min, v2 Max,
string* Options, s32 OptionsCount,
s32 Start, s32 Selected, interface_config Config, mouse_state Mouse)
internal rect
DrawListElement(string Label, interface_list* List, mouse_state Mouse, render_command_buffer* RenderBuffer, interface_config Interface)
{
scroll_list_result Result = {};
Result.IndexSelected = Selected;
Result.StartIndex = Start;
Result.Selection = Selection_None;
rect Bounds = DrawListElementBackground(List, Mouse, RenderBuffer);
r32 OptionHeight = NewLineYOffset(*Config.Font) + (2 * Config.Margin.y);
r32 OptionOffset = OptionHeight + Config.Margin.y;
v2 LabelPosition = Bounds.Min + List->ElementLabelIndent;
DrawString(RenderBuffer, Label, Interface.Font, LabelPosition, List->TextColor);
s32 OptionsToDisplay = ((Max.y - Min.y) / OptionHeight) - 2;
OptionsToDisplay = GSMin(OptionsToDisplay, (OptionsCount - Start));
string* OptionCursor = 0;
OptionCursor = Options + Start;
v2 ButtonMin = v2{Min.x, Max.y - OptionHeight};
v2 ButtonMax = v2{Max.x, Max.y};
for (s32 i = 0; i < OptionsToDisplay; i++)
{
button_result Button = EvaluateSelectableButton(RenderBuffer, ButtonMin, ButtonMax,
*OptionCursor,
Config, Mouse, (Selected == Start + i));
if (Button.Pressed)
{
s32 SelectedIndex = Start + i;
if (SelectedIndex == Result.IndexSelected)
{
Result.Selection = Selection_Deselected;
Result.IndexSelected = -1;
}
else
{
Result.Selection = Selection_Selected;
Result.IndexSelected = Start + i;
}
}
OptionCursor++;
ButtonMin.y -= OptionOffset;
ButtonMax.y -= OptionOffset;
}
r32 HalfWidthWithMargin = ((Max.x - Min.x) / 2.0f) - Config.Margin.x;
string DownArrowString = MakeStringLiteral(" v ");
string UpArrowString = MakeStringLiteral(" ^ ");
button_result Down = EvaluateButton(RenderBuffer, Min, v2{Min.x + HalfWidthWithMargin, Min.y + OptionHeight},
DownArrowString, Config, Mouse);
button_result Up = EvaluateButton(RenderBuffer, v2{Min.x + HalfWidthWithMargin + Config.Margin.x, Min.y},
v2{Max.x, Min.y + OptionHeight},
UpArrowString, Config, Mouse);
if (Down.Pressed)
{
Result.StartIndex += 1;
}
if (Up.Pressed)
{
Result.StartIndex -= 1;
}
Result.StartIndex = GSClamp(0, Result.StartIndex, OptionsCount);
return Result;
List->ListElementsCount++;
return Bounds;
}
internal r32
EvaluateColorChannelSlider (render_command_buffer* RenderBuffer, v4 ChannelMask, v2 Min, v2 Max, r32 Current, mouse_state Mouse)
{

View File

@ -0,0 +1,498 @@
////////////////////////////////////////
//
// Node Lister
//
///////////////////////////////////////
struct node_lister_operation_state
{
search_lister SearchLister;
v2 ListPosition;
};
OPERATION_RENDER_PROC(RenderNodeLister)
{
node_lister_operation_state* OpState = (node_lister_operation_state*)Operation.OpStateMemory;
v2 TopLeft = OpState->ListPosition;
v2 Dimension = v2{300, 30};
// Filter the lister
OpState->SearchLister.Filter = State->ActiveTextEntry.Buffer;
FilterSearchLister(&OpState->SearchLister);
// Display Search Lister
search_lister_result NodeListerResult = EvaluateSearchLister (RenderBuffer, TopLeft, Dimension,
MakeStringLiteral("Nodes List"),
OpState->SearchLister.SourceList,
OpState->SearchLister.FilteredIndexLUT,
OpState->SearchLister.FilteredListCount,
OpState->SearchLister.HotItem,
&State->ActiveTextEntry.Buffer,
State->ActiveTextEntry.CursorPosition,
State->Font, State->Interface, Mouse);
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerNextItem)
{
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
OpState->SearchLister.HotItem = GetNextFilteredItem(OpState->SearchLister);
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerPrevItem)
{
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
OpState->SearchLister.HotItem = GetPrevFilteredItem(OpState->SearchLister);
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeLister)
{
DeactivateCurrentOperationMode(&State->Modes);
}
FOLDHAUS_INPUT_COMMAND_PROC(SelectAndCloseNodeLister)
{
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
s32 FilteredNodeIndex = OpState->SearchLister.HotItem;
if (FilteredNodeIndex >= 0)
{
s32 NodeIndex = OpState->SearchLister.FilteredIndexLUT[FilteredNodeIndex];
PushNodeOnListFromSpecification(State->NodeList, (node_type)NodeIndex,
Mouse.Pos, State->Permanent);
}
CloseNodeLister(State, Event, Mouse);
}
input_command UniverseViewCommads [] = {
{ KeyCode_DownArrow, KeyCode_Invalid, Command_Began, NodeListerNextItem },
{ KeyCode_UpArrow, KeyCode_Invalid, Command_Began, NodeListerPrevItem },
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, SelectAndCloseNodeLister },
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, CloseNodeLister },
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseNodeLister },
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
};
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeLister)
{
operation_mode* AddNodeOperation = ActivateOperationModeWithCommands(&State->Modes, UniverseViewCommads);
AddNodeOperation->Render = RenderNodeLister;
node_lister_operation_state* OpState = CreateOperationState(AddNodeOperation,
&State->Modes,
node_lister_operation_state);
{
OpState->SearchLister.SourceListCount = NodeSpecificationsCount;
OpState->SearchLister.SourceList = PushArray(&State->Modes.Arena, string, OpState->SearchLister.SourceListCount);
{
for (s32 i = 0; i < OpState->SearchLister.SourceListCount; i++)
{
OpState->SearchLister.SourceList[i] = MakeString(
NodeSpecifications[i].Name,
NodeSpecifications[i].NameLength);
}
}
OpState->SearchLister.Filter = MakeString(PushArray(&State->Modes.Arena, char, 64), 0, 64);
OpState->SearchLister.FilteredListMax = OpState->SearchLister.SourceListCount;
OpState->SearchLister.FilteredListCount = 0;
OpState->SearchLister.FilteredIndexLUT = PushArray(&State->Modes.Arena, s32, OpState->SearchLister.SourceListCount);
}
OpState->ListPosition = Mouse.Pos;
SetTextInputDestinationToString(&State->ActiveTextEntry, &OpState->SearchLister.Filter);
}
////////////////////////////////////////
//
// Node Color Picker
//
///////////////////////////////////////
struct color_picker_operation_state
{
v4* ValueAddr;
};
internal void
CloseColorPicker(app_state* State)
{
DeactivateCurrentOperationMode(&State->Modes);
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseColorPickerCommand)
{
CloseColorPicker(State);
}
OPERATION_RENDER_PROC(RenderColorPicker)
{
color_picker_operation_state* OpState = (color_picker_operation_state*)Operation.OpStateMemory;
b32 ShouldClose = EvaluateColorPicker(RenderBuffer, OpState->ValueAddr,
v2{200, 200}, State->Interface, Mouse);
if (ShouldClose)
{
CloseColorPicker(State);
}
}
input_command ColorPickerCommands [] = {
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseColorPickerCommand },
};
internal void
OpenColorPicker(app_state* State, node_connection* Connection)
{
operation_mode* ColorPickerMode = ActivateOperationModeWithCommands(&State->Modes, ColorPickerCommands);
ColorPickerMode->Render = RenderColorPicker;
color_picker_operation_state* OpState = CreateOperationState(ColorPickerMode,
&State->Modes,
color_picker_operation_state);
OpState->ValueAddr = Connection->V4ValuePtr;
}
////////////////////////////////////////
//
// Node Field Text Edit
//
///////////////////////////////////////
FOLDHAUS_INPUT_COMMAND_PROC(EndNodeFieldTextEdit)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command NodeFieldTextEditCommands [] = {
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
};
internal void
BeginNodeFieldTextEdit(app_state* State, node_connection* Connection)
{
operation_mode* NodeFieldTextEditMode = ActivateOperationModeWithCommands(&State->Modes,
NodeFieldTextEditCommands);
SetTextInputDestinationToFloat(&State->ActiveTextEntry, Connection->R32ValuePtr);
}
////////////////////////////////////////
//
// Node Port Mouse Drag
//
///////////////////////////////////////
struct drag_node_port_operation_state
{
node_interaction Interaction;
};
OPERATION_RENDER_PROC(RenderDraggingNodePort)
{
drag_node_port_operation_state* OpState = (drag_node_port_operation_state*)Operation.OpStateMemory;
UpdateDraggingNodePort(Mouse.Pos, OpState->Interaction, State->NodeList,
State->NodeRenderSettings, RenderBuffer);
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNodePort)
{
drag_node_port_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_port_operation_state);
TryConnectNodes(OpState->Interaction, Mouse.Pos, State->NodeList, State->NodeRenderSettings);
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragNodePortInputCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNodePort },
};
internal void
BeginDraggingNodePort(app_state* State, node_interaction Interaction)
{
operation_mode* DragNodePortMode = ActivateOperationModeWithCommands(
&State->Modes,
DragNodePortInputCommands);
DragNodePortMode->Render = RenderDraggingNodePort;
drag_node_port_operation_state* OpState = CreateOperationState(DragNodePortMode,
&State->Modes,
drag_node_port_operation_state);
OpState->Interaction = Interaction;
}
////////////////////////////////////////
//
// Node Field Mouse Drag
//
///////////////////////////////////////
OPERATION_RENDER_PROC(RenderDragNodeField)
{
// TODO(Peter):
//UpdateDraggingNodeValue(Mouse.Pos, Mouse.OldPos, OpState->Interaction, State->NodeList, State->NodeRenderSettings, State);
}
internal void
BeginInteractWithNodeField(app_state* State, node_interaction Interaction)
{
// TODO(Peter):
}
////////////////////////////////////////
//
// Node Mouse Drag
//
///////////////////////////////////////
struct drag_node_operation_state
{
node_interaction Interaction;
};
OPERATION_RENDER_PROC(RenderDraggingNode)
{
drag_node_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_operation_state);
UpdateDraggingNode(Mouse.Pos, OpState->Interaction, State->NodeList,
State->NodeRenderSettings);
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNode)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragNodeInputCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNode },
};
internal void
BeginDraggingNode(app_state* State, node_interaction Interaction)
{
operation_mode* DragNodeMode = ActivateOperationModeWithCommands(
&State->Modes,
DragNodeInputCommands);
DragNodeMode->Render = RenderDraggingNode;
drag_node_operation_state* OpState = CreateOperationState(DragNodeMode,
&State->Modes,
drag_node_operation_state);
OpState->Interaction = Interaction;
}
////////////////////////////////////////
//
// Node View
//
///////////////////////////////////////
struct node_view_operation_state
{
s32 SelectedNodeHandle;
};
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseDragInteraction)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.DownPos, State->NodeRenderSettings);
if (Node)
{
node_interaction NewInteraction = GetNodeInteractionType(Node,
Mouse.Pos,
State->NodeRenderSettings);
if (IsDraggingNodePort(NewInteraction))
{
BeginDraggingNodePort(State, NewInteraction);
}
else if(IsDraggingNodeValue(NewInteraction))
{
// TODO(Peter): This probably wants to live in a mouse held action
// the first frame we realize we're held over a field, just transition to
// drag node field
//BeginInteractWithNodeField(State, NewInteraction, State->NodeRenderSettings);
}
else // IsDraggingNode
{
OpState->SelectedNodeHandle = Node->Handle;
BeginDraggingNode(State, NewInteraction);
}
}
else
{
OpState->SelectedNodeHandle = 0;
}
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseSelectInteraction)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.Pos, State->NodeRenderSettings);
if (Node)
{
node_interaction NewInteraction = GetNodeInteractionType(Node,
Mouse.Pos,
State->NodeRenderSettings);
if(IsDraggingNodeValue(NewInteraction))
{
node_connection* Connection = Node->Connections + NewInteraction.InputValue;
struct_member_type InputType = Connection->Type;
if (InputType == MemberType_r32)
{
BeginNodeFieldTextEdit(State, Connection);
}
else if (InputType == MemberType_v4)
{
OpenColorPicker(State, Connection);
}
}
}
}
OPERATION_RENDER_PROC(RenderNodeView)
{
node_view_operation_state* OpState = (node_view_operation_state*)Operation.OpStateMemory;
DEBUG_TRACK_FUNCTION;
MakeStringBuffer(NodeHeaderBuffer, 128);
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
node_list_iterator NodeIter = GetNodeListIterator(*State->NodeList);
while (NodeIteratorIsValid(NodeIter))
{
node_header* Node = NodeIter.At;
rect NodeBounds = CalculateNodeBounds(Node, State->NodeRenderSettings);
b32 DrawFields = PointIsInRect(Mouse.Pos, NodeBounds);
if (Node == SelectedNode)
{
PushRenderQuad2D(RenderBuffer, NodeBounds.Min - v2{2, 2}, NodeBounds.Max + v2{2, 2}, WhiteV4);
}
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.5f, .5f, .5f, 1.f});
// TODO(Peter): This is just for debug purposes. We can remove and go back to just having
// Node->Name in DrawString
string NodeName = GetNodeName(*Node);
PrintF(&NodeHeaderBuffer, "%.*s: %d", NodeName.Length, NodeName.Memory, Node->Handle);
DrawString(RenderBuffer, NodeHeaderBuffer, State->NodeRenderSettings.Font,
v2{NodeBounds.Min.x + 5, NodeBounds.Max.y - (State->NodeRenderSettings.Font->PixelHeight + NODE_HEADER_HEIGHT + 5)},
WhiteV4);
for (s32 Connection = 0; Connection < Node->ConnectionsCount; Connection++)
{
v4 PortColor = State->NodeRenderSettings.PortColors[Node->Connections[Connection].Type];
// Inputs
if (ConnectionIsInput(Node, Connection))
{
rect PortBounds = CalculateNodeInputPortBounds(Node, Connection, State->NodeRenderSettings);
DrawPort(RenderBuffer, PortBounds, PortColor);
//
// TODO(Peter): I don't like excluding OutputNode, feels too much like a special case
// but I don't want to get in to the meta programming right now.
// We should just generate a spec and struct member types for NodeType_OutputNode
//
// :ExcludingOutputNodeSpecialCase
//
if (Node->Type != NodeType_OutputNode && DrawFields)
{
node_specification Spec = NodeSpecifications[Node->Type];
node_struct_member Member = Spec.MemberList[Connection];
DrawString(RenderBuffer, MakeString(Member.Name),
State->NodeRenderSettings.Font,
v2{PortBounds.Min.x - 8, PortBounds.Min.y}, WhiteV4, Align_Right);
}
rect ValueBounds = CalculateNodeInputValueBounds(Node, Connection, State->NodeRenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
// NOTE(Peter): its way easier to draw the connection on the input port b/c its a 1:1 relationship,
// whereas output ports might have many connections, they really only know about the most recent one
// Not sure if this is a problem. We mostly do everything backwards here, starting at the
// most downstream node and working back up to find dependencies.
if (ConnectionHasUpstreamConnection(Node, Connection))
{
rect ConnectedPortBounds = GetBoundsOfPortConnectedToInput(Node, Connection, State->NodeList, State->NodeRenderSettings);
v2 InputCenter = CalculateRectCenter(PortBounds);
v2 OutputCenter = CalculateRectCenter(ConnectedPortBounds);
PushRenderLine2D(RenderBuffer, OutputCenter, InputCenter, 1, WhiteV4);
}
}
// Outputs
if (ConnectionIsOutput(Node, Connection))
{
rect PortBounds = CalculateNodeOutputPortBounds(Node, Connection, State->NodeRenderSettings);
DrawPort(RenderBuffer, PortBounds, PortColor);
if (DrawFields)
{
node_specification Spec = NodeSpecifications[Node->Type];
node_struct_member Member = Spec.MemberList[Connection];
DrawString(RenderBuffer, MakeString(Member.Name),
State->NodeRenderSettings.Font,
v2{PortBounds.Max.x + 8, PortBounds.Min.y}, WhiteV4);
}
rect ValueBounds = CalculateNodeOutputValueBounds(Node, Connection, State->NodeRenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
}
for (s32 Button = 0; Button < 3; Button++)
{
rect ButtonRect = CalculateNodeDragHandleBounds(NodeBounds, Button, State->NodeRenderSettings);
PushRenderQuad2D(RenderBuffer, ButtonRect.Min, ButtonRect.Max, DragButtonColors[Button]);
}
}
Next(&NodeIter);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewDeleteNode)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
if (OpState->SelectedNodeHandle > 0)
{
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
FreeNodeOnList(State->NodeList, SelectedNode);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeView)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command NodeViewCommands [] = {
{ KeyCode_Tab, KeyCode_Invalid, Command_Began, CloseNodeView},
{ KeyCode_A, KeyCode_Invalid, Command_Began, OpenNodeLister},
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, NodeViewBeginMouseDragInteraction},
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, NodeViewBeginMouseSelectInteraction},
{ KeyCode_X, KeyCode_Invalid, Command_Began, NodeViewDeleteNode},
};
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeView)
{
operation_mode* NodeViewMode = ActivateOperationModeWithCommands(&State->Modes, NodeViewCommands);
NodeViewMode->Render = RenderNodeView;
node_view_operation_state* OpState = CreateOperationState(NodeViewMode,
&State->Modes,
node_view_operation_state);
OpState->SelectedNodeHandle = 0;
}

View File

@ -0,0 +1,492 @@
// TODO
// [x] - Moving animation blocks
// [x] - dragging beginning and end of time blocks
// [] - creating a timeblock with a specific animation
// [x] - play, pause, stop,
// [] - setting the start and end of the animation system
// [] - displaying multiple layers
inline r32
GetTimeFromPointInAnimationPanel(v2 Point, rect PanelBounds, s32 StartFrame, s32 EndFrame, r32 SecondsPerFrame)
{
r32 StartFrameTime = (r32)StartFrame * SecondsPerFrame;
r32 EndFrameTime = (r32)EndFrame * SecondsPerFrame;
r32 TimeAtPoint = GSRemap(Point.x, PanelBounds.Min.x, PanelBounds.Max.x, StartFrameTime, EndFrameTime);
return TimeAtPoint;
}
inline s32
GetFrameFromPointInAnimationPanel (v2 Point, rect PanelBounds, s32 StartFrame, s32 EndFrame, r32 SecondsPerFrame)
{
r32 TimeAtPoint = GetTimeFromPointInAnimationPanel(Point, PanelBounds, StartFrame, EndFrame, SecondsPerFrame);
s32 FrameAtPoint = (s32)(TimeAtPoint * SecondsPerFrame);
return FrameAtPoint;
}
inline s32
GetXPositionFromTimeInAnimationPanel (r32 Time, rect PanelBounds, s32 StartFrame, s32 EndFrame, r32 SecondsPerFrame)
{
r32 StartFrameTime = (r32)StartFrame * SecondsPerFrame;
r32 EndFrameTime = (r32)EndFrame * SecondsPerFrame;
s32 XPositionAtTime = GSRemap(Time, StartFrameTime, EndFrameTime, PanelBounds.Min.x, PanelBounds.Max.x);
return XPositionAtTime;
}
internal void
AddAnimationBlock(r32 StartTime, r32 EndTime, animation_proc* Proc, animation_system* AnimationSystem)
{
animation_block NewBlock = {0};
NewBlock.StartTime = StartTime;
NewBlock.EndTime = EndTime;
NewBlock.Proc = Proc;
AddAnimationBlock(NewBlock, AnimationSystem);
}
#define NEW_ANIMATION_BLOCK_DURATION 3
internal void
AddAnimationBlockAtCurrentTime (animation_proc* Proc, animation_system* System)
{
r32 CurrentTime = System->Time;
AddAnimationBlock(CurrentTime, CurrentTime + NEW_ANIMATION_BLOCK_DURATION, Proc, System);
}
internal void
DeleteAnimationBlock(animation_block_handle AnimationBlockHandle, app_state* State)
{
RemoveAnimationBlock(State->SelectedAnimationBlockHandle, &State->AnimationSystem);
State->SelectedAnimationBlockHandle = {0};
}
internal void
SelectAnimationBlock(animation_block_handle BlockHandle, app_state* State)
{
State->SelectedAnimationBlockHandle = BlockHandle;
}
internal void
DeselectCurrentAnimationBlock(app_state* State)
{
State->SelectedAnimationBlockHandle = {};
}
FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
{
if (AnimationBlockHandleIsValid(State->SelectedAnimationBlockHandle))
{
DeleteAnimationBlock(State->SelectedAnimationBlockHandle, State);
}
}
//
// Drag Time Marker
//
OPERATION_STATE_DEF(drag_time_marker_operation_state)
{
rect TimelineBounds;
s32 StartFrame;
s32 EndFrame;
};
OPERATION_RENDER_PROC(UpdateDragTimeMarker)
{
drag_time_marker_operation_state* OpState = (drag_time_marker_operation_state*)Operation.OpStateMemory;
r32 TimeAtMouseX = GetTimeFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->StartFrame, OpState->EndFrame, State->AnimationSystem.SecondsPerFrame);
State->AnimationSystem.Time = TimeAtMouseX;
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDragTimeMarker)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragTimeMarkerCommands [] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragTimeMarker },
};
internal void
StartDragTimeMarker(rect TimelineBounds, s32 PanelStartFrame, s32 PanelEndFrame, app_state* State)
{
operation_mode* DragTimeMarkerMode = ActivateOperationModeWithCommands(&State->Modes, DragTimeMarkerCommands, UpdateDragTimeMarker);
drag_time_marker_operation_state* OpState = CreateOperationState(DragTimeMarkerMode,
&State->Modes,
drag_time_marker_operation_state);
OpState->StartFrame = PanelStartFrame;
OpState->EndFrame = PanelEndFrame ;
OpState->TimelineBounds = TimelineBounds;
}
// --------------------
//
// Drag Animation Clip
//
#define CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE 10
OPERATION_STATE_DEF(drag_animation_clip_state)
{
rect TimelineBounds;
r32 AnimationPanel_StartFrame;
r32 AnimationPanel_EndFrame;
r32 SelectedClip_InitialStartTime;
r32 SelectedClip_InitialEndTime;
};
OPERATION_RENDER_PROC(UpdateDragAnimationClip)
{
drag_animation_clip_state* OpState = (drag_animation_clip_state*)Operation.OpStateMemory;
s32 ClipInitialStartTimeXPosition = GetXPositionFromTimeInAnimationPanel(OpState->SelectedClip_InitialStartTime, OpState->TimelineBounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
s32 ClipInitialEndTimeXPosition = GetXPositionFromTimeInAnimationPanel(OpState->SelectedClip_InitialEndTime, OpState->TimelineBounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
r32 TimeAtMouseDownX = GetTimeFromPointInAnimationPanel(Mouse.DownPos, OpState->TimelineBounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
r32 TimeAtMouseX = GetTimeFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
r32 TimeOffset = TimeAtMouseX - TimeAtMouseDownX;
animation_block* AnimationBlock = GetAnimationBlockWithHandle(State->SelectedAnimationBlockHandle, &State->AnimationSystem);
if (GSAbs(Mouse.DownPos.x - ClipInitialStartTimeXPosition) < CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE)
{
AnimationBlock->StartTime = OpState->SelectedClip_InitialStartTime + TimeOffset;
}
else if (GSAbs(Mouse.DownPos.x - ClipInitialEndTimeXPosition) < CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE)
{
AnimationBlock->EndTime = OpState->SelectedClip_InitialEndTime + TimeOffset;
}
else
{
AnimationBlock->StartTime = OpState->SelectedClip_InitialStartTime + TimeOffset;
AnimationBlock->EndTime = OpState->SelectedClip_InitialEndTime + TimeOffset;
}
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDragAnimationClip)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragAnimationClipCommands [] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragAnimationClip },
};
internal void
SelectAndBeginDragAnimationBlock(animation_block_handle BlockHandle, s32 PanelStartFrame, s32 PanelEndFrame, rect TimelineBounds, app_state* State)
{
SelectAnimationBlock(BlockHandle, State);
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
&State->Modes,
drag_animation_clip_state);
OpState->TimelineBounds = TimelineBounds;
OpState->AnimationPanel_StartFrame = PanelStartFrame;
OpState->AnimationPanel_EndFrame = PanelEndFrame ;
animation_block* SelectedBlock = GetAnimationBlockWithHandle(BlockHandle, &State->AnimationSystem);
OpState->SelectedClip_InitialStartTime = SelectedBlock->StartTime;
OpState->SelectedClip_InitialEndTime = SelectedBlock->EndTime;
}
// -------------------
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
{
panel_and_bounds ActivePanel = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, State->WindowBounds);
r32 MouseDownPositionPercent = (Mouse.Pos.x - ActivePanel.Bounds.Min.x) / Width(ActivePanel.Bounds);
r32 NewBlockTimeStart = MouseDownPositionPercent * State->AnimationSystem.AnimationEnd;
#define NEW_BLOCK_DURATION 1
r32 NewBlockTimeEnd = NewBlockTimeStart + NEW_BLOCK_DURATION;
animation_block Block = {0};
Block.StartTime = NewBlockTimeStart;
Block.EndTime = NewBlockTimeEnd;
Block.Proc = TestPatternThree;
animation_block_handle NewBlockHandle = AddAnimationBlock(Block, &State->AnimationSystem);
SelectAnimationBlock(NewBlockHandle, State);
}
input_command AnimationTimeline_Commands[] = {
{ KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand },
{ KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand },
};
PANEL_INIT_PROC(AnimationTimeline_Init)
{
}
PANEL_CLEANUP_PROC(AnimationTimeline_Cleanup)
{
}
internal r32
DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, s32 StartFrame, s32 EndFrame, rect PanelBounds, mouse_state Mouse, app_state* State)
{
MakeStringBuffer(TempString, 256);
s32 FrameCount = EndFrame - StartFrame;
r32 FrameBarHeight = 24;
v2 FrameBarMin = v2{PanelBounds.Min.x, PanelBounds.Max.y - FrameBarHeight};
v2 FrameBarMax = PanelBounds.Max;
PushRenderQuad2D(RenderBuffer, FrameBarMin, FrameBarMax, v4{.16f, .16f, .16f, 1.f});
// Mouse clicked inside frame nubmer bar -> change current frame on timeline
if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
&& PointIsInRange(Mouse.DownPos, FrameBarMin, FrameBarMax))
{
StartDragTimeMarker(rect{FrameBarMin, FrameBarMax}, StartFrame, EndFrame, State);
}
// Frame Ticks
for (s32 f = 0; f < FrameCount; f += 10)
{
s32 Frame = StartFrame + f;
PrintF(&TempString, "%d", Frame);
r32 FramePercent = (r32)f / (r32)FrameCount;
r32 FrameX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, FramePercent);
v2 FrameTextPos = v2{FrameX, FrameBarMin.y + 2};
DrawString(RenderBuffer, TempString, State->Interface.Font, FrameTextPos, WhiteV4);
// Frame Vertical Slices
v2 LineTop = v2{FrameX, FrameBarMin.y};
v2 LineBottom = v2{FrameX + 1, PanelBounds.Min.y};
PushRenderQuad2D(RenderBuffer, LineTop, LineBottom, v4{.16f, .16f, .16f, 1.f});
}
return FrameBarMin.y;
}
internal rect
DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, r32 SecondsPerFrame, s32 FrameCount, s32 StartFrame, rect TimelineBounds, render_command_buffer* RenderBuffer)
{
rect BlockBounds = {};
r32 TimelineWidth = Width(TimelineBounds);
s32 BlockStartFrame = AnimationBlock.StartTime / SecondsPerFrame;
r32 StartFramePercent = (r32)(BlockStartFrame - StartFrame) / (r32)FrameCount;
r32 StartPosition = TimelineWidth * StartFramePercent;
s32 BlockEndFrame = AnimationBlock.EndTime / SecondsPerFrame;
r32 EndFramePercent = (r32)(BlockEndFrame - StartFrame) / (r32)FrameCount;
r32 EndPosition = TimelineWidth * EndFramePercent;
BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, 25};
BlockBounds.Max = TimelineBounds.Min + v2{EndPosition, 75};
PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor);
PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4);
return BlockBounds;
}
internal animation_block_handle
DrawAnimationTimeline (animation_system* AnimationSystem, s32 StartFrame, s32 EndFrame, rect PanelBounds, animation_block_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse)
{
string TempString = MakeString(PushArray(&State->Transient, char, 256), 256);
s32 FrameCount = EndFrame - StartFrame;
animation_block_handle Result = SelectedBlockHandle;
r32 AnimationPanelHeight = PanelBounds.Max.y - PanelBounds.Min.y;
r32 AnimationPanelWidth = PanelBounds.Max.x - PanelBounds.Min.x;
{
s32 FirstPlayableFrame = (AnimationSystem->AnimationStart / AnimationSystem->SecondsPerFrame);
s32 LastPlayableFrame = (AnimationSystem->AnimationEnd / AnimationSystem->SecondsPerFrame);
r32 FirstPlayablePercentX = ((r32)(FirstPlayableFrame - StartFrame) / (r32)FrameCount);
r32 LastPlayablePercentX = ((r32)(LastPlayableFrame - StartFrame) / (r32)FrameCount);
v2 PlayableMin = v2{(FirstPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Min.y };
v2 PlayableMax = v2{(LastPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Max.y };
PushRenderQuad2D(RenderBuffer, PanelBounds.Min, PanelBounds.Max, v4{.16f, .16f, .16f, 1.f});
PushRenderQuad2D(RenderBuffer, PlayableMin, PlayableMax, v4{.22f, .22f, .22f, 1.f});
}
r32 FrameBarBottom = DrawFrameBar(AnimationSystem, RenderBuffer, StartFrame, EndFrame, PanelBounds, Mouse, State);
// Animation Blocks
rect TimelineBounds = rect{ PanelBounds.Min, v2{PanelBounds.Max.x, FrameBarBottom} };
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState);
for (u32 i = 0; i < AnimationSystem->BlocksCount; i++)
{
animation_block_entry* AnimationBlockEntry = GetEntryAtIndex(i, AnimationSystem);
if (AnimationBlockIsFree(*AnimationBlockEntry)) { continue; }
animation_block_handle CurrentBlockHandle = {};
CurrentBlockHandle.Index = i;
CurrentBlockHandle.Generation = AnimationBlockEntry->Generation;
animation_block AnimationBlockAt = AnimationBlockEntry->Block;
v4 BlockColor = BlackV4;
if (AnimationBlockHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle))
{
BlockColor = PinkV4;
}
rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, AnimationSystem->SecondsPerFrame, FrameCount, StartFrame, TimelineBounds, RenderBuffer);
if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max)
&& MouseButtonTransitionedDown(Mouse.LeftButtonState))
{
MouseDownAndNotHandled = false;
SelectAndBeginDragAnimationBlock(CurrentBlockHandle, StartFrame, EndFrame, TimelineBounds, State);
}
}
// Time Slider
s32 SliderFrame = AnimationSystem->Time / AnimationSystem->SecondsPerFrame;
r32 TimePercent = (r32)(SliderFrame - StartFrame) / (r32)FrameCount;
r32 SliderX = PanelBounds.Min.x + (AnimationPanelWidth * TimePercent);
v2 SliderMin = v2{SliderX, PanelBounds.Min.y};
v2 SliderMax = v2{SliderX + 1, PanelBounds.Max.y - 25};
v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f};
PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, TimeSliderColor);
r32 SliderHalfWidth = 10;
v2 HeadMin = v2{SliderX - SliderHalfWidth, SliderMax.y};
v2 HeadMax = v2{SliderX + SliderHalfWidth, PanelBounds.Max.y};
PushRenderQuad2D(RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
PrintF(&TempString, "%d", SliderFrame);
DrawString(RenderBuffer, TempString, State->Interface.Font, HeadMin + v2{4, 4}, WhiteV4);
if (MouseDownAndNotHandled && PointIsInRect(Mouse.Pos, TimelineBounds))
{
DeselectCurrentAnimationBlock(State);
}
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(rect PanelBounds, mouse_state Mouse, render_command_buffer* RenderBuffer, app_state* State)
{
v4 LineBGColors[] = {
{ .16f, .16f, .16f, 1.f },
{ .18f, .18f, .18f, 1.f },
};
interface_list List = {};
List.LineBGColors = LineBGColors;
List.LineBGColorsCount = sizeof(LineBGColors) / sizeof(LineBGColors[0]);
List.LineBGHoverColor = v4{ .22f, .22f, .22f, 1.f };
List.TextColor = WhiteV4;
List.ListBounds = PanelBounds;
List.ListElementDimensions = v2{
Width(PanelBounds),
(r32)(State->Interface.Font->PixelHeight + 8),
};
List.ElementLabelIndent = v2{10, 4};
string TitleString = MakeStringLiteral("Animation Clips");
DrawListElement(TitleString, &List, Mouse, RenderBuffer, State->Interface);
for (s32 i = 0; i < GlobalAnimationClipsCount; i++)
{
animation_clip Clip = GlobalAnimationClips[i];
string ClipName = MakeString(Clip.Name, Clip.NameLength);
rect ElementBounds = DrawListElement(ClipName, &List, Mouse, RenderBuffer, State->Interface);
if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
&& PointIsInRect(Mouse.DownPos, ElementBounds))
{
AddAnimationBlockAtCurrentTime(Clip.Proc, &State->AnimationSystem);
}
}
}
PANEL_RENDER_PROC(AnimationTimeline_Render)
{
animation_block_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle;
r32 OptionsRowHeight = 25;
rect AnimationClipListBounds = rect{
PanelBounds.Min,
v2{PanelBounds.Min.x + 300, PanelBounds.Max.y - OptionsRowHeight},
};
rect TimelineBounds = rect{
v2{AnimationClipListBounds.Max.x, PanelBounds.Min.y},
v2{PanelBounds.Max.x, PanelBounds.Max.y - OptionsRowHeight},
};
if (Height(TimelineBounds) > 0)
{
DrawAnimationClipsList(AnimationClipListBounds, Mouse, RenderBuffer, State);
s32 FrameStart = (s32)(State->AnimationSystem.AnimationStart / State->AnimationSystem.SecondsPerFrame);
s32 FrameEnd = (s32)(State->AnimationSystem.AnimationEnd / State->AnimationSystem.SecondsPerFrame);
SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem,
FrameStart - 20, FrameEnd + 20,
TimelineBounds,
SelectedBlockHandle,
RenderBuffer, State, Mouse);
}
v2 OptionsRowMin = v2{ PanelBounds.Min.x, TimelineBounds.Max.y };
v2 OptionsRowMax = PanelBounds.Max;
panel_result AnimationPanel = EvaluatePanel(RenderBuffer, OptionsRowMin, OptionsRowMax,
0, State->Interface);
r32 ButtonWidth = 35;
v2 ButtonMin = v2{0, 0};
v2 ButtonMax = v2{35, OptionsRowHeight - 2};
v2 ButtonAt = v2{OptionsRowMin.x + 1, OptionsRowMin.y + 1};
button_result PauseResult = EvaluateButton(RenderBuffer,
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
MakeStringLiteral("Pause"),
State->Interface, Mouse);
ButtonAt.x += ButtonWidth + 2;
button_result PlayResult = EvaluateButton(RenderBuffer,
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
MakeStringLiteral("Play"),
State->Interface, Mouse);
ButtonAt.x += ButtonWidth + 2;
button_result StopResult = EvaluateButton(RenderBuffer,
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
MakeStringLiteral("Stop"),
State->Interface, Mouse);
if (PauseResult.Pressed)
{
State->AnimationSystem.TimelineShouldAdvance = false;
}
if (PlayResult.Pressed)
{
State->AnimationSystem.TimelineShouldAdvance = true;
}
if (StopResult.Pressed)
{
State->AnimationSystem.TimelineShouldAdvance = false;
State->AnimationSystem.Time = 0;
}
}

View File

@ -0,0 +1,77 @@
PANEL_INIT_PROC(DMXView_Init)
{
}
PANEL_CLEANUP_PROC(DMXView_Cleanup)
{
}
PANEL_RENDER_PROC(DMXView_Render)
{
#if 0
DEBUG_TRACK_SCOPE(DrawUniverseOutputDisplay);
universe_view_operation_state* OpState = (universe_view_operation_state*)Operation.OpStateMemory;
string TitleBarString = InitializeEmptyString(PushArray(State->Transient, char, 64), 64);
v2 DisplayArea_Dimension = v2{600, 600};
v2 DisplayContents_Offset = OpState->DisplayOffset;
//
// TODO(Peter): I don't like this. Dragging the Universe view should be an operation mode, just
// like rotating the 3D view, but modes don't have access to the state of modes above them in the stack
// (and attempting to cast those states to the appropriate type seems risky)
//
// :NeedToPassStateDownModeChain
//
if (OpState->MouseDown)
{
DisplayContents_Offset += (Mouse.Pos - Mouse.DownPos);
}
v2 DisplayArea_TopLeft = v2{300, (r32)RenderBuffer->ViewHeight - 50} + DisplayContents_Offset;
v2 UniverseDisplayDimension = v2{100, 100} * OpState->Zoom;
v2 Padding = v2{25, 50} * OpState->Zoom;
v2 UniverseDisplayTopLeft = DisplayArea_TopLeft;
sacn_universe_buffer* UniverseList = State->SACN.UniverseBuffer;
while(UniverseList)
{
for (s32 UniverseIdx = 0;
UniverseIdx < UniverseList->Used;
UniverseIdx++)
{
sacn_universe* Universe = UniverseList->Universes + UniverseIdx;
DrawSACNUniversePixels(RenderBuffer, Universe, UniverseDisplayTopLeft, UniverseDisplayDimension);
if (OpState->Zoom > .5f)
{
v2 TitleDisplayStart = UniverseDisplayTopLeft + v2{0, 12};
PrintF(&TitleBarString, "Universe %d", Universe->Universe);
DrawString(RenderBuffer, TitleBarString, State->Interface.Font,
TitleDisplayStart, WhiteV4);
}
UniverseDisplayTopLeft.x += UniverseDisplayDimension.x + Padding.x;
if (UniverseDisplayTopLeft.x > DisplayArea_TopLeft.x + DisplayArea_Dimension.x)
{
UniverseDisplayTopLeft.x = DisplayArea_TopLeft.x;
UniverseDisplayTopLeft.y -= UniverseDisplayDimension.y + Padding.y;
}
if (UniverseDisplayTopLeft.y < DisplayArea_TopLeft.y - DisplayArea_Dimension.y)
{
break;
}
}
UniverseList = UniverseList->Next;
}
#endif
}

View File

@ -0,0 +1,78 @@
PANEL_INIT_PROC(HierarchyView_Init)
{
}
PANEL_CLEANUP_PROC(HierarchyView_Cleanup)
{
}
PANEL_RENDER_PROC(HierarchyView_Render)
{
r32 PanelWidth = PanelBounds.Max.x - PanelBounds.Min.x;
r32 PanelHeight = PanelBounds.Max.y - PanelBounds.Min.y;
v4 LineBGColors[] = {
{ .16f, .16f, .16f, 1.f },
{ .18f, .18f, .18f, 1.f },
};
interface_list List = {};
List.LineBGColors = LineBGColors;
List.LineBGColorsCount = sizeof(LineBGColors) / sizeof(LineBGColors[0]);
List.LineBGHoverColor = v4{ .22f, .22f, .22f, 1.f };
List.ListBounds = PanelBounds;
List.ListElementDimensions = v2{
Width(PanelBounds),
(r32)(State->Interface.Font->PixelHeight + 8),
};
v2 TextOffset = v2{10, 4};
string TempString = MakeString(PushArray(&State->Transient, char, 256), 256);
s32 LineCount = (s32)(PanelHeight / List.ListElementDimensions.y) + 1;
for (s32 i = 0; i < LineCount; i++)
{
rect ElementBounds = DrawListElementBackground(&List, Mouse, RenderBuffer);
if (i < State->ActiveAssemblyIndecies.Used)
{
array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
assembly Assembly = *GetElementWithHandle(AssemblyHandle, State->AssemblyList);
PrintF(&TempString, "%S", Assembly.Name);
DrawString(RenderBuffer, TempString, State->Interface.Font, ElementBounds.Min + TextOffset, WhiteV4);
PrintF(&TempString, "X");
v2 XLowerRight = v2{ElementBounds.Max.x - 25, ElementBounds.Min.y + TextOffset.y};
v2 XLowerLeft = DrawString(RenderBuffer, TempString, State->Interface.Font, XLowerRight, WhiteV4, Align_Right);
if (MouseButtonTransitionedUp(Mouse.LeftButtonState)
&& PointIsInRange(Mouse.Pos, XLowerLeft, ElementBounds.Max))
{
UnloadAssembly(AssemblyHandle.Index, State, Context);
}
}
else if (i == State->ActiveAssemblyIndecies.Used)
{
PrintF(&TempString, "+ Add Assembly");
v2 TextMinX = ElementBounds.Min + TextOffset;
DrawString(RenderBuffer, TempString, State->Interface.Font, TextMinX, WhiteV4);
if (MouseButtonTransitionedUp(Mouse.LeftButtonState)
&& PointIsInRange(Mouse.Pos, ElementBounds.Min, ElementBounds.Max))
{
char FilePath[256];
b32 Success = Context.PlatformGetFilePath(FilePath, 256, "Foldhaus Files\0*.fold\0\0");
if (Success)
{
LoadAssembly(State, Context, FilePath);
}
}
}
List.ListElementsCount++;
}
}

View File

@ -0,0 +1,199 @@
PANEL_INIT_PROC(ProfilerView_Init)
{
}
PANEL_CLEANUP_PROC(ProfilerView_Cleanup)
{
}
internal void
RenderProfiler_ScopeVisualization(render_command_buffer* RenderBuffer,
interface_config Interface, mouse_state Mouse,
v2 Min, v2 Max,
debug_frame* VisibleFrame, memory_arena* Memory)
{
v4 ThreadColors[] = {
v4{.73f, .33f, .83f, 1},
v4{0, .50f, .50f, 1},
v4{.83f, 0, 0, 1},
v4{.33f, .49f, .83f, 1},
v4{.74f, .40f, .25f, 1},
};
r32 Width = Max.x - Min.x;
r32 DepthHeight = 64;
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
debug_scope_record_list* ThreadScopeCalls = GetScopeListForThreadInFrame(GlobalDebugServices,
VisibleFrame);
MakeStringBuffer(String, 256);
for (s32 i = 0; i < ThreadScopeCalls->Count; i++)
{
scope_record* Record = ThreadScopeCalls->Calls + i;
scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash);
r32 PercentStart = (r32)(Record->StartCycles - FrameStartCycles) / (r32)FrameTotalCycles;
r32 PercentEnd = (r32)(Record->EndCycles - FrameStartCycles) / (r32)FrameTotalCycles;
v2 ScopeMin = v2{Min.x + (Width * PercentStart), Max.y - ((Record->CallDepth + 1) * DepthHeight)};
v2 ScopeMax = v2{Min.x + (Width * PercentEnd), ScopeMin.y + (DepthHeight - 4)};
if ((ScopeMax.x - ScopeMin.x) >= 1)
{
v4 Color = ThreadColors[0];
if (PointIsInRange(Mouse.Pos, ScopeMin, ScopeMax))
{
Color = GreenV4;
}
PushRenderQuad2D(RenderBuffer, ScopeMin, ScopeMax, Color);
PushRenderBoundingBox2D(RenderBuffer, ScopeMin, ScopeMax, 1, BlackV4);
if (PointIsInRange(Mouse.Pos, ScopeMin, ScopeMax))
{
PushRenderQuad2D(RenderBuffer, Mouse.Pos, Mouse.Pos + v2{256, 32}, BlackV4);
PrintF(&String, "%.*s : %d - %d", Name->Name.Length, Name->Name.Memory, Record->StartCycles, Record->EndCycles);
DrawString(RenderBuffer, String, Interface.Font, Mouse.Pos, WhiteV4);
}
}
}
}
internal void
RenderProfiler_ListVisualization(render_command_buffer* RenderBuffer,
interface_config Interface, mouse_state Mouse,
v2 Min, v2 Max,
debug_frame* VisibleFrame, memory_arena* Memory)
{
MakeStringBuffer(String, 256);
r32 YAt = Max.y - Interface.Font->PixelHeight;
r32 Column0X = Min.x;
r32 Column1X = Column0X + 256;
r32 Column2X = Column1X + 128;
r32 Column3X = Column2X + 128;
r32 Column4X = Column3X + 100;
for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
{
scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
if (NameEntry.Hash != 0)
{
collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
PrintF(&String, "%.*s", NameEntry.Name.Length, NameEntry.Name.Memory);
DrawString(RenderBuffer, String, Interface.Font, v2{Column0X, YAt}, WhiteV4);
PrintF(&String, "%f", CollatedRecord->PercentFrameTime);
DrawString(RenderBuffer, String, Interface.Font, v2{Column1X, YAt}, WhiteV4);
PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
DrawString(RenderBuffer, String, Interface.Font, v2{Column2X, YAt}, WhiteV4);
PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
DrawString(RenderBuffer, String, Interface.Font, v2{Column3X, YAt}, WhiteV4);
PrintF(&String, "%d calls", CollatedRecord->CallCount);
DrawString(RenderBuffer, String, Interface.Font, v2{Column4X, YAt}, WhiteV4);
YAt -= Interface.Font->PixelHeight + 4;
if (YAt < Min.y) { break; }
}
}
}
PANEL_RENDER_PROC(ProfilerView_Render)
{
memory_arena* Memory = &State->Transient;
string String = InitializeEmptyString(PushArray(Memory, char, 256), 256);
v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 };
r32 FrameListHeight = 64;
v2 FrameListMin = v2{PanelBounds.Min.x + 16, PanelBounds.Max.y - (16 + FrameListHeight)};
v2 FrameListMax = v2{PanelBounds.Max.x - 16, PanelBounds.Max.y - 16};
r32 FrameListPadding = 4;
r32 FrameListInnerWidth = (FrameListMax.x - FrameListMin.x) - (FrameListPadding * 2);
r32 SingleFrameStep = FrameListInnerWidth / DEBUG_FRAME_COUNT;
r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2);
PushRenderBoundingBox2D(RenderBuffer, FrameListMin, FrameListMax, 2, WhiteV4);
if (PointIsInRange(Mouse.Pos, FrameListMin, FrameListMax) &&
MouseButtonHeldDown(Mouse.LeftButtonState))
{
r32 LocalMouseX = (Mouse.Pos.x - FrameListMin.x) + FrameListPadding;
s32 ClosestFrameIndex = (LocalMouseX / SingleFrameStep);
if (ClosestFrameIndex >= 0 && ClosestFrameIndex < DEBUG_FRAME_COUNT)
{
GlobalDebugServices->RecordFrames = false;
GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex;
}
}
for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++)
{
v2 Min = v2{FrameListMin.x + FrameListPadding + (F * SingleFrameStep), FrameListMin.y + 4};
v2 Max = v2{Min.x + SingleFrameWidth, FrameListMax.y - 4};
s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F);
if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; }
v4 Color = FrameColors[GSClamp(0, FramesAgo, 3)];
PushRenderQuad2D(RenderBuffer, Min, Max, Color);
}
debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
PrintF(&String, "Frame %d - Total Cycles: %lld",
GlobalDebugServices->CurrentDebugFrame - 1,
FrameTotalCycles);
DrawString(RenderBuffer, String, State->Interface.Font, FrameListMin - v2{0, 32}, WhiteV4);
v2 ButtonMin = v2{FrameListMax.x - 128, FrameListMin.y - 32};
v2 ButtonMax = ButtonMin + v2{128, 28};
button_result ShouldResumeRecording = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
MakeString("Resume Recording"), State->Interface, Mouse);
if (ShouldResumeRecording.Pressed)
{
GlobalDebugServices->RecordFrames = true;
}
ButtonMin = v2{FrameListMin.x, FrameListMin.y - 60};
ButtonMax = v2{FrameListMin.x + 128, FrameListMin.y - 42};
button_result ActivateScopeView = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
MakeString("Scope View"), State->Interface, Mouse);
ButtonMin.x += 152;
ButtonMax.x += 152;
button_result ActivateListView = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
MakeString("List View"), State->Interface, Mouse);
if (ActivateScopeView.Pressed) { GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER; }
if (ActivateListView.Pressed) { GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST; }
v2 ViewModeMin = v2{FrameListMin.x, PanelBounds.Min.y};
v2 ViewModeMax = v2{FrameListMax.x, FrameListMin.y - 96};
if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER)
{
RenderProfiler_ScopeVisualization(RenderBuffer, State->Interface, Mouse,
ViewModeMin, ViewModeMax,
VisibleFrame, Memory);
}
else
{
RenderProfiler_ListVisualization(RenderBuffer, State->Interface, Mouse,
ViewModeMin, ViewModeMax,
VisibleFrame, Memory);
}
}

View File

@ -0,0 +1,168 @@
// 3D Mouse View
OPERATION_STATE_DEF(mouse_rotate_view_operation_state)
{
v4 CameraStartPos;
};
OPERATION_RENDER_PROC(Update3DViewMouseRotate)
{
mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory;
v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos;
m44 XRotation = GetXRotation(-TotalDeltaPos.y * State->PixelsToWorldScale);
m44 YRotation = GetYRotation(TotalDeltaPos.x * State->PixelsToWorldScale);
m44 Combined = XRotation * YRotation;
State->Camera.Position = V3(Combined * OpState->CameraStartPos);
}
FOLDHAUS_INPUT_COMMAND_PROC(End3DViewMouseRotate)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command MouseRotateViewCommands [] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate},
};
FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate)
{
operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate);
mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode,
&State->Modes,
mouse_rotate_view_operation_state);
OpState->CameraStartPos = V4(State->Camera.Position, 1);
}
// ----------------
input_command SculptureView_Commands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, Begin3DViewMouseRotate },
};
PANEL_INIT_PROC(SculptureView_Init)
{
}
PANEL_CLEANUP_PROC(SculptureView_Cleanup)
{
}
struct draw_leds_job_data
{
led* LEDs;
pixel* Colors;
s32 StartIndex;
s32 OnePastLastIndex;
render_quad_batch_constructor* Batch;
m44 FaceCameraMatrix;
m44 ModelViewMatrix;
r32 LEDHalfWidth;
};
internal void
DrawLEDsInBufferRangeJob (s32 ThreadID, void* JobData)
{
DEBUG_TRACK_FUNCTION;
draw_leds_job_data* Data = (draw_leds_job_data*)JobData;
s32 LEDCount = Data->OnePastLastIndex - Data->StartIndex;
quad_batch_constructor_reserved_range BatchReservedRange = ThreadSafeReserveRangeInQuadConstructor(Data->Batch, LEDCount * 2);
s32 TrisUsed = 0;
r32 HalfWidth = Data->LEDHalfWidth;
v4 P0_In = v4{-HalfWidth, -HalfWidth, 0, 1};
v4 P1_In = v4{HalfWidth, -HalfWidth, 0, 1};
v4 P2_In = v4{HalfWidth, HalfWidth, 0, 1};
v4 P3_In = v4{-HalfWidth, HalfWidth, 0, 1};
v2 UV0 = v2{0, 0};
v2 UV1 = v2{1, 0};
v2 UV2 = v2{1, 1};
v2 UV3 = v2{0, 1};
led* LED = Data->LEDs + Data->StartIndex;
for (s32 LEDIdx = 0;
LEDIdx < LEDCount;
LEDIdx++)
{
pixel PixelColor = Data->Colors[LED->Index];
v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f};
v4 V4Position = LED->Position;
V4Position.w = 0;
v4 P0 = P0_In + V4Position;
v4 P1 = P1_In + V4Position;
v4 P2 = P2_In + V4Position;
v4 P3 = P3_In + V4Position;
SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++,
P0, P1, P2, UV0, UV1, UV2, Color, Color, Color);
SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++,
P0, P2, P3, UV0, UV2, UV3, Color, Color, Color);
LED++;
}
}
PANEL_RENDER_PROC(SculptureView_Render)
{
DEBUG_TRACK_SCOPE(RenderSculpture);
r32 PanelWidth = PanelBounds.Max.x - PanelBounds.Min.x;
r32 PanelHeight = PanelBounds.Max.y - PanelBounds.Min.y;
State->Camera.AspectRatio = PanelWidth / PanelHeight;
m44 ModelViewMatrix = GetCameraModelViewMatrix(State->Camera);
m44 ProjectionMatrix = GetCameraPerspectiveProjectionMatrix(State->Camera);
r32 LEDHalfWidth = .5f;
PushRenderPerspective(RenderBuffer, PanelBounds.Min.x, PanelBounds.Min.y, PanelWidth, PanelHeight, State->Camera);
// TODO(Peter): Pretty sure this isn't working right now
m44 FaceCameraMatrix = GetLookAtMatrix(v4{0, 0, 0, 1}, V4(State->Camera.Position, 1));
FaceCameraMatrix = FaceCameraMatrix;
s32 MaxLEDsPerJob = 2048;
render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->TotalLEDsCount);
for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++)
{
array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies);
assembly Assembly = *GetElementWithHandle(AssemblyHandle, State->AssemblyList);
s32 JobsNeeded = IntegerDivideRoundUp(Assembly.LEDCount, MaxLEDsPerJob);
for (s32 Job = 0; Job < JobsNeeded; Job++)
{
draw_leds_job_data* JobData = PushStruct(&State->Transient, draw_leds_job_data);
JobData->LEDs = Assembly.LEDs;
JobData->Colors = Assembly.Colors;
JobData->StartIndex = Job * MaxLEDsPerJob;
JobData->OnePastLastIndex = GSMin(JobData->StartIndex + MaxLEDsPerJob, Assembly.LEDCount);
JobData->Batch = &RenderLEDsBatch;
JobData->FaceCameraMatrix;
JobData->ModelViewMatrix = ModelViewMatrix;
JobData->LEDHalfWidth = LEDHalfWidth;
Context.GeneralWorkQueue->PushWorkOnQueue(
Context.GeneralWorkQueue,
DrawLEDsInBufferRangeJob,
JobData);
}
}
Context.GeneralWorkQueue->DoQueueWorkUntilDone(Context.GeneralWorkQueue, 0);
Context.GeneralWorkQueue->ResetWorkQueue(Context.GeneralWorkQueue);
}

View File

@ -330,8 +330,8 @@ SACNGetUniverseSendAddress(s32 Universe)
u8 MulticastAddressBuffer[4] = {};
MulticastAddressBuffer[0] = 239;
MulticastAddressBuffer[1] = 255;
MulticastAddressBuffer[2] = (u8)(Universe & 0xff00); // high bit
MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff) >> 8); // low bit
MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit
MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit
u_long V4Address = (u_long)UpackB4(MulticastAddressBuffer);
return V4Address;

View File

@ -158,41 +158,6 @@ PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle)
return (platform_socket_handle)NewSocketIndex;
}
#define NETWORK_ADDRESS_DICTIONARY_GROW_SIZE 32
s32 Win32NetworkAddressHandleMax;
s32 Win32NetworkAddressHandleCount;
sockaddr_in* NetworkAddressValues;
PLATFORM_GET_SEND_ADDRESS_HANDLE(Win32GetSendAddress)
{
if (Win32NetworkAddressHandleCount >= Win32NetworkAddressHandleMax)
{
s32 NewDictionaryMax = Win32NetworkAddressHandleMax + NETWORK_ADDRESS_DICTIONARY_GROW_SIZE;
s32 NewDictionaryDataSize = NewDictionaryMax * sizeof(sockaddr_in);
u8* DictionaryMemory = Win32Alloc(NewDictionaryDataSize);
Assert(DictionaryMemory);
sockaddr_in* NewValues = (sockaddr_in*)(DictionaryMemory);
if (NetworkAddressValues)
{
GSMemCopy(NetworkAddressValues, NewValues, sizeof(win32_socket) * NewDictionaryMax);
Win32Free((u8*)NetworkAddressValues, sizeof(win32_socket) * Win32NetworkAddressHandleCount);
}
NetworkAddressValues = NewValues;
Win32NetworkAddressHandleMax = NewDictionaryMax;
}
Assert(Win32NetworkAddressHandleCount < Win32NetworkAddressHandleMax);
s32 NewAddressIndex = Win32NetworkAddressHandleCount++;
NetworkAddressValues[NewAddressIndex].sin_family = AddressFamily;
NetworkAddressValues[NewAddressIndex].sin_port = HostToNetU16(Port);
NetworkAddressValues[NewAddressIndex].sin_addr.s_addr = HostToNetU32(Address);
return (platform_network_address_handle)NewAddressIndex;
}
PLATFORM_SET_SOCKET_OPTION(Win32SetSocketOption)
{
s32 SocketIndex = (s32)SocketHandle;
@ -211,10 +176,12 @@ PLATFORM_SEND_TO(Win32SendTo)
s32 SocketIndex = (s32)SocketHandle;
Assert(SocketIndex < Win32SocketHandleCount);
s32 AddressIndex = (s32)AddressHandle;
Assert(AddressIndex < Win32NetworkAddressHandleCount);
sockaddr_in SockAddress = {};
SockAddress.sin_family = Address.Family;
SockAddress.sin_port = HostToNetU16(Address.Port);
SockAddress.sin_addr.s_addr = HostToNetU32(Address.Address);
s32 LengthSent = sendto(SocketValues[SocketIndex].Socket, Buffer, BufferLength, Flags, (sockaddr*)&NetworkAddressValues[AddressIndex], sizeof(sockaddr_in));
s32 LengthSent = sendto(SocketValues[SocketIndex].Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
if (LengthSent == SOCKET_ERROR)
{
@ -233,7 +200,6 @@ PLATFORM_CLOSE_SOCKET(Win32CloseSocket)
closesocket(SocketValues[SocketIndex].Socket);
}
HDC FontDrawingDC;
HBITMAP FontBitmap;
HFONT CurrentFont;
@ -404,6 +370,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse
AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, false, true,
ShiftDown, AltDown, CtrlDown, false);
Mouse->RightButtonState = KeyState_IsDown & ~KeyState_WasDown;
Mouse->DownPos = Mouse->Pos;
}break;
case WM_LBUTTONUP:
@ -522,11 +489,11 @@ Win32GetThreadId()
int WINAPI
WinMain (
HINSTANCE HInstance,
HINSTANCE HPrevInstance,
PSTR CmdLineArgs,
INT NCmdShow
)
HINSTANCE HInstance,
HINSTANCE HPrevInstance,
PSTR CmdLineArgs,
INT NCmdShow
)
{
MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents);
Win32UpdateWindowDimension(&MainWindow);
@ -594,19 +561,18 @@ INT NCmdShow
context Context = {};
Context.MemorySize = InitialMemorySize;
Context.MemoryBase = InitialMemory;
Context.WindowWidth = MainWindow.Width;
Context.WindowHeight = MainWindow.Height;
Context.WindowBounds = rect{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}};
// Platform functions
Context.GeneralWorkQueue = &WorkQueue;
Context.PlatformAlloc = Win32Alloc;
Context.PlatformFree = Win32Free;
Context.PlatformRealloc = Win32Realloc;
Context.PlatformReadEntireFile = Win32ReadEntireFile;
Context.PlatformWriteEntireFile = Win32WriteEntireFile;
Context.PlatformGetFilePath = Win32SystemDialogueOpenFile;
Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle;
Context.PlatformGetSocketHandle = Win32GetSocketHandle;
Context.PlatformGetSendAddress = Win32GetSendAddress;
Context.PlatformSetSocketOption = Win32SetSocketOption;
Context.PlatformSendTo = Win32SendTo;
Context.PlatformCloseSocket = Win32CloseSocket;
@ -668,11 +634,7 @@ INT NCmdShow
HandleWindowMessage(Message, &MainWindow, &InputQueue, &Mouse);
}
// TODO(Peter): We shouldn't need to do this translation. the platform layer knows about win32_windows. We should just make that the interface
// to all windows.
// TODO(Peter): Decide which we want to use, context or renderbuffer. Should only be one
Context.WindowWidth = MainWindow.Width;
Context.WindowHeight = MainWindow.Height;
Context.WindowBounds = rect{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}};
RenderBuffer.ViewWidth = MainWindow.Width;
RenderBuffer.ViewHeight = MainWindow.Height;
Context.DeltaTime = LastFrameSecondsElapsed;

View File

@ -1,7 +1,7 @@
TODO FOLDHAUS
YOU WERE IN THE MIDDLE OF
Reimplement Node View
- probably want to take a fresh pass at nodes all together
Assembly -> SACN interface
x you need to rebuild the map from leds -> universes
@ -36,14 +36,13 @@ Switch To Nodes
- evaluation step (one node at a time)
Hardening
- turn the default sculpture view into an operation mode ? (have to think about this)
x turn the default sculpture view into an operation mode ? (have to think about this)
- Then we want to think about separating out mode render functions from mode update functions. Not sure its necessary but having something that operates like an update funciton but is called render is weird. Might want some sort of coroutine functionality in place, where modes can add and remove optional, parallel
update functions
- memory visualization
- - Log memory allocations
- x Log memory allocations
- separate rendering thread
- cache led positions. Only update if they are moving
- :HotCodeReloading
UI Improvements
- highlight node field under active edit
@ -89,16 +88,9 @@ Resource Management
- Icons
Animation
- timeline
- create clips that play
x timeline
x create clips that play
- clips can have parameters that drive them?
- clips should have prerequesites
- - channels active
- - patterns active in the channel
- - when a clip is playing, it should just take over the whole structure
Command Line
- select a channel/pattern
Optimization
- investigate the memory access pattern of the SACN / LED systems. Guessing they are very slow

View File

@ -44,3 +44,7 @@ x go back to calling SpecificationIndex Type
Hardening
x input context changes
Animation Timeline
x drag ends of animation clips to change start and end times
x click to drag time marker