commit
e3705842dc
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
BIN
data/splash.png
BIN
data/splash.png
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
s32 ModesMemorySize = Kilobytes(32);
|
||||
u8* ModesMemory = PushSize(State->Permanent, ModesMemorySize);
|
||||
InitMemoryArena(&State->Modes.Arena, ModesMemory, ModesMemorySize, 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;
|
||||
|
||||
{ // 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];
|
||||
input_command_registry ActiveCommands = {};
|
||||
if (State->Modes.ActiveModesCount > 0)
|
||||
{
|
||||
ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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))
|
||||
for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
#include "foldhaus_interface.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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -43,4 +43,154 @@ struct input_command_queue
|
|||
s32 Size;
|
||||
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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
v4 CameraStartPos;
|
||||
};
|
||||
// ----------------
|
||||
|
||||
OPERATION_RENDER_PROC(Update3DViewMouseRotate)
|
||||
#define PANEL_EDGE_CLICK_MAX_DISTANCE 6
|
||||
|
||||
internal b32
|
||||
HandleMouseDownPanelInteractionOrRecurse(panel* Panel, panel_edit_mode PanelEditMode, rect PanelBounds, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory;
|
||||
b32 HandledMouseInput = false;
|
||||
|
||||
v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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*
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
};
|
|
@ -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);
|
|
@ -0,0 +1 @@
|
|||
//
|
|
@ -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);
|
||||
}
|
|
@ -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
|
461
src/gs_memory.h
461
src/gs_memory.h
|
@ -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
|
|
@ -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)
|
||||
|
|
2206
src/gs_string.h
2206
src/gs_string.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
{
|
||||
|
|
162
src/interface.h
162
src/interface.h
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
20
todo.txt
20
todo.txt
|
@ -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
|
||||
|
|
|
@ -43,4 +43,8 @@ x go back to calling SpecificationIndex Type
|
|||
- x connections shouldn't store their own values, they should just point into permanent storage
|
||||
|
||||
Hardening
|
||||
x input context changes
|
||||
x input context changes
|
||||
|
||||
Animation Timeline
|
||||
x drag ends of animation clips to change start and end times
|
||||
x click to drag time marker
|
Loading…
Reference in New Issue