First Commit

This commit is contained in:
Peter Slattery 2019-07-19 13:56:21 -07:00
parent 56648be357
commit 134a0a1e20
42 changed files with 18864 additions and 0 deletions

206
assembly_parser.cpp Normal file
View File

@ -0,0 +1,206 @@
internal assembly_token
ParseToken (tokenizer* Tokenizer)
{
assembly_token Result = {};
Result.Token = Tokenizer->At;
Result.Length = 1;
Tokenizer->At++;
if (*Result.Token == ':'){ Result.Type = AssemblyToken_Colon; }
else if (*Result.Token == ';'){ Result.Type = AssemblyToken_SemiColon; }
else if (*Result.Token =='{'){ Result.Type = AssemblyToken_LeftCurlyBrace; }
else if (*Result.Token =='}'){ Result.Type = AssemblyToken_RightCurlyBrace; }
else if (*Result.Token ==','){ Result.Type = AssemblyToken_Comma; }
else if (IsNumericExtended(*Result.Token))
{
while(*Tokenizer->At && IsNumericExtended(*Tokenizer->At)) { Tokenizer->At++; }
Result.Type = AssemblyToken_Number;
Result.Length = Tokenizer->At - Result.Token;
}
else if (*Result.Token =='\"')
{
while(*Tokenizer->At && *Tokenizer->At != '\"') { Tokenizer->At++; }
Result.Token++; // Skip the quote
Result.Type = AssemblyToken_String;
Result.Length = (Tokenizer->At - Result.Token) - 1;
}
else if (*Result.Token == '(')
{
while(*Tokenizer->At && *Tokenizer->At != ')') { Tokenizer->At++; }
Result.Token++; // Skip the paren
Result.Type = AssemblyToken_Vector;
Result.Length = (Tokenizer->At - Result.Token) - 1;
}
else if (CharArraysEqualUpToLength(Result.Token, LED_STRIP_IDENTIFIER, CharArrayLength(LED_STRIP_IDENTIFIER)))
{
Result.Type = AssemblyToken_LEDStrip;
Result.Length = CharArrayLength(LED_STRIP_IDENTIFIER);
Tokenizer->At += Result.Length - 1;
}
else if (CharArraysEqualUpToLength(Result.Token, END_ASSEMBLY_FILE_IDENTIFIER, CharArrayLength(END_ASSEMBLY_FILE_IDENTIFIER)))
{
Result.Type = AssemblyToken_EndOfFile;
Result.Length = CharArrayLength(END_ASSEMBLY_FILE_IDENTIFIER);
Tokenizer->At += Result.Length - 1;
}
else
{
Result.Type = AssemblyToken_Identifier;
while(*Tokenizer->At && !IsWhitespace(*Tokenizer->At)) { Tokenizer->At++; }
}
return Result;
}
internal v3
ParseAssemblyVector (char* String)
{
v3 Result = {};
tokenizer Tokenizer = {};
Tokenizer.At = String;
EatWhitespace(&Tokenizer);
Result.x = ParseFloatUnsafe(Tokenizer.At);
EatPastCharacter(&Tokenizer, ',');
EatWhitespace(&Tokenizer);
Result.y = ParseFloatUnsafe(Tokenizer.At);
EatPastCharacter(&Tokenizer, ',');
EatWhitespace(&Tokenizer);
Result.z = ParseFloatUnsafe(Tokenizer.At);
EatPastCharacter(&Tokenizer, ',');
return Result;
}
internal void
ParseAssemblyFileHeader (tokenizer* Tokenizer, assembly_definition* Definition)
{
if (CharArraysEqualUpToLength(Tokenizer->At, LED_STRIP_COUNT_IDENTIFIER, CharArrayLength(LED_STRIP_COUNT_IDENTIFIER)))
{
Tokenizer->At += CharArrayLength(LED_STRIP_COUNT_IDENTIFIER);
EatWhitespace(Tokenizer);
assembly_token CountToken = ParseToken(Tokenizer);
if (CountToken.Type == AssemblyToken_Number)
{
Definition->LEDStripSize = ParseSignedIntUnsafe(CountToken.Token);
}
else
{
InvalidCodePath;
}
EatWhitespace(Tokenizer);
}
else
{
// TODO(Peter): Handle corrupted files, try to recover?
InvalidCodePath;
}
}
internal void
ParseLEDStrip (tokenizer* Tokenizer, assembly_definition* Assembly)
{
led_strip_definition* LEDStripDef = Assembly->LEDStrips + Assembly->LEDStripCount;
Assembly->LEDStripCount++;
// Control Box Index
while (*Tokenizer->At && !IsNumericExtended(*Tokenizer->At)) { Tokenizer->At++; }
assembly_token BoxIDToken = ParseToken(Tokenizer);
Assert(BoxIDToken.Type == AssemblyToken_Number);
LEDStripDef->ControlBoxID = ParseSignedIntUnsafe(BoxIDToken.Token);
// Start Universe
EatPastCharacter(Tokenizer, ',');
EatWhitespace(Tokenizer);
assembly_token StartUniverseToken = ParseToken(Tokenizer);
Assert(BoxIDToken.Type == AssemblyToken_Number);
LEDStripDef->StartUniverse = ParseSignedIntUnsafe(StartUniverseToken.Token);
// Start Channel
EatPastCharacter(Tokenizer, ',');
EatWhitespace(Tokenizer);
assembly_token StartChannelToken = ParseToken(Tokenizer);
Assert(BoxIDToken.Type == AssemblyToken_Number);
LEDStripDef->StartChannel = ParseSignedIntUnsafe(StartChannelToken.Token);
// Strip Type
// TODO(Peter): This is unused for now, and would be a branch point for parsing
// the rest of the info. Fix this.
EatPastCharacter(Tokenizer, ',');
EatWhitespace(Tokenizer);
if (CharArraysEqualUpToLength(Tokenizer->At, INTERPOLATE_POINTS_IDENTIFIER, CharArrayLength(INTERPOLATE_POINTS_IDENTIFIER)))
{
LEDStripDef->InterpolationType = StripInterpolate_Points;
// Start Position
EatPastCharacter(Tokenizer, ',');
EatWhitespace(Tokenizer);
assembly_token StartPositionToken = ParseToken(Tokenizer);
Assert(StartPositionToken.Type == AssemblyToken_Vector);
LEDStripDef->InterpolatePositionStart = ParseAssemblyVector(StartPositionToken.Token);
// End Position
EatPastCharacter(Tokenizer, ',');
EatWhitespace(Tokenizer);
assembly_token EndPositionToken = ParseToken(Tokenizer);
Assert(EndPositionToken.Type == AssemblyToken_Vector);
LEDStripDef->InterpolatePositionEnd = ParseAssemblyVector(EndPositionToken.Token);
// LEDs Per Strip
EatPastCharacter(Tokenizer, ',');
EatWhitespace(Tokenizer);
assembly_token LEDsPerStripToken = ParseToken(Tokenizer);
Assert(BoxIDToken.Type == AssemblyToken_Number);
LEDStripDef->LEDsPerStrip = ParseSignedIntUnsafe(LEDsPerStripToken.Token);
}
EatPastCharacter(Tokenizer, '}');
EatWhitespace(Tokenizer);
}
internal assembly_definition
ParseAssemblyFile (char* File, memory_arena* Storage)
{
assembly_definition Result = {};
tokenizer Tokenizer = {};
Tokenizer.At = File;
ParseAssemblyFileHeader(&Tokenizer, &Result);
Result.LEDStrips = PushArray(Storage, led_strip_definition,
Result.LEDStripSize);
EatWhitespace(&Tokenizer);
while(*Tokenizer.At)
{
EatWhitespace(&Tokenizer);
assembly_token Token = ParseToken(&Tokenizer);
if (Token.Type != AssemblyToken_EndOfFile)
{
switch (Token.Type)
{
case AssemblyToken_LEDStrip:
{
ParseLEDStrip(&Tokenizer, &Result);
} break;
default:
{
InvalidCodePath;
} break;
}
}
else
{
break;
}
}
return Result;
}

60
assembly_parser.h Normal file
View File

@ -0,0 +1,60 @@
#define LED_STRIP_COUNT_IDENTIFIER "led_strip_count"
#define LED_STRIP_IDENTIFIER "led_strip"
#define INTERPOLATE_POINTS_IDENTIFIER "INTERPOLATE_POINTS"
#define END_ASSEMBLY_FILE_IDENTIFIER "END_OF_ASSEMBLY_FILE"
enum assembly_token_type
{
AssemblyToken_Colon,
AssemblyToken_SemiColon,
AssemblyToken_LeftCurlyBrace,
AssemblyToken_RightCurlyBrace,
AssemblyToken_Comma,
AssemblyToken_Number,
AssemblyToken_String,
AssemblyToken_Vector,
AssemblyToken_LEDStrip,
AssemblyToken_Identifier,
AssemblyToken_EndOfFile
};
struct assembly_token
{
char* Token;
s32 Length;
assembly_token_type Type;
};
enum strip_interpolation_type
{
StripInterpolate_Boxes,
StripInterpolate_Points,
};
struct led_strip_definition
{
s32 ControlBoxID;
s32 StartUniverse, StartChannel;
strip_interpolation_type InterpolationType;
// Interpolate Boxes
s32 StartBoxIndex, EndBoxIndex;
// Interpolate Positions
v3 InterpolatePositionStart, InterpolatePositionEnd;
// Universal Interpolation
s32 LEDsPerStrip;
};
struct assembly_definition
{
s32 LEDStripSize;
s32 LEDStripCount;
led_strip_definition* LEDStrips;
};

884
foldhaus_app.cpp Normal file
View File

@ -0,0 +1,884 @@
#include "foldhaus_platform.h"
#include "foldhaus_app.h"
internal v4
MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 WindowHeight)
{
DEBUG_TRACK_SCOPE(MouseToWorldRay);
r32 X = ((2.0f * MouseX) / WindowWidth) - 1;
r32 Y = ((2.0f * MouseY) / WindowHeight) - 1;
v4 ScreenPos = v4{X, Y, -1, 1};
m44 InverseProjection = {};
Inverse(GetCameraPerspectiveProjectionMatrix(*Camera), &InverseProjection);
m44 InverseModelView = {};
Inverse(GetCameraModelViewMatrix(*Camera), &InverseModelView);
InverseModelView = Transpose(InverseModelView);
v4 ClipSpacePos = InverseProjection * ScreenPos;
v4 WorldPosition = InverseModelView * ClipSpacePos;
return WorldPosition;
}
internal void
PushLEDBufferOnList (led_buffer* List, led_buffer* Entry)
{
if (List->Next)
{
PushLEDBufferOnList(List->Next, Entry);
}
else
{
List->Next = Entry;
}
}
internal led_buffer*
RemoveLEDBufferFromList (led_buffer* List, led_buffer* Entry)
{
led_buffer* ListHead = 0;
if (List != Entry && List->Next)
{
ListHead = RemoveLEDBufferFromList(List->Next, Entry);
}
else if (List == Entry)
{
ListHead = Entry->Next;
}
else
{
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
InvalidCodePath;
}
return ListHead;
}
internal void
ConstructAssemblyFromDefinition (assembly_definition Definition,
string AssemblyName,
v3 RootPosition,
r32 Scale,
context Context,
app_state* State)
{
Assert(State->AssembliesUsed < ASSEMBLY_LIST_LENGTH);
assembly* Assembly = State->AssemblyList + State->AssembliesUsed++;
// 1. Find # of LEDs, # of Universes
s32 UniversesUsedByLEDs[2048]; // TODO(Peter): find the max universe number and size these accordingly
s32 ChannelsInUniverse[2048];
GSZeroMemory(UniversesUsedByLEDs, sizeof(s32) * 2048);
GSZeroMemory(ChannelsInUniverse, sizeof(s32) * 2048);
s32 UniverseCount = 0;
s32 LEDCount = 0;
for (s32 StripIdx = 0; StripIdx < Definition.LEDStripCount; StripIdx++)
{
led_strip_definition StripDef = Definition.LEDStrips[StripIdx];
s32 ChannelsPerStrip = StripDef.LEDsPerStrip * 3;
s32 UniversesPerStrip = IntegerDivideRoundUp(ChannelsPerStrip, 512);
s32 ChannelsAllocated = 0;
for (s32 UniverseIdx = 0; UniverseIdx < UniversesPerStrip; UniverseIdx++)
{
s32 UniverseID = StripDef.StartUniverse + UniverseIdx;
s32 UniverseIndex = -1;
for (s32 RegisteredUniverse = 0; RegisteredUniverse < UniverseCount; RegisteredUniverse++)
{
if (UniversesUsedByLEDs[RegisteredUniverse] == UniverseID)
{
UniverseIndex = RegisteredUniverse;
break;
}
}
if (UniverseIndex < 0)
{
UniverseIndex = UniverseCount++;
}
s32 ChannelsRequested = GSMin(STREAM_BODY_SIZE, ChannelsPerStrip - ChannelsAllocated);
ChannelsAllocated += ChannelsRequested;
ChannelsInUniverse[UniverseIndex] += ChannelsRequested;
Assert(ChannelsInUniverse[UniverseIndex] <= 512);
UniversesUsedByLEDs[UniverseIndex++] = UniverseID;
}
LEDCount += StripDef.LEDsPerStrip;
}
sacn_add_universes_result AddedUniverses = SACNAddUniverses(UniversesUsedByLEDs, UniverseCount, &State->SACN, Context);
Assembly->MemorySize = CalculateMemorySizeForAssembly(LEDCount, AssemblyName.Length);
memory_arena TemporaryAssemblyArena = AllocateNonGrowableArenaWithSpace(Context.PlatformAlloc, Assembly->MemorySize);
Assembly->MemoryBase = TemporaryAssemblyArena.CurrentRegion->Base;
Assembly->Universes = AddedUniverses.NewUniverseBuffer;
Assembly->SendBuffer = AddedUniverses.NewSendBuffer;
Assembly->Name = MakeString(PushArray(&TemporaryAssemblyArena, char, AssemblyName.Length), AssemblyName.Length);
CopyStringTo(AssemblyName, &Assembly->Name);
led_buffer* LEDBuffer = PushStruct(&TemporaryAssemblyArena, led_buffer);
LEDBuffer->Next = 0;
LEDBuffer->Count = 0;
LEDBuffer->Max = LEDCount;
LEDBuffer->LEDs = PushArray(&TemporaryAssemblyArena, led, LEDCount);
LEDBuffer->Colors = PushArray(&TemporaryAssemblyArena, sacn_pixel, LEDCount);
Assembly->LEDBuffer = LEDBuffer;
if (State->LEDBufferList)
{
PushLEDBufferOnList(State->LEDBufferList, LEDBuffer);
}
else
{
State->LEDBufferList = LEDBuffer;
}
State->TotalLEDsCount += LEDCount;
// Add LEDs
for (s32 StripIdx = 0; StripIdx < Definition.LEDStripCount; StripIdx++)
{
led_strip_definition StripDef = Definition.LEDStrips[StripIdx];
v3 WS_StripStart = {};
v3 WS_StripEnd = {};
s32 LEDsInStripCount = 0;
switch(StripDef.InterpolationType)
{
case StripInterpolate_Points:
{
WS_StripStart= StripDef.InterpolatePositionStart * Scale;
WS_StripEnd= StripDef.InterpolatePositionEnd * Scale;
LEDsInStripCount = StripDef.LEDsPerStrip;
}break;
default:
{
InvalidCodePath;
}break;
}
sacn_universe* CurrentUniverse = SACNGetUniverse(StripDef.StartUniverse, &State->SACN);
s32 ChannelsUsed = 0;
CurrentUniverse->BeginPixelCopyFromOffset = LEDBuffer->Count * sizeof(sacn_pixel);
r32 Percent = 0;
r32 PercentStep = 1 / (r32)LEDsInStripCount;
for (s32 Step = 0; Step < LEDsInStripCount; Step++)
{
v3 LEDPosition = Lerp(WS_StripStart, WS_StripEnd, Percent);
s32 LEDIndex = LEDBuffer->Count++;
Assert(LEDIndex < LEDCount);
led* LED = LEDBuffer->LEDs + LEDIndex;
sacn_pixel* LEDColor = LEDBuffer->Colors + LEDIndex;
LED->Position = LEDPosition;
LED->PositionMatrix = GetPositionM44(V4(LED->Position, 1));
LED->Index = LEDIndex;
Percent += PercentStep;
ChannelsUsed += 3;
if (ChannelsUsed > STREAM_BODY_SIZE)
{
ChannelsUsed -= STREAM_BODY_SIZE;
CurrentUniverse = SACNGetUniverse(CurrentUniverse->Universe + 1, &State->SACN);
CurrentUniverse->BeginPixelCopyFromOffset = (LEDBuffer->Count + sizeof(sacn_pixel)) - ChannelsUsed;
}
}
}
}
struct draw_leds_job_data
{
led* LEDs;
sacn_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 DrawCommandsCount = Data->OnePastLastIndex - Data->StartIndex;
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 < DrawCommandsCount;
LEDIdx++)
{
sacn_pixel SACNColor = Data->Colors[LED->Index];
v4 Color = v4{SACNColor.R / 255.f, SACNColor.G / 255.f, SACNColor.B / 255.f, 1.0f};
m44 ModelMatrix = Data->FaceCameraMatrix * LED->PositionMatrix;// * Data->FaceCameraMatrix;
v4 P0 = ModelMatrix * P0_In;
v4 P1 = ModelMatrix * P1_In;
v4 P2 = ModelMatrix * P2_In;
v4 P3 = ModelMatrix * P3_In;
PushTri3DOnBatch(Data->Batch, P0, P1, P2, UV0, UV1, UV2, Color);
PushTri3DOnBatch(Data->Batch, P0, P2, P3, UV0, UV2, UV3, Color);
LED++;
}
}
struct send_sacn_job_data
{
streaming_acn SACN;
sacn_universe_buffer UniverseList;
s32 StartUniverse;
s32 OnePastLastUniverse;
platform_send_to* PlatformSendTo;
};
internal void
SendSACNBufferData (s32 ThreadID, void* JobData)
{
DEBUG_TRACK_FUNCTION;
send_sacn_job_data* Data = (send_sacn_job_data*)JobData;
sacn_universe* SendUniverse = Data->UniverseList.Universes + Data->StartUniverse;
for (s32 UniverseIdx = Data->StartUniverse; UniverseIdx < Data->OnePastLastUniverse; UniverseIdx++)
{
SACNSendDataToUniverse(&Data->SACN, SendUniverse, Data->PlatformSendTo);
SendUniverse++;
}
}
internal void
LoadAssembly (app_state* State, context Context, char* Path)
{
platform_memory_result TestAssemblyFile = Context.PlatformReadEntireFile(Path);
if (TestAssemblyFile.Size <= 0)
{
InvalidCodePath;
}
assembly_definition AssemblyDefinition = ParseAssemblyFile((char*)TestAssemblyFile.Base, State->Transient);
Context.PlatformFree(TestAssemblyFile.Base, TestAssemblyFile.Size);
string PathString = MakeStringLiteral(Path);
s32 IndexOfLastSlash = FastLastIndexOfChar(PathString.Memory, PathString.Length, '\\');
string FileName = Substring(PathString, IndexOfLastSlash + 1);
r32 Scale = 100;
ConstructAssemblyFromDefinition(AssemblyDefinition, FileName, v3{0, 0, 0}, Scale, Context, State);
}
internal void
UnloadAssembly (s32 AssemblyIndex, app_state* State, context Context)
{
assembly Assembly = State->AssemblyList[AssemblyIndex];
SACNRemoveUniverseAndSendBuffer(&State->SACN, Assembly.Universes, Assembly.SendBuffer);
State->LEDBufferList = RemoveLEDBufferFromList(State->LEDBufferList, Assembly.LEDBuffer);
s32 LEDsInAssembly = Assembly.LEDBuffer->Count;
s32 MemoryRequiredForAssembly = CalculateMemorySizeForAssembly(LEDsInAssembly, Assembly.Name.Length);
Context.PlatformFree((u8*)Assembly.LEDBuffer, MemoryRequiredForAssembly);
State->TotalLEDsCount -= LEDsInAssembly;
if (AssemblyIndex != (State->AssembliesUsed - 1))
{
State->AssemblyList[AssemblyIndex] = State->AssemblyList[State->AssembliesUsed - 1];
}
State->AssembliesUsed -= 1;
}
////////////////////////////////////////////////////////////////////////
internal render_texture*
PushTexture (app_state* State)
{
render_texture* Result = 0;
if (State->LoadedTexturesUsed < State->LoadedTexturesSize)
{
Result = State->LoadedTextures + State->LoadedTexturesUsed++;
}
else
{
// TODO(Peter): Be able to grow this array
for (s32 i = 0; i < State->LoadedTexturesUsed; i++)
{
if (State->LoadedTextures[i].Handle == 0)
{
Result = State->LoadedTextures + i;
}
}
}
Assert(Result);
return Result;
}
internal render_texture*
StoreTexture (app_state* State, u8* Memory, s32 Width, s32 Height, s32 BytesPerPixel, s32 Stride)
{
render_texture* Result = PushTexture(State);
Result->Memory = Memory;
Result->Handle = 0;
Result->Width = Width;
Result->Height = Height;
Result->BytesPerPixel = BytesPerPixel;
Result->Stride = Stride;
}
internal void
RemoveTexture (app_state* State, s32 Index)
{
State->LoadedTextures[Index].Handle = 0;
// TODO(Peter): Free the memory it was using
}
internal void
RemoveTexture (app_state* State, render_texture* Texture)
{
Texture->Handle = 0;
// TODO(Peter): Free the memory it was using
}
RELOAD_STATIC_DATA(ReloadStaticData)
{
app_state* State = (app_state*)Context.MemoryBase;
GlobalDebugServices = DebugServices;
if (State->InputCommandRegistry.Size > 0)
{
RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_Delete, false, KeyCode_Invalid,
DeleteSelectedChannelOrPattern);
RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_MouseLeftButton, true, KeyCode_Invalid,
CameraMouseControl);
RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_U, false, KeyCode_Invalid, ToggleUniverseDebugView);
RegisterMouseWheelCommand(&State->InputCommandRegistry, CameraMouseZoom);
RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_A, false, KeyCode_Invalid, AddNode);
}
}
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);
State->LoadedTexturesSize = 8;
State->LoadedTextures = PushArray(State->Permanent, render_texture, State->LoadedTexturesSize);
State->LoadedTexturesUsed = 0;
InitializeInputCommandRegistry(&State->InputCommandRegistry, 32, State->Permanent);
// TODO(Peter): put in InitializeInterface?
r32 FontSize = 14;
{
platform_memory_result FontFile = Context.PlatformReadEntireFile("Anonymous Pro.ttf");
if (FontFile.Size)
{
stbtt_fontinfo StbFont;
if (stbtt_InitFont(&StbFont, FontFile.Base, stbtt_GetFontOffsetForIndex(FontFile.Base, 0)))
{
bitmap_font* Font = PushStruct(State->Permanent, bitmap_font);
Font->Atlas = PushTexture(State);
RenderStbttToBitmapFont(Font, StbFont, FontSize, 512, State->Permanent, State->Transient );
State->Interface.Font = Font;
State->Font = Font;
Font->Atlas->Handle = Context.PlatformGetGPUTextureHandle(Font->Atlas->Memory, Font->Atlas->Width, Font->Atlas->Height);
}
} else {}
}
State->Interface.FontSize = FontSize;
State->Interface.PanelBGColors[0] = v4{.3f, .3f, .3f, 1};
State->Interface.PanelBGColors[1] = v4{.4f, .4f, .4f, 1};
State->Interface.PanelBGColors[2] = v4{.5f, .5f, .5f, 1};
State->Interface.PanelBGColors[3] = v4{.6f, .6f, .6f, 1};
State->Interface.ButtonColor_Inactive = BlackV4;
State->Interface.ButtonColor_Active = v4{.1f, .1f, .1f, 1};
State->Interface.ButtonColor_Selected = v4{.1f, .1f, .3f, 1};
State->Interface.TextColor = WhiteV4;
State->Interface.Margin = v2{5, 5};
State->SACN = InitializeSACN(Context.PlatformAlloc, Context);
State->Camera.FieldOfView = DegreesToRadians(45.0f);
State->Camera.AspectRatio = (r32)Context.WindowWidth / (r32)Context.WindowHeight;
State->Camera.Near = 1.0f;
State->Camera.Far = 100.0f;
State->Camera.Position = v3{0, 0, -250};
State->Camera.LookAt = v3{0, 0, 0};
InitLEDPatternSystem(&State->PatternSystem, State->Permanent,
32, Megabytes(4));
InitLEDChannelSystem(&State->ChannelSystem, State->Permanent,
sizeof(led_channel) * 32);
#if 1
char Path[] = "radialumia.fold";
LoadAssembly(State, Context, Path);
#endif
State->InterfaceState.AddingPattern = false;
State->InterfaceState.PatternSelectorStart = 0;
State->InterfaceState.ChannelSelected = -1;
State->InterfaceYMax = 200;
State->PixelsToWorldScale = .01f;
State->Camera_StartDragPos = {};
State->UniverseOutputDisplayOffset = v2{0, 0};
State->UniverseOutputDisplayZoom = 1.0f;
GlobalDebugServices->Interface.RenderSculpture = true;
State->NodeList = AllocateNodeList(State->Permanent, Kilobytes(64));
State->NodeInteraction = NewNodeInteraction();
State->NodeRenderSettings.PortDim = v2{20, 15};
State->NodeRenderSettings.PortStep = State->NodeRenderSettings.PortDim.y + 10;
State->NodeRenderSettings.PortColors[MemberType_r32] = RedV4;
State->NodeRenderSettings.PortColors[MemberType_s32] = GreenV4;
State->NodeRenderSettings.PortColors[MemberType_v4] = BlueV4;
State->NodeRenderSettings.Font = State->Font;
State->OutputNode = PushOutputNodeOnList(State->NodeList, v2{500, 250}, State->Transient);
ReloadStaticData(Context, GlobalDebugServices);
}
UPDATE_AND_RENDER(UpdateAndRender)
{
app_state* State = (app_state*)Context.MemoryBase;
ExecuteAllRegisteredCommands(&State->InputCommandRegistry, Input, State);
UpdateOutputNodeCalculations(State->OutputNode, State->NodeList, State->Transient,
State->LEDBufferList->LEDs,
State->LEDBufferList->Colors,
State->LEDBufferList->Count);
/*
patterns_update_list Temp_PatternsNeedUpdate = UpdateAllChannels(&State->ChannelSystem,
Context.DeltaTime,
State->Transient);
UpdateAllPatterns(&Temp_PatternsNeedUpdate, &State->PatternSystem, State->LEDBufferList, Context.DeltaTime, State->Transient);
*/
{
// NOTE(Peter): We know that these two lists should be maintained together. Each element in the list is one sculpture's worth of
// information, and should always be evaluated in pairs.
sacn_universe_buffer* UniverseList = State->SACN.UniverseBuffer;
led_buffer* LEDBuffer = State->LEDBufferList;
while (UniverseList && LEDBuffer)
{
for (s32 U = 0; U < UniverseList->Used; U++)
{
sacn_universe* UniverseOne = UniverseList->Universes + U;
Assert(UniverseOne->BeginPixelCopyFromOffset >= 0);
u8* LEDColorBuffer = (u8*)LEDBuffer->Colors + UniverseOne->BeginPixelCopyFromOffset;
u8* SACNSendBuffer = UniverseOne->StartPositionInSendBuffer + STREAM_HEADER_SIZE;
GSMemCopy(LEDColorBuffer, SACNSendBuffer, STREAM_BODY_SIZE);
}
UniverseList = UniverseList->Next;
LEDBuffer = LEDBuffer->Next;
}
Assert(!LEDBuffer && !UniverseList);
}
DEBUG_IF(GlobalDebugServices->Interface.SendSACNData)
{
if (++State->SACN.SequenceIterator == 0) // Never use 0 after the first one
{
++State->SACN.SequenceIterator;
}
sacn_universe_buffer* UniverseList = State->SACN.UniverseBuffer;
while (UniverseList)
{
s32 JobCount = 2;
s32 UniversesPerJob = UniverseList->Used / JobCount;
send_sacn_job_data* SACNData = PushArray(State->Transient, send_sacn_job_data, JobCount);
for (s32 i = 0; i < JobCount; i++)
{
SACNData[i].SACN = State->SACN;
SACNData[i].UniverseList = *UniverseList;
SACNData[i].StartUniverse = i * UniversesPerJob;
SACNData[i].OnePastLastUniverse = (i * UniversesPerJob) + UniversesPerJob;
if (SACNData[i].OnePastLastUniverse > UniverseList->Used)
{
SACNData[i].OnePastLastUniverse = UniverseList->Used;
}
Context.GeneralWorkQueue->PushWorkOnQueue(
Context.GeneralWorkQueue,
SendSACNBufferData,
SACNData + i);
}
UniverseList = UniverseList->Next;
}
}
////////////////////////////////
// Render Assembly
///////////////////////////////
if (Context.WindowIsVisible)
{
State->Camera.AspectRatio = (r32)Context.WindowWidth / (r32)Context.WindowHeight;
m44 ModelViewMatrix = GetCameraModelViewMatrix(State->Camera);
m44 ProjectionMatrix = GetCameraPerspectiveProjectionMatrix(State->Camera);
RenderBuffer->ViewWidth = Context.WindowWidth;
RenderBuffer->ViewHeight = Context.WindowHeight;
r32 LEDHalfWidth = .5f;
PushRenderPerspective(RenderBuffer, Context.WindowWidth, Context.WindowHeight, State->Camera);
PushRenderClearScreen(RenderBuffer);
DEBUG_IF(GlobalDebugServices->Interface.RenderSculpture) // DebugServices RenderSculpture Toggle
{
s32 JobsNeeded = IntegerDivideRoundUp(State->TotalLEDsCount, LED_BUFFER_SIZE);
draw_leds_job_data* JobDataBank = PushArray(State->Transient, draw_leds_job_data, JobsNeeded);
s32 JobDataBankUsed = 0;
// 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;
render_quad_batch_constructor BatchConstructor = PushRenderQuad3DBatch(RenderBuffer, State->TotalLEDsCount);
led_buffer* LEDBuffer = State->LEDBufferList;
s32 LEDBufferLEDsAssignedToJobs = 0;
for (s32 Job = 0; Job < JobsNeeded; Job++)
{
draw_leds_job_data* JobData = JobDataBank + JobDataBankUsed++;
JobData->LEDs = LEDBuffer->LEDs;
JobData->Colors = LEDBuffer->Colors;
JobData->StartIndex = LEDBufferLEDsAssignedToJobs;
JobData->OnePastLastIndex = GSMin(JobData->StartIndex + LED_BUFFER_SIZE, LEDBuffer->Count);
LEDBufferLEDsAssignedToJobs += JobData->OnePastLastIndex - JobData->StartIndex;
// New
JobData->Batch = &BatchConstructor;
JobData->FaceCameraMatrix = FaceCameraMatrix;
JobData->ModelViewMatrix = ModelViewMatrix;
JobData->LEDHalfWidth = LEDHalfWidth;
Context.GeneralWorkQueue->PushWorkOnQueue(
Context.GeneralWorkQueue,
DrawLEDsInBufferRangeJob,
JobData);
Assert(LEDBufferLEDsAssignedToJobs <= LEDBuffer->Count); // We should never go OVER the number of leds in the buffer
if (LEDBufferLEDsAssignedToJobs == LEDBuffer->Count)
{
LEDBuffer = LEDBuffer->Next;
LEDBufferLEDsAssignedToJobs = 0;
}
}
Context.GeneralWorkQueue->DoQueueWorkUntilDone(Context.GeneralWorkQueue, 0);
Context.GeneralWorkQueue->ResetWorkQueue(Context.GeneralWorkQueue);
}
///////////////////////////////////////
// Interface
//////////////////////////////////////
DEBUG_TRACK_SCOPE(DrawInterface);
PushRenderOrthographic(RenderBuffer, Context.WindowWidth, Context.WindowHeight);
// Universe Data View
if (State->DrawUniverseOutputDisplay)
{
DEBUG_TRACK_SCOPE(DrawUniverseOutputDisplay);
string TitleBarString = InitializeString(PushArray(State->Transient, char, 64), 64);
v2 DisplayArea_Dimension = v2{600, 600};
v2 DisplayContents_Offset = State->UniverseOutputDisplayOffset;
v2 DisplayArea_TopLeft = v2{300, Context.WindowHeight - 50} + DisplayContents_Offset;
v2 UniverseDisplayDimension = v2{100, 100} * State->UniverseOutputDisplayZoom;
v2 Padding = v2{25, 50} * State->UniverseOutputDisplayZoom;
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 (State->UniverseOutputDisplayZoom > .5f)
{
v2 TitleDisplayStart = UniverseDisplayTopLeft + v2{0, 12};
PrintF(&TitleBarString, "Universe %d", Universe->Universe);
DrawString(RenderBuffer, TitleBarString, State->Interface.Font, 12,
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;
}
}
///////////////////////////////////////
// Menu Bar
//////////////////////////////////////
r32 TopBarHeight = 40;
{
panel_result TopBarPanel = EvaluatePanel(RenderBuffer,
v2{0, Context.WindowHeight - TopBarHeight},
v2{Context.WindowWidth, Context.WindowHeight},
0, State->Interface, Input);
v2 ButtonDim = v2{200, State->Interface.Font->NewLineYOffset + 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, Input);
string InterfaceString = MakeString(PushArray(State->Transient, char, 256), 256);
for (int i = 0; i < State->AssembliesUsed; 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, Input);
if (UnloadAssemblyBtn.Pressed)
{
UnloadAssembly(i, State, Context);
}
}
if (LoadAssemblyBtn.Pressed)
{
char FilePath[256];
b32 Success = Context.PlatformGetFilePath(FilePath, 256);
if (Success)
{
LoadAssembly(State, Context, FilePath);
}
}
}
///////////////////////////////////////
// Figuring Out Nodes
//////////////////////////////////////
{
v2 MousePos = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY};
v2 LastFrameMousePos = v2{(r32)Input.Old->MouseX, (r32)Input.Old->MouseY};
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton))
{
node_offset Node = GetNodeUnderPoint(State->NodeList, MousePos, State->NodeRenderSettings);
if (Node.Node)
{
State->NodeInteraction = GetNodeInteractionType(Node.Node, Node.Offset, MousePos, State->NodeRenderSettings);
}
}
else if (KeyTransitionedUp(Input, KeyCode_MouseLeftButton))
{
if (IsDraggingNodePort(State->NodeInteraction))
{
TryConnectNodes(State->NodeInteraction, MousePos, State->NodeList, State->NodeRenderSettings);
}
State->NodeInteraction = NewNodeInteraction();
}
UpdateDraggingNode(MousePos, State->NodeInteraction, State->NodeList,
State->NodeRenderSettings);
UpdateDraggingNodePort(MousePos, State->NodeInteraction, State->NodeList,
State->NodeRenderSettings, RenderBuffer);
UpdateDraggingNodeValue(MousePos, LastFrameMousePos, State->NodeInteraction, State->NodeList, State->NodeRenderSettings, State);
RenderNodeList(State->NodeList, State->NodeRenderSettings, RenderBuffer);
ResetNodesUpdateState(State->NodeList);
if (State->InterfaceShowNodeList)
{
v2 TopLeft = State->NodeListMenuPosition;
// Title Bar
PushRenderQuad2D(RenderBuffer, v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y},
v4{.3f, .3f, .3f, 1.f});
DrawString(RenderBuffer, MakeStringLiteral("Nodes List"), State->Font, 14,
v2{TopLeft.x, TopLeft.y - 25}, WhiteV4);
TopLeft.y -= 30;
for (s32 i = 0; i < NodeSpecificationsCount; i++)
{
node_specification Spec = NodeSpecifications[i];
button_result Button = EvaluateButton(RenderBuffer, v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y},
MakeStringLiteral(Spec.Name), State->Interface, Input);
if (Button.Pressed)
{
PushNodeOnListFromSpecification(State->NodeList, Spec, MousePos, State->Permanent);
}
TopLeft.y -= 30;
}
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton) ||
KeyTransitionedDown(Input, KeyCode_Esc))
{
State->InterfaceShowNodeList = false;
}
}
}
if (State->ColorPickerEditValue != 0)
{
b32 ShouldClose = EvaluateColorPicker(RenderBuffer, State->ColorPickerEditValue,
v2{200, 200}, State->Interface, Input);
if (ShouldClose ||
KeyTransitionedDown(Input, KeyCode_Esc))
{
State->ColorPickerEditValue = 0;
}
}
#if 0
///////////////////////////////////////
// Current Patterns Panel
//////////////////////////////////////
r32 LeftPanelRightEdge = DrawLeftHandInterface(State, Input, Context.WindowHeight - TopBarHeight, RenderBuffer);
if (State->InterfaceState.ChannelSelected >= 0)
{
led_channel* ActiveChannel = GetChannelByIndex(State->InterfaceState.ChannelSelected,
State->ChannelSystem);
button_result OperationButtonState = EvaluateButton(
RenderBuffer,
v2{Context.WindowWidth - 150, 500},
v2{Context.WindowWidth - 50, 550},
MakeStringLiteral(PatternSelectorOperationsText[ActiveChannel->BlendMode]),
State->Interface,
Input);
if (OperationButtonState.Pressed)
{
State->InterfaceState.ChooseOperationPanelOpen = !State->InterfaceState.ChooseOperationPanelOpen;
}
if (State->InterfaceState.ChooseOperationPanelOpen)
{
s32 StringLength = 128;
s32 OperationsStart = PatternSelectorCombine_Invalid + 1;
s32 OperationsOnePastLast = PatternSelectorCombine_Count;
s32 OperationsCount = (OperationsOnePastLast - OperationsStart);
string* OperationChoices = PushArray(State->Transient, string, OperationsCount);
for (s32 Choice = OperationsStart;
Choice < OperationsOnePastLast;
Choice++)
{
s32 Index = Choice - OperationsStart;
PushString(&OperationChoices[Index], State->Transient, StringLength);
CopyCharArrayToString(PatternSelectorOperationsText[Choice],
&OperationChoices[Index]);
}
v2 Min = v2{Context.WindowWidth - 250, 250};
v2 Max = v2{Context.WindowWidth - 50, 500};
scroll_list_result OperationChoice = DrawSelectableOptionsList(RenderBuffer, Min, Max, OperationChoices, OperationsCount,
0, ActiveChannel->BlendMode - 1,
State->Interface, Input);
if (OperationChoice.IndexSelected + 1 > (int)ChannelBlend_Invalid &&
OperationChoice.IndexSelected + 1 < (int)ChannelBlend_Count)
{
ActiveChannel->BlendMode = (channel_blend_mode)(OperationChoice.IndexSelected + 1);
}
}
}
#endif
DrawDebugInterface(RenderBuffer, 25,
State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight,
Context.DeltaTime, State->Camera, Input, State->Transient);
}
ClearArena(State->Transient);
EndDebugFrame(GlobalDebugServices);
}
CLEANUP_APPLICATION(CleanupApplication)
{
app_state* State = (app_state*)Context.MemoryBase;
SACNCleanup(&State->SACN, Context);
}

126
foldhaus_app.h Normal file
View File

@ -0,0 +1,126 @@
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb/stb_truetype.h"
#include "../meta/gs_meta_lexer.h"
#include "gs_font.h"
#include "interface.h"
#include "foldhaus_network_ordering.h"
#include "foldhaus_sacn.h"
#define LED_BUFFER_SIZE 256
struct led
{
s32 Index;
v3 Position;
m44 PositionMatrix;
};
struct led_buffer
{
sacn_pixel* Colors;
led* LEDs;
s32 Count;
s32 Max;
led_buffer* Next;
};
#define CalculateMemorySizeForAssembly(leds, name_length) ((sizeof(led) + sizeof(sacn_pixel)) * (leds)) + sizeof(led_buffer) + name_length;
struct assembly
{
s32 MemorySize;
u8* MemoryBase;
string Name;
string FilePath;
led_buffer* LEDBuffer;
// Memory managed by the SACN system
sacn_universe_buffer* Universes;
sacn_send_buffer* SendBuffer;
};
#include "assembly_parser.h"
//#include "foldhaus_environment.h"
// TODO(Peter): remove this, and get pattern_system.h outta here!!
typedef struct led_pattern led_pattern;
#include "foldhaus_node.h"
#include "foldhaus_patterns.h"
#include "foldhaus_channel.h"
#include "foldhaus_patterns.cpp"
#include "foldhaus_channel.cpp"
#include "assembly_parser.cpp"
#include "test_patterns.h"
#include "patterns_registry.h"
#include "foldhaus_interface.h"
typedef struct app_state app_state;
#include "foldhaus_debug_visuals.h"
#include "foldhaus_command_dispatch.h"
#include "generated/foldhaus_nodes_generated.cpp"
struct app_state
{
memory_arena* Permanent;
memory_arena* Transient;
memory_arena SACNMemory;
render_texture* LoadedTextures;
s32 LoadedTexturesSize;
s32 LoadedTexturesUsed;
camera Camera;
input_command_registry InputCommandRegistry;
streaming_acn SACN;
s32 TotalLEDsCount;
led_buffer* LEDBufferList;
// 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 AssembliesUsed;
//environment Environment;
led_channel_system ChannelSystem;
led_pattern_system PatternSystem;
bitmap_font* Font;
interface_state InterfaceState;
interface_config Interface;
r32 InterfaceYMax;
r32 PixelsToWorldScale;
v4 Camera_StartDragPos;
b32 DrawUniverseOutputDisplay;
v2 UniverseOutputDisplayOffset;
r32 UniverseOutputDisplayZoom;
b32 InterfaceShowNodeList;
v2 NodeListMenuPosition;
node_list* NodeList;
node_interaction NodeInteraction;
node_render_settings NodeRenderSettings;
interface_node* OutputNode;
v4* ColorPickerEditValue;
};
#include "foldhaus_sacn_view.cpp"
#include "foldhaus_command_dispatch.cpp"
#include "foldhaus_node.cpp"
#include "foldhaus_interface.cpp"

261
foldhaus_channel.cpp Normal file
View File

@ -0,0 +1,261 @@
inline void
SetTransition(pattern_transition* Transition, r32 Duration, r32 Elapsed)
{
Transition->Duration = Duration;
Transition->TimeElapsed = Elapsed;
}
internal void
InitLEDChannelSystem (led_channel_system* System, memory_arena* ParentStorage, s32 StorageSize)
{
System->Channels = 0;
System->ChannelCount = 0;
InitMemoryArena(&System->Storage, PushSize(ParentStorage, StorageSize), StorageSize, 0);
System->FreeList = 0;
}
inline void
ResetChannel (led_channel* Channel, led_channel_system* ChannelSystem)
{
if (Channel->Patterns == 0)
{
Channel->Patterns = PushArray(&ChannelSystem->Storage,
pattern_index_id_key,
CHANNEL_MAX_PATTERNS);
}
Channel->ActivePatterns = 0;
Channel->ActivePatternIndex = -1;
SetTransition(&Channel->Transition, 3, 0);
Channel->BlendMode = ChannelBlend_Override;
Channel->ChannelID = ChannelIDAccumulator++;
Channel->Next = 0;
}
internal led_channel*
AddLEDChannel (led_channel_system* ChannelSystem)
{
led_channel* Channel = 0;
if (ChannelSystem->FreeList)
{
Channel = ChannelSystem->FreeList;
ChannelSystem->FreeList = ChannelSystem->FreeList->Next;
}
else
{
Channel = PushStruct(&ChannelSystem->Storage, led_channel);
}
ResetChannel(Channel, ChannelSystem);
Channel->Next = ChannelSystem->Channels;
ChannelSystem->Channels = Channel;
ChannelSystem->ChannelCount++;
return Channel;
}
internal b32
RemoveLEDChannel_ (led_channel* PrevChannel, led_channel* ToRemove, led_channel_system* ChannelSystem)
{
b32 Result = true;
if (PrevChannel->Next == ToRemove)
{
PrevChannel->Next = ToRemove->Next;
ToRemove->Next = ChannelSystem->FreeList;
ChannelSystem->FreeList = ToRemove;
ChannelSystem->ChannelCount--;
Result = true;
}
else
{
Result = false;
}
return Result;
}
internal b32
RemoveLEDChannel (led_channel* Channel, led_channel_system* ChannelSystem)
{
b32 Result = true;
led_channel* Cursor = ChannelSystem->Channels;
for (s32 i = 0;
(Cursor->Next != Channel && i < ChannelSystem->ChannelCount);
Cursor = Cursor->Next, i++){}
Result = RemoveLEDChannel_(Cursor, Channel, ChannelSystem);
return Result;
}
internal b32
RemoveLEDChannel (s32 Index, led_channel_system* ChannelSystem)
{
Assert(Index < ChannelSystem->ChannelCount);
b32 Result = true;
if (Index == 0)
{
led_channel* FirstChannel = ChannelSystem->Channels;
ChannelSystem->Channels = FirstChannel->Next;
FirstChannel->Next = ChannelSystem->FreeList;
ChannelSystem->FreeList = FirstChannel;
ChannelSystem->ChannelCount--;
Result = true;
}
else
{
led_channel* PrevChannel = ChannelSystem->Channels;
for (s32 i = 0; i < Index - 1; i++)
{
PrevChannel = PrevChannel->Next;
}
Result = RemoveLEDChannel_(PrevChannel, PrevChannel->Next, ChannelSystem);
}
return Result;
}
internal led_channel*
GetChannelByIndex (s32 Index, led_channel_system ChannelSystem)
{
Assert(Index < ChannelSystem.ChannelCount);
led_channel* Result = ChannelSystem.Channels;
for (s32 i = 0; i < Index; i++)
{
Result = Result->Next;
}
return Result;
}
internal void
AddPatternKeyToChannel (pattern_index_id_key Key, led_channel* Channel)
{
Assert(Channel->ActivePatterns < CHANNEL_MAX_PATTERNS);
Channel->Patterns[Channel->ActivePatterns++] = Key;
}
internal b32
RemovePatternKeyFromChannel (pattern_index_id_key Key, led_channel* Channel)
{
b32 Result = false;
s32 RemoveFrom = -1;
for (s32 i = 0; i < Channel->ActivePatterns; i++)
{
if (Channel->Patterns[i].ID == Key.ID)
{
RemoveFrom = i;
}
}
if (RemoveFrom >= 0)
{
for (s32 j = 0; j < Channel->ActivePatterns; j++)
{
Channel->Patterns[j] = Channel->Patterns[j + 1];
}
Channel->ActivePatterns--;
Result = true;
}
return Result;
}
internal void
PushPatternKeyOnUpdateList (pattern_index_id_key Key,
pattern_push_color_proc* PushColorProc,
patterns_update_list* UpdateList,
memory_arena* Storage)
{
if (UpdateList->Used >= UpdateList->Size)
{
if (!UpdateList->Next)
{
UpdateList->Next = PushStruct(Storage, patterns_update_list);
UpdateList->Next->Size = UpdateList->Size;
UpdateList->Next->Used = 0;
UpdateList->Next->Patterns = PushArray(Storage, pattern_update_list_entry, UpdateList->Next->Size);
}
PushPatternKeyOnUpdateList(Key, PushColorProc, UpdateList->Next, Storage);
}
else
{
pattern_update_list_entry Entry = {};
Entry.Key = Key;
Entry.PushColorProc = PushColorProc;
UpdateList->Patterns[UpdateList->Used++] = Entry;
}
}
internal void
UpdateChannel (led_channel* Channel,
patterns_update_list* PatternsNeedUpdateList,
r32 DeltaTime,
memory_arena* Storage)
{
// Update Transition
Channel->Transition.TimeElapsed += DeltaTime;
if (Channel->Transition.TimeElapsed >= Channel->Transition.Duration ||
(Channel->ActivePatternIndex < 0 && Channel->ActivePatterns > 0))
{
Channel->Transition.TimeElapsed -= Channel->Transition.Duration;
Channel->ActivePatternIndex++;
if (Channel->ActivePatternIndex >= Channel->ActivePatterns)
{
Channel->ActivePatternIndex = 0;
}
}
// Create Active Pattern List
if (Channel->ActivePatterns > 0)
{
Assert(Channel->ActivePatternIndex >= 0 && Channel->ActivePatternIndex < Channel->ActivePatterns);
pattern_push_color_proc* PushColorProc = 0;
switch (Channel->BlendMode)
{
case ChannelBlend_Override: { PushColorProc = PushColor_Override; } break;
case ChannelBlend_Add: { PushColorProc = PushColor_Add; } break;
case ChannelBlend_Multiply: { PushColorProc = PushColor_Multiply; } break;
default:
{
InvalidCodePath;
}break;
}
PushPatternKeyOnUpdateList(Channel->Patterns[Channel->ActivePatternIndex],
PushColorProc,
PatternsNeedUpdateList, Storage);
}
}
internal patterns_update_list
UpdateAllChannels (led_channel_system* ChannelSystem, r32 DeltaTime, memory_arena* Transient)
{
patterns_update_list Result = {};
// NOTE(Peter): The initial size of this array is ChannelCount * 2 b/c at the moment, in the worst case,
// we need to update the two patterns a channel is blending between, and there isn't a case where we'd
// update 3 per channel
Result.Size = ChannelSystem->ChannelCount * 2;
Result.Used = 0;
Result.Patterns = PushArray(Transient, pattern_update_list_entry, Result.Size);
Result.Next = 0;
for (led_channel* Channel = ChannelSystem->Channels;
Channel;
Channel = Channel->Next)
{
UpdateChannel(Channel, &Result, DeltaTime, Transient);
}
return Result;
}

98
foldhaus_channel.h Normal file
View File

@ -0,0 +1,98 @@
#if 0 // USAGE CODE
{
channel* FirstChannel = AddChannel(State, State->Channels);
channel* ActiveChannel = GetChannelAtIndex(State->Channels, ActiveIndex);
AddPattern(State, ActiveChannel, PatternSpec);
patterns_need_update UpdatePatternsList = UdpateAllChannels(ChannelSystem, State->Transient);
for (s32 i = 0; i < UpdatePatternsList.Count; i++)
{
UpdateActivePatterns(State, Patterns, UpdatePatternsList.PatternIDs[i]);
}
pattern* Pattern = ...;
DeletePattern(State, Pattern);
RemoveChannel(FirstChannel, State->Channels);
RemoveChannel(2, State->Channels);
}
#endif
struct pattern_transition
{
r32 Duration;
r32 TimeElapsed;
};
enum channel_blend_mode
{
ChannelBlend_Invalid,
ChannelBlend_Override,
ChannelBlend_Add,
ChannelBlend_Multiply,
ChannelBlend_Count,
};
char* METAChannelBlendModeNames[] = {
"Invalid", //ChannelBlend_Invalid
"Override", //ChannelBlend_Override
"Add", // ChannelBlend_Add
"Multiply", //ChannelBlend_Multiply
"Count", //ChannelBlend_Count
};
global_variable s32 ChannelIDAccumulator;
// TODO(Peter): This number is gonna have to get bigger
#define CHANNEL_MAX_PATTERNS 8
struct led_channel
{
s32 ChannelID;
pattern_index_id_key* Patterns;
// TODO(Peter): Rename this once we get patterns in their own system. All patterns in this
// list are active. ATM this is just here for legacy reasons.
s32 ActivePatterns;
// TODO(Peter): and this should probably be CurrentPatternIndex, or HotPatternIndex
s32 ActivePatternIndex;
// TODO(Peter): extend this to be able to have different kinds of transitions
// including, most importantly, being able to fade between patterns
pattern_transition Transition;
channel_blend_mode BlendMode;
led_channel* Next;
};
struct led_channel_system
{
led_channel* Channels;
s32 ChannelCount;
// TODO(Peter): think about ways this can give back to the main pool
// Like, if we just stay within initial storage, no problem. But if we grow really big,
// then shrink really small, we should probably give some of it back to the main app.
// maybe by keeping track of FreeList size...
// TODO(Peter): Also need to think about how memory_arenas get bigger...
memory_arena Storage;
led_channel* FreeList;
};
struct pattern_update_list_entry
{
pattern_index_id_key Key;
pattern_push_color_proc* PushColorProc;
};
struct patterns_update_list
{
pattern_update_list_entry* Patterns;
s32 Size;
s32 Used;
patterns_update_list* Next;
};

View File

@ -0,0 +1,89 @@
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 input_command*
FindExistingCommand (input_command_registry* CommandRegistry, key_code Key, key_code Mdfr)
{
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)
{
Result = Command;
break;
}
}
return Result;
}
internal void
RegisterKeyPressCommand (input_command_registry* CommandRegistry,
key_code Key,
b32 Held,
key_code Mdfr,
input_command_proc* Proc)
{
input_command* Command = FindExistingCommand(CommandRegistry, Key, Mdfr);
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->Held = Held;
Command->Mdfr = Mdfr;
Command->Proc = Proc;
}
internal void
RegisterMouseWheelCommand (input_command_registry* CommandRegistry,
input_command_proc* Proc)
{
CommandRegistry->MouseWheelCommand = Proc;
}
internal void
ExecuteAllRegisteredCommands (input_command_registry* CommandRegistry,
input Input,
app_state* State)
{
if (Input.New->MouseScroll != 0)
{
CommandRegistry->MouseWheelCommand(State, Input);
}
for (s32 i = 0; i < CommandRegistry->Used; i++)
{
input_command Command = CommandRegistry->Commands[i];
if (Command.Held)
{
if (KeyDown(Input, Command.Key) &&
(Command.Mdfr == KeyCode_Invalid || KeyDown(Input, Command.Mdfr)))
{
Command.Proc(State, Input);
}
}
else
{
if (KeyTransitionedDown(Input, Command.Key) &&
(Command.Mdfr == KeyCode_Invalid || KeyDown(Input, Command.Mdfr)))
{
Command.Proc(State, Input);
}
}
}
}

View File

@ -0,0 +1,30 @@
#if USAGE_CODE
RegisterInputCommand(KeyCode_UpArrow, SACNView_DrawNextUniverse, Context);
RegisterInputCommand(KeyCode_MouseLeftButton, PanCamera, Context);
ExecuteRegisteredCommands(Context, Input);
#endif // USAGE_CODE
#define FOLDHAUS_INPUT_COMMAND_PROC(name) void name(app_state* State, input Input)
typedef FOLDHAUS_INPUT_COMMAND_PROC(input_command_proc);
// TODO(Peter): At the moment these are all key press commands. Need a way to differentiate between
// press and hold. Probably add a second array to input_command_Registry
struct input_command
{
key_code Key;
b32 Held;
key_code Mdfr;
input_command_proc* Proc;
};
struct input_command_registry
{
input_command* Commands;
s32 Size;
s32 Used;
input_command_proc* MouseWheelCommand;
};

244
foldhaus_debug.h Normal file
View File

@ -0,0 +1,244 @@
#define SCOPE_NAME_LENGTH 256
struct scope_time_record
{
char ScopeName_[SCOPE_NAME_LENGTH];
string ScopeName;
u32 Duration_Cycles;
};
struct debug_interface
{
b32 ShowCameraMouse;
b32 ShowTrackedScopes;
b32 RenderSculpture;
b32 SendSACNData;
};
typedef s64 debug_timing_proc();
#define HISTOGRAM_DEPTH 10
struct debug_histogram_entry
{
char ScopeName_[SCOPE_NAME_LENGTH];
string ScopeName;
u32 PerFrame_Cycles[HISTOGRAM_DEPTH];
u32 PerFrame_CallCount[HISTOGRAM_DEPTH];
s32 CurrentFrame;
// NOTE(Peter): Cached Values, recalculated ever frame
u32 Average_Cycles;
u32 Average_CallCount;
u32 Total_Cycles;
u32 Total_CallCount;
};
#define SCOPE_HISTOGRAM_SIZE 512
struct debug_services
{
s32 TrackedScopesCount;
s32 TrackedScopesMax;
scope_time_record* TrackedScopes;
s64 PerformanceCountFrequency;
memory_arena DebugStorage;
debug_interface Interface;
debug_timing_proc* GetWallClock;
debug_histogram_entry ScopeHistogram[SCOPE_HISTOGRAM_SIZE];
s32 ScopeHistogramUsed;
};
internal void
InitDebugServices (debug_services* Services, u8* Memory, s32 MemorySize, s32 TrackedScopesMax, s64 PerformanceCountFrequency)
{
InitMemoryArena(&Services->DebugStorage, Memory, MemorySize, 0);
Services->TrackedScopesCount = 0;
Services->TrackedScopesMax = TrackedScopesMax;
Services->TrackedScopes = PushArray(&Services->DebugStorage, scope_time_record, TrackedScopesMax);
Services->Interface.RenderSculpture = true;
Services->PerformanceCountFrequency = PerformanceCountFrequency;
Services->Interface.ShowCameraMouse = false;
Services->Interface.ShowTrackedScopes = false;
Services->Interface.RenderSculpture = true;
Services->Interface.SendSACNData = false;
Services->ScopeHistogramUsed = 0;
}
internal s32
DEBUGFindScopeHistogram (debug_services* Services, string Name)
{
s32 Result = -1;
for (s32 i = 0; i < SCOPE_HISTOGRAM_SIZE; i++)
{
if (StringsEqual(Services->ScopeHistogram[i].ScopeName, Name))
{
Result = i;
break;
}
}
return Result;
}
internal s32
DEBUGAddScopeHistogram (debug_services* Services, scope_time_record Record)
{
Assert(Services->ScopeHistogramUsed < SCOPE_HISTOGRAM_SIZE);
s32 Result = Services->ScopeHistogramUsed++;
debug_histogram_entry* Entry = Services->ScopeHistogram + Result;
Entry->ScopeName = MakeString(Entry->ScopeName_, 256);
Entry->CurrentFrame = 0;
Entry->Average_Cycles = 0;
Entry->Average_CallCount = 0;
Entry->Total_Cycles = 0;
Entry->Total_CallCount = 0;
CopyStringTo(Record.ScopeName, &Entry->ScopeName);
return Result;
}
internal void
DEBUGRecordScopeInHistogram (debug_services* Services, s32 Index, scope_time_record Record)
{
debug_histogram_entry* Entry = Services->ScopeHistogram + Index;
s32 FrameIndex = Entry->CurrentFrame;
if (FrameIndex >= 0 && FrameIndex < HISTOGRAM_DEPTH)
{
Entry->PerFrame_Cycles[FrameIndex] += Record.Duration_Cycles;
Entry->PerFrame_CallCount[FrameIndex]++;
}
}
internal void
DEBUGCacheScopeHistogramValues (debug_histogram_entry* Histogram)
{
Histogram->Total_Cycles = 0;
Histogram->Total_CallCount = 0;
// TODO(Peter): This doesn't account for the first frames when the histogram isn't full
for (s32 i = 0; i < HISTOGRAM_DEPTH; i++)
{
Histogram->Total_Cycles += Histogram->PerFrame_Cycles[i];
Histogram->Total_CallCount += Histogram->PerFrame_CallCount[i];
}
Histogram->Average_Cycles = (Histogram->Total_Cycles / HISTOGRAM_DEPTH);
Histogram->Average_CallCount = (Histogram->Total_CallCount / HISTOGRAM_DEPTH);
}
internal void
DEBUGCollateScopeRecords (debug_services* Services)
{
for (s32 i = 0; i < Services->TrackedScopesCount; i++)
{
scope_time_record Record = Services->TrackedScopes[i];
s32 Index = DEBUGFindScopeHistogram(Services, Record.ScopeName);
if (Index < 0)
{
Index = DEBUGAddScopeHistogram(Services, Record);
}
DEBUGRecordScopeInHistogram(Services, Index, Record);
}
for (s32 h = 0; h < Services->ScopeHistogramUsed; h++)
{
DEBUGCacheScopeHistogramValues(Services->ScopeHistogram + h);
}
}
internal void
EndDebugFrame (debug_services* Services)
{
DEBUGCollateScopeRecords(Services);
GSZeroMemory((u8*)Services->TrackedScopes, sizeof(scope_time_record) * Services->TrackedScopesMax);
Services->TrackedScopesCount = 0;
for (s32 i = 0; i < Services->ScopeHistogramUsed; i++)
{
s32 NewFrame = Services->ScopeHistogram[i].CurrentFrame + 1;
if (NewFrame >= HISTOGRAM_DEPTH)
{
NewFrame = 0;
}
Services->ScopeHistogram[i].CurrentFrame = NewFrame;
Services->ScopeHistogram[i].PerFrame_Cycles[NewFrame] = 0;
Services->ScopeHistogram[i].PerFrame_CallCount[NewFrame] = 0;
}
}
internal scope_time_record*
PushScopeRecord(string ScopeName, debug_services* Services)
{
scope_time_record* Result = 0;
s32 OnePastIndex = InterlockedIncrement((long*)&Services->TrackedScopesCount);
Assert(OnePastIndex <= Services->TrackedScopesMax);
Result = Services->TrackedScopes + OnePastIndex - 1;
Result->ScopeName = MakeString(Result->ScopeName_, SCOPE_NAME_LENGTH);
CopyStringTo(ScopeName, &Result->ScopeName);
return Result;
}
internal void
LogScopeTime (debug_services* Services, string ScopeName, u64 CyclesElapsed)
{
scope_time_record* Record = PushScopeRecord(ScopeName, Services);
Record->Duration_Cycles = CyclesElapsed;
}
internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFrequency)
{
r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency);
return Result;
}
#if 1
#define DEBUG_TRACK_FUNCTION scope_tracker ScopeTracker (__FUNCTION__, GlobalDebugServices)
#define DEBUG_TRACK_SCOPE(name) scope_tracker ScopeTracker_##name (#name, GlobalDebugServices)
#else
#define DEBUG_TRACK_FUNCTION
#define DEBUG_TRACK_SCOPE(name)
#endif
struct scope_tracker
{
s64 ScopeStart;
char ScopeName_[SCOPE_NAME_LENGTH];
string ScopeName;
debug_services* DebugServices;
scope_tracker(char* ScopeName, debug_services* DebugServices)
{
this->ScopeName = MakeString(this->ScopeName_, SCOPE_NAME_LENGTH);
CopyCharArrayToString(ScopeName, &this->ScopeName);
this->ScopeStart = DebugServices->GetWallClock();
this->DebugServices = DebugServices;
}
~scope_tracker()
{
s64 ScopeEnd = DebugServices->GetWallClock();
u32 CyclesElapsed = (u32)(ScopeEnd - this->ScopeStart);
#if 0
r32 SecondsElapsed = DEBUGGetSecondsElapsed(this->ScopeStart, ScopeEnd, DebugServices->PerformanceCountFrequency);
#endif
LogScopeTime(DebugServices, ScopeName, CyclesElapsed);
}
};

138
foldhaus_debug_visuals.h Normal file
View File

@ -0,0 +1,138 @@
internal void
DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_config Interface, r32 WindowWidth, r32 WindowHeight, r32 DeltaTime, camera Camera, input Input, memory_arena* Transient)
{
DEBUG_TRACK_SCOPE(DrawDebugInterface);
v2 TopOfDebugView = v2{StartX, WindowHeight - (Interface.Font->NewLineYOffset + 5)};
v2 TopOfScreenLinePos = TopOfDebugView;
arena_snapshot StartTempMemory = TakeSnapshotOfArena(*Transient);
string DebugString = InitializeString(PushArray(Transient, char, 256), 256);
if (GlobalDebugServices->Interface.ShowCameraMouse || GlobalDebugServices->Interface.ShowTrackedScopes)
{
PushRenderQuad2D(RenderBuffer,
v2{TopOfDebugView.x, TopOfDebugView.y - 500},
v2{TopOfDebugView.x + 700, TopOfDebugView.y},
v4{0, 0, 0, .8f});
}
r32 FramesPerSecond = 1.0f / DeltaTime;
PrintF(&DebugString, "Framerate: %.*f s %d fps",
5, DeltaTime,
(u32)FramesPerSecond);
DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize, TopOfScreenLinePos, WhiteV4);
v2 ButtonDim = v2{200, Interface.Font->NewLineYOffset + 10};
TopOfScreenLinePos.y -= ButtonDim.y + 10;
v2 ButtonPos = TopOfScreenLinePos;
button_result CameraBtn = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
MakeStringLiteral("Camera"), Interface, Input);
ButtonPos.x += ButtonDim.x + 10;
button_result ScopeTimeBtn = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
MakeStringLiteral("Scope Time"), Interface, Input);
ButtonPos.x += ButtonDim.x + 10;
button_result RenderSculptureBtn = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
MakeStringLiteral("Visualize"), Interface, Input);
ButtonPos.x += ButtonDim.x + 10;
string SACNButtonString;
if (GlobalDebugServices->Interface.SendSACNData)
{
SACNButtonString = MakeStringLiteral("Turn SACN Off");
}
else
{
SACNButtonString = MakeStringLiteral("Turn SACN On");
}
button_result SendSACNDataBtn = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
SACNButtonString, Interface, Input);
TopOfScreenLinePos.y -= Interface.Font->NewLineYOffset + 10;
if (CameraBtn.Pressed)
{
GlobalDebugServices->Interface.ShowCameraMouse = !GlobalDebugServices->Interface.ShowCameraMouse;
}
if (ScopeTimeBtn.Pressed)
{
GlobalDebugServices->Interface.ShowTrackedScopes = !GlobalDebugServices->Interface.ShowTrackedScopes;
}
if (RenderSculptureBtn.Pressed)
{
GlobalDebugServices->Interface.RenderSculpture =
!GlobalDebugServices->Interface.RenderSculpture;
}
if (SendSACNDataBtn.Pressed)
{
GlobalDebugServices->Interface.SendSACNData = !GlobalDebugServices->Interface.SendSACNData;
}
if (GlobalDebugServices->Interface.ShowCameraMouse)
{
PrintF(&DebugString, "Camera x=%.*f y=%.*f z=%.*f LookAt x=%.*f y=%.*f z=%.*f",
3, Camera.Position.x,
3, Camera.Position.y,
3, Camera.Position.z,
3, Camera.LookAt.x,
3, Camera.LookAt.y,
3, Camera.LookAt.z);
DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize, TopOfScreenLinePos, v4{1.0f, 1.0f, 1.0f, 1.0f});
TopOfScreenLinePos.y -= Interface.Font->NewLineYOffset;
}
if (GlobalDebugServices->Interface.ShowTrackedScopes)
{
r32 ColumnsStartX = TopOfScreenLinePos.x;
for (s32 i = 0; i < GlobalDebugServices->ScopeHistogramUsed; i++)
{
v2 Register = v2{ColumnsStartX, TopOfScreenLinePos.y};
s32 CurrentFrame = GlobalDebugServices->ScopeHistogram[i].CurrentFrame - 1;
if (CurrentFrame < 0) { CurrentFrame = HISTOGRAM_DEPTH - 1; }
u64 CyclesPerHit = GlobalDebugServices->ScopeHistogram[i].PerFrame_Cycles[CurrentFrame];
r32 SecondsPerHit = (r32)CyclesPerHit / (r32)GlobalDebugServices->PerformanceCountFrequency;
// Column 1
PrintF(&DebugString, "%.*s",
GlobalDebugServices->ScopeHistogram[i].ScopeName.Length,
GlobalDebugServices->ScopeHistogram[i].ScopeName.Memory);
r32 ColumnOneX = DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize,
Register, WhiteV4).x;
Register.x += GSMax(ColumnOneX - Register.x, 250.f);
// Column 2
PrintF(&DebugString, "%d hits", GlobalDebugServices->ScopeHistogram[i].PerFrame_CallCount[CurrentFrame]);
r32 ColumnTwoX = DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize,
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, Interface.FontSize,
Register, WhiteV4).x;
Register.x += GSMax(ColumnThreeX - Register.x, 200.f);
PrintF(&DebugString, "%f sec", SecondsPerHit);
r32 ColumnFourX = DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize,
Register, WhiteV4).x;
Register.x += GSMax(ColumnFourX - Register.x, 200.f);
TopOfScreenLinePos.y -= Interface.Font->NewLineYOffset;
}
}
ZeroArenaToSnapshot(Transient, StartTempMemory);
ClearArenaToSnapshot(Transient, StartTempMemory);
}

278
foldhaus_interface.cpp Normal file
View File

@ -0,0 +1,278 @@
// NOTE(Peter): returns the rightmost bound of the panel
internal r32
DrawLeftHandInterface (app_state* State, input Input, r32 WindowHeight, render_command_buffer* RenderBuffer)
{
DEBUG_TRACK_FUNCTION;
s32 StringLength = 128;
panel_result LeftHandPanel = EvaluatePanel(RenderBuffer, v2{0, 0}, v2{250, WindowHeight},
MakeStringLiteral("Channel Ops"), 0, State->Interface, Input);
r32 ListHeight = (LeftHandPanel.ChildMax.y - LeftHandPanel.ChildMin.y) / 2;
panel_result ChannelListPanel = EvaluatePanel(RenderBuffer, &LeftHandPanel,
ListHeight, MakeStringLiteral("Channels"),
State->Interface, Input);
panel_result PatternsListPanel = EvaluatePanel(RenderBuffer, &LeftHandPanel, ListHeight, MakeStringLiteral("Patterns"),
State->Interface, Input);
// NOTE(Peter): have to do this before we open it otherwise, it'll just close again immediately
// due to the mouse being pressed, outside the box, on the frame it is opened;
if (State->InterfaceState.AddingPattern &&
State->InterfaceState.ChannelSelected >= 0)
{
v2 PatternSelectorDim = v2{300, 200};
v2 PatternSelectorPosition = PatternsListPanel.NextPanelMin;
PatternSelectorPosition.y = PatternsListPanel.ChildMax.y - PatternSelectorDim.y;
panel_result AddPatternPanel = EvaluatePanel(
RenderBuffer,
PatternSelectorPosition, PatternSelectorPosition + PatternSelectorDim,
MakeStringLiteral("Add Pattern"), 0, State->Interface, Input);
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton) &&
!PointIsInRange(v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY},
AddPatternPanel.ChildMin, AddPatternPanel.ChildMax))
{
State->InterfaceState.AddingPattern = false;
}
s32 PatternsCount = sizeof(PatternRegistry)/sizeof(PatternRegistry[0]);
string* PatternNames = PushArray(State->Transient, string, PatternsCount);
for (s32 i = 0; i < PatternsCount; i++)
{
PushString(&PatternNames[i], State->Transient, StringLength);
CopyCharArrayToString(PatternRegistry[i].Name, &PatternNames[i]);
}
scroll_list_result PatternsResult = DrawOptionsList(RenderBuffer, AddPatternPanel.ChildMin,
AddPatternPanel.ChildMax,
PatternNames,
PatternsCount,
State->InterfaceState.PatternSelectorStart,
State->Interface,
Input);
if (PatternsResult.IndexSelected >= 0)
{
led_channel* ActiveChannel = GetChannelByIndex(State->InterfaceState.ChannelSelected,
State->ChannelSystem);
pattern_index_id_key PatternKey = AddPattern(
&State->PatternSystem,
&PatternRegistry[PatternsResult.IndexSelected]);
AddPatternKeyToChannel(PatternKey, ActiveChannel);
}
State->InterfaceState.PatternSelectorStart = PatternsResult.StartIndex;
}
s32 ChannelCount = State->ChannelSystem.ChannelCount;
// NOTE(Peter): adding one to the channel count here so that we can tack on the '+ Add Channel' lable at the end.
// NOTE(Peter): I think I've spelled lable as label all throughout here now... oops.
s32 ChannelLabelsCount = ChannelCount + 1;
string* ChannelTitles = PushArray(State->Transient, string, ChannelLabelsCount);
led_channel* Channel = State->ChannelSystem.Channels;
for (s32 ChannelIdx = 0; ChannelIdx < ChannelCount; ChannelIdx++)
{
PushString(&ChannelTitles[ChannelIdx], State->Transient, StringLength);
PrintF(&ChannelTitles[ChannelIdx], "Channel %d", Channel->ChannelID);
Channel = Channel->Next;
}
ChannelTitles[ChannelCount] = MakeStringLiteral("+ Add Channel");
scroll_list_result ChannelList = DrawSelectableOptionsList(
RenderBuffer,
ChannelListPanel.ChildMin,
ChannelListPanel.ChildMax,
ChannelTitles,
ChannelLabelsCount,
State->InterfaceState.ChannelSelectorStart,
State->InterfaceState.ChannelSelected,
State->Interface, Input);
State->InterfaceState.ChannelSelectorStart = ChannelList.StartIndex;
if (ChannelList.Selection == Selection_Selected)
{
if (ChannelList.IndexSelected >= ChannelCount)
{
led_channel* NewChannel = AddLEDChannel(&State->ChannelSystem);
}
else
{
State->InterfaceState.SelectionType = InterfaceSelection_Channel;
State->InterfaceState.ChannelSelected = ChannelList.IndexSelected;
State->InterfaceState.ActiveChannelPatternSelected = -1;
}
}
else if (ChannelList.Selection == Selection_Deselected)
{
State->InterfaceState.ChannelSelected = -1;
State->InterfaceState.ActiveChannelPatternSelected = -1;
}
s32 ActiveChannelPatternCount = 0;
s32 PatternLabelsCount = 1;
string* PatternTitles = PushArray(State->Transient, string, 1);;
if (State->InterfaceState.ChannelSelected >= 0)
{
led_channel* ActiveChannel = GetChannelByIndex(State->InterfaceState.ChannelSelected,
State->ChannelSystem);
ActiveChannelPatternCount = ActiveChannel->ActivePatterns;
// NOTE(Peter): We're just growing the PatternTitles array allocated above.
// IMPORTANT(Peter): make sure no allocations happen between the PushArray to PatternTitles and this one vvv
PushArray(State->Transient, string, ActiveChannelPatternCount);
PatternLabelsCount += ActiveChannelPatternCount;
for (s32 P = 0; P < ActiveChannelPatternCount; P++)
{
led_pattern* Pattern = FindPatternAndUpdateIDKey(&ActiveChannel->Patterns[P],
&State->PatternSystem);
PushString(&PatternTitles[P], State->Transient, StringLength);
PrintF(&PatternTitles[P], "%s %d", Pattern->Name, ActiveChannel->Patterns[P].ID);
Pattern++;
}
}
PatternTitles[ActiveChannelPatternCount]= MakeStringLiteral("+ Add Pattern");
scroll_list_result PatternsList = DrawSelectableOptionsList(
RenderBuffer,
PatternsListPanel.ChildMin,
PatternsListPanel.ChildMax,
PatternTitles,
PatternLabelsCount,
State->InterfaceState.ActiveChannelPatternSelectorStart,
State->InterfaceState.ActiveChannelPatternSelected,
State->Interface, Input
);
State->InterfaceState.ActiveChannelPatternSelectorStart = PatternsList.StartIndex;
if (PatternsList.Selection == Selection_Selected)
{
if (PatternsList.IndexSelected >= ActiveChannelPatternCount)
{
State->InterfaceState.AddingPattern = true;
}
else if (PatternsList.IndexSelected >= 0)
{
State->InterfaceState.SelectionType = InterfaceSelection_Pattern;
OutputDebugStringA("Pattern\n");
State->InterfaceState.ActiveChannelPatternSelected = PatternsList.IndexSelected;
}
}
else if (PatternsList.Selection == Selection_Deselected)
{
State->InterfaceState.SelectionType = InterfaceSelection_None;
OutputDebugStringA("None\n");
State->InterfaceState.ActiveChannelPatternSelected = -1;
}
return LeftHandPanel.NextPanelMin.x;
}
FOLDHAUS_INPUT_COMMAND_PROC(DeleteSelectedChannelOrPattern)
{
if (State->InterfaceState.ChannelSelected >= 0)
{
switch (State->InterfaceState.SelectionType)
{
case InterfaceSelection_Channel:
{
led_channel* DeleteCandidate = GetChannelByIndex(
State->InterfaceState.ChannelSelected,
State->ChannelSystem);
for (s32 i = 0; i < DeleteCandidate->ActivePatterns; i++)
{
RemovePattern(DeleteCandidate->Patterns[i],
&State->PatternSystem);
}
RemoveLEDChannel(State->InterfaceState.ChannelSelected, &State->ChannelSystem);
State->InterfaceState.ChannelSelected--;
}break;
case InterfaceSelection_Pattern:
{
if (State->InterfaceState.ActiveChannelPatternSelected >= 0)
{
led_channel* ActiveChannel = GetChannelByIndex(State->InterfaceState.ChannelSelected,
State->ChannelSystem);
s32 KeyIndex = State->InterfaceState.ActiveChannelPatternSelected;
pattern_index_id_key Key = ActiveChannel->Patterns[KeyIndex];
if (RemovePattern(Key, &State->PatternSystem))
{
RemovePatternKeyFromChannel(Key, ActiveChannel);
}
}
}break;
}
}
}
FOLDHAUS_INPUT_COMMAND_PROC(CameraMouseControl)
{
if (State->NodeInteraction.NodeOffset >= 0) { return; }
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton))
{
State->Camera_StartDragPos = V4(State->Camera.Position, 1);
}
if (Input.MouseDownY > State->InterfaceYMax)
{
if (!State->DrawUniverseOutputDisplay)
{
v2 DeltaPos = v2{
(r32)(Input.New->MouseX - Input.MouseDownX),
(r32)(Input.New->MouseY - Input.MouseDownY)
};
m44 XRotation = GetXRotation(-DeltaPos.y * State->PixelsToWorldScale);
m44 YRotation = GetYRotation(DeltaPos.x * State->PixelsToWorldScale);
m44 Combined = XRotation * YRotation;
State->Camera.Position = V3(Combined * State->Camera_StartDragPos);
}
else
{
v2 DeltaPos = v2{
(r32)(Input.New->MouseX - Input.Old->MouseX),
(r32)(Input.New->MouseY - Input.Old->MouseY)
};
State->UniverseOutputDisplayOffset += DeltaPos;
}
}
}
FOLDHAUS_INPUT_COMMAND_PROC(CameraMouseZoom)
{
if (State->DrawUniverseOutputDisplay)
{
r32 DeltaZoom = (r32)(Input.New->MouseScroll) / 120;
State->UniverseOutputDisplayZoom = GSClamp(0.1f, State->UniverseOutputDisplayZoom + DeltaZoom, 4.f);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(ToggleUniverseDebugView)
{
State->DrawUniverseOutputDisplay = !State->DrawUniverseOutputDisplay;
}
FOLDHAUS_INPUT_COMMAND_PROC(AddNode)
{
State->InterfaceShowNodeList = true;
State->NodeListMenuPosition = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY};
}

23
foldhaus_interface.h Normal file
View File

@ -0,0 +1,23 @@
enum interface_selection_type
{
InterfaceSelection_None,
InterfaceSelection_Channel,
InterfaceSelection_Pattern,
};
struct interface_state
{
b32 AddingPattern;
s32 ChannelSelectorStart;
s32 ChannelSelected;
s32 ActiveChannelPatternSelectorStart;
s32 ActiveChannelPatternSelected;
s32 PatternSelectorStart;
b32 ChooseOperationPanelOpen;
interface_selection_type SelectionType;
};

324
foldhaus_memory.h Normal file
View File

@ -0,0 +1,324 @@
#ifndef GS_MEMORY_H
#ifndef GS_LANGUAGE_H
typedef uint8_t u8;
typedef int8_t s8;
typedef uint32_t u32;
typedef int32_t s32;
internal void
GSMemSet (u8* Base, s32 Value, s32 Count)
{
u8* Cursor = Base;
for (s32 i = 0; i < Count; i++)
{
*Cursor++ = Value;
}
}
internal void
GSMemCopy (u8* Source, u8* Destination, s32 Count)
{
u8* Src = Source;
u8* Dst = Destination;
for (s32 i = 0; i < Count; i++)
{
*Dst++ = *Src++;
}
}
#endif // GS_LANGUAGE_H
#ifndef GS_PLATFORM_H
#define PLATFORM_MEMORY_NO_ERROR 0
#define PLATFORM_MEMORY_ERROR 1
struct platform_memory_result
{
s32 Size;
u8* Base;
s32 Error;
};
#define PLATFORM_ALLOC(name) platform_memory_result name(s32 Size)
typedef PLATFORM_ALLOC(platform_alloc);
// Returns 1 if successful, 0 otherwise
#define PLATFORM_FREE(name) b32 name(u8* Memory, s32 Size)
typedef PLATFORM_FREE(platform_free);
#endif // GS_PLATFORM_H
#if !defined Assert && defined DEBUG
#define Assert(expression) if(!(expression)){ *((int *)0) = 5; }
#define InvalidCodePath Assert(0)
#endif
#define MEMORY_REGION_PAGE_SIZE Megabytes(1)
struct memory_region
{
u8* Base;
u32 Size;
u32 Used;
memory_region* PreviousRegion;
};
struct memory_arena
{
memory_region* CurrentRegion;
platform_alloc* PlatformAlloc;
};
internal memory_region*
BootstrapRegionOntoMemory (u8* Memory, s32 Size)
{
Assert(Size > sizeof(memory_region));
memory_region* Result = (memory_region*)Memory;
Result->Base = Memory + sizeof(memory_region);
Result->Size = Size - sizeof(memory_region);
Result->Used = 0;
return Result;
}
#define PushStruct(arena, type) (type*)PushSize_(arena, sizeof(type))
#define PushArray(arena, type, count) (type*)PushSize_(arena, sizeof(type)*count)
#define PushSize(arena, size) PushSize_(arena, size)
static u8*
PushSize_ (memory_arena* Arena, u32 Size)
{
memory_region* PushOntoRegion = Arena->CurrentRegion;
if (!PushOntoRegion || PushOntoRegion->Used + Size > PushOntoRegion->Size)
{
// NOTE(Peter): we only search backwards if the item doesn't already fit in the most recent spot. This way, memory allocated
// one after another is more likely to be contiguous. You can expect that two allocations performed back to back are also next
// to eachother in memory most of the time.
if (PushOntoRegion)
{
// NOTE(Peter): Search backwards through previous regions to see if there is a region allocated that has enough room
// to fit this allocation
memory_region* PreviousRegion = Arena->CurrentRegion->PreviousRegion;
while (PreviousRegion)
{
if (PreviousRegion->Used + Size <= PreviousRegion->Size)
{
PushOntoRegion = PreviousRegion;
break;
}
PreviousRegion = PreviousRegion->PreviousRegion;
}
}
if (!PushOntoRegion || PushOntoRegion->Used + Size > PushOntoRegion->Size)
{
if (Arena->PlatformAlloc != 0)
{
// NOTE(Peter): Probably want to have this be a multiple of some minimum size so that we aren't constantly
// allocating new pages.
s32 SizeNeeded = Size + sizeof(memory_region);
s32 RegionPagesNeeded = IntegerDivideRoundUp(SizeNeeded, MEMORY_REGION_PAGE_SIZE);
s32 SizeToAllocate = RegionPagesNeeded * MEMORY_REGION_PAGE_SIZE;
platform_memory_result AllocResult = Arena->PlatformAlloc(SizeToAllocate);
Assert(AllocResult.Error == PLATFORM_MEMORY_NO_ERROR);
Assert(AllocResult.Size >= SizeNeeded);
memory_region* NewRegion = BootstrapRegionOntoMemory(AllocResult.Base, AllocResult.Size);
NewRegion->PreviousRegion = Arena->CurrentRegion;
Arena->CurrentRegion = NewRegion;
PushOntoRegion = Arena->CurrentRegion;
}
else
{
// NOTE(Peter): We ran out of memory in a memory arena that cannot/should not grow
InvalidCodePath;
}
}
}
u8* Result = PushOntoRegion->Base + PushOntoRegion->Used;
PushOntoRegion->Used += Size;
return Result;
}
static void
InitMemoryArena (memory_arena* Arena, u8* Base, u32 Size, platform_alloc* PlatformAlloc)
{
if (Base)
{
Arena->CurrentRegion = BootstrapRegionOntoMemory(Base, Size);
}
Arena->PlatformAlloc = PlatformAlloc;
}
static memory_arena*
BootstrapArenaIntoMemory (u8* Memory, u32 Size)
{
Assert(Size > sizeof(memory_arena));
// NOTE(Peter): takes in a block of memory, places a memory arena at the head, and gives
// the arena access to the rest of the block to use.
memory_arena* Result = (memory_arena*)Memory;
InitMemoryArena(Result, Memory + sizeof(memory_arena), Size - sizeof(memory_arena), 0);
return Result;
}
static memory_arena
AllocateNonGrowableArenaWithSpace(platform_alloc* PlatformAlloc, s32 SizeNeeded)
{
// TODO(Peter): This causes a leak currently. If you don't free the whole region later, you'll end up with
// the memory_region still being in memory. Should probably just make the first memory region be a member
// variable, not a pointer, in the memory_arena struct.
memory_arena Result = {};
s32 AllocateSize = SizeNeeded + sizeof(memory_region);
platform_memory_result Memory = PlatformAlloc(AllocateSize);
Assert(Memory.Error == PLATFORM_MEMORY_NO_ERROR);
Assert(Memory.Size == AllocateSize);
InitMemoryArena(&Result, Memory.Base, Memory.Size, 0);
return Result;
}
static void
ClearMemoryRegion (memory_region* Region)
{
Region->Used = 0;
}
static void
ClearArena (memory_arena* Arena)
{
memory_region* CurrentRegion = Arena->CurrentRegion;
while (CurrentRegion)
{
ClearMemoryRegion(CurrentRegion);
CurrentRegion = CurrentRegion->PreviousRegion;
}
}
struct arena_snapshot
{
memory_region* CurrentRegion;
u32 UsedAtSnapshot;
};
static arena_snapshot
TakeSnapshotOfArena (memory_arena Arena)
{
arena_snapshot Result = {};
Result.CurrentRegion = Arena.CurrentRegion;
Result.UsedAtSnapshot = Arena.CurrentRegion->Used;
return Result;
};
static void
ZeroArenaToSnapshot (memory_arena* Arena, arena_snapshot Snapshot)
{
memory_region* RegionCursor = Arena->CurrentRegion;
while (RegionCursor && RegionCursor != Snapshot.CurrentRegion)
{
GSZeroMemory(RegionCursor->Base, RegionCursor->Size);
RegionCursor = RegionCursor->PreviousRegion;
}
Assert(RegionCursor == Snapshot.CurrentRegion);
GSZeroMemory(RegionCursor->Base + Snapshot.UsedAtSnapshot,
RegionCursor->Used - Snapshot.UsedAtSnapshot);
}
static void
ClearArenaToSnapshot (memory_arena* Arena, arena_snapshot Snapshot)
{
memory_region* RegionCursor = Arena->CurrentRegion;
while (RegionCursor && RegionCursor != Snapshot.CurrentRegion)
{
RegionCursor->Used = 0;
RegionCursor = RegionCursor->PreviousRegion;
}
Assert(RegionCursor == Snapshot.CurrentRegion);
RegionCursor->Used = Snapshot.UsedAtSnapshot;
}
//
// 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

117
foldhaus_network_ordering.h Normal file
View File

@ -0,0 +1,117 @@
//Packs a u8 to a known big endian buffer
inline u8* PackB1(u8* ptr, u8 val)
{
*ptr = val;
return ptr + sizeof(val);
}
//Unpacks a u8 from a known big endian buffer
inline u8 UpackB1(const u8* ptr)
{
return *ptr;
}
//Packs a u8 to a known little endian buffer
inline u8* PackL1(u8* ptr, u8 val)
{
*ptr = val;
return ptr + sizeof(val);
}
//Unpacks a u8 from a known little endian buffer
inline u8 UpackL1(const u8* ptr)
{
return *ptr;
}
//Packs a u16 to a known big endian buffer
inline u8* PackB2(u8* ptr, u16 val)
{
ptr[1] = (u8)(val & 0xff);
ptr[0] = (u8)((val & 0xff00) >> 8);
return ptr + sizeof(val);
}
//Unpacks a u16 from a known big endian buffer
inline u16 UpackB2(const u8* ptr)
{
return (u16)(ptr[1] | ptr[0] << 8);
}
//Packs a u16 to a known little endian buffer
inline u8* PackL2(u8* ptr, u16 val)
{
*((u16*)ptr) = val;
return ptr + sizeof(val);
}
//Unpacks a u16 from a known little endian buffer
inline u16 UpackL2(const u8* ptr)
{
return *((u16*)ptr);
}
//Packs a u32 to a known big endian buffer
inline u8* PackB4(u8* ptr, u32 val)
{
ptr[3] = (u8) (val & 0xff);
ptr[2] = (u8)((val & 0xff00) >> 8);
ptr[1] = (u8)((val & 0xff0000) >> 16);
ptr[0] = (u8)((val & 0xff000000) >> 24);
return ptr + sizeof(val);
}
//Unpacks a u32 from a known big endian buffer
inline u32 UpackB4(const u8* ptr)
{
return (u32)(ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24));
}
//Packs a u32 to a known little endian buffer
inline u8* PackL4(u8* ptr, u32 val)
{
*((u32*)ptr) = val;
return ptr + sizeof(val);
}
//Unpacks a u32 from a known little endian buffer
inline u32 UpackL4(const u8* ptr)
{
return *((u32*)ptr);
}
//Packs a u64 to a known big endian buffer
inline u8* PackB8(u8* ptr, u64 val)
{
ptr[7] = (u8) (val & 0xff);
ptr[6] = (u8)((val & 0xff00) >> 8);
ptr[5] = (u8)((val & 0xff0000) >> 16);
ptr[4] = (u8)((val & 0xff000000) >> 24);
ptr[3] = (u8)((val & 0xff00000000) >> 32);
ptr[2] = (u8)((val & 0xff0000000000) >> 40);
ptr[1] = (u8)((val & 0xff000000000000) >> 48);
ptr[0] = (u8)((val & 0xff00000000000000) >> 56);
return ptr + sizeof(val);
}
//Unpacks a uint64 from a known big endian buffer
inline u64 UpackB8(const u8* ptr)
{
return ((u64)ptr[7]) | (((u64)ptr[6]) << 8) | (((u64)ptr[5]) << 16) |
(((u64)ptr[4]) << 24) | (((u64)ptr[3]) << 32) |
(((u64)ptr[2]) << 40) | (((u64)ptr[1]) << 48) |
(((u64)ptr[0]) << 56);
}
//Packs a u64 to a known little endian buffer
inline u8* PackL8(u8* ptr, u64 val)
{
*((u64*)ptr) = val;
return ptr + sizeof(val);
}
//Unpacks a u64 from a known little endian buffer
inline u64 UpackL8(const u8* ptr)
{
return *((u64*)ptr);
}

1079
foldhaus_node.cpp Normal file

File diff suppressed because it is too large Load Diff

182
foldhaus_node.h Normal file
View File

@ -0,0 +1,182 @@
typedef enum node_type node_type;
#define IsInputMember 1 << 0
#define IsOutputMember 1 << 1
#define NODE_COLOR_BUFFER \
led* LEDs; \
sacn_pixel* Colors; \
s32 LEDCount;
#define NAMED_NODE_COLOR_BUFFER(name) \
led* name##LEDs; \
sacn_pixel* name##Colors; \
s32 name##LEDCount;
#define NODE_COLOR_BUFFER_INOUT NODE_COLOR_BUFFER
#define NODE_COLOR_BUFFER_IN(name) NAMED_NODE_COLOR_BUFFER(name)
#define NODE_COLOR_BUFFER_OUT(name) NAMED_NODE_COLOR_BUFFER(name)
enum mouse_node_interaction
{
MouseNodeInteraction_None,
MouseNodeInteraction_DragNode,
MouseNodeInteraction_DragInput,
MouseNodeInteraction_Count,
};
enum node_port_direction
{
NodePort_Input,
NodePort_Output,
};
// TODO(Peter): Generate this
enum struct_member_type
{
MemberType_s32,
MemberType_r32,
MemberType_v4,
MemberType_NODE_COLOR_BUFFER,
MemberTypeCount,
};
struct node_led_color_connection
{
NODE_COLOR_BUFFER;
};
struct node_connection
{
struct_member_type Type;
// NOTE(Peter): Offset from the head of the node list that the connected node
// is stored at. See GetNodeAtOffset for example of how this is used
s32 UpstreamNodeOffset;
s32 UpstreamNodePortIndex;
s32 DownstreamNodeOffset;
s32 DownstreamNodePortIndex;
b32 DirectionMask;
union
{
s32 S32Value;
r32 R32Value;
v4 V4Value;
node_led_color_connection LEDsValue;
};
};
// TODO(Peter): cant decide if this needs to be dynamic or just a really big number
// reevaluate once you have some examples
#define NODE_CONNECTIONS_MAX 8
struct interface_node
{
string Name;
v2 Min, Dim;
v2 MinAfterUpdate;
s32 ConnectionsCount;
node_connection* Connections;
node_type Type;
b32 UpdatedThisFrame;
};
struct node_list
{
u8* Memory;
s32 Max;
s32 Used;
node_list* Next;
};
struct node_offset
{
interface_node* Node;
s32 Offset;
};
struct node_list_iterator
{
node_list List;
interface_node* At;
};
enum node_interaction_flag
{
NodeInteraction_AllUpstream = 0x1,
NodeInteraction_AllDownstream = 0x2,
};
struct node_interaction
{
s32 NodeOffset;
v2 MouseOffset;
b32 Flags;
s32 InputPort;
s32 InputValue;
s32 OutputPort;
s32 OutputValue;
};
struct node_struct_member
{
struct_member_type Type;
char* Name;
u64 Offset;
b32 IsInput;
};
struct node_specification
{
node_type Type;
char* Name;
s32 NameLength;
node_struct_member* MemberList;
s32 DataStructSize;
s32 MemberListLength;
b32 IsPattern;
};
struct node_render_settings
{
v2 PortDim;
r32 PortStep;
v4 PortColors[MemberTypeCount];
bitmap_font* Font;
};
v4 DragButtonColors[] = {
v4{.7f, .7f, .7f, 1},
BlackV4,
v4{.7f, .7f, .7f, 1},
};
#define NODE_HEADER_HEIGHT 20
///////////////////////////////////////////////
// Pre Processor Macros
///////////////////////////////////////////////
#define NODE_STRUCT(data_name) \
struct data_name
#define NODE_PATTERN_STRUCT(data_name) \
struct data_name
#define NODE_PROC(proc_name, input_type) \
void proc_name(input_type* Data)
#define NODE_PATTERN_PROC(proc_name, input_type) \
void proc_name(input_type* Data, led* LEDs, sacn_pixel* Colors, s32 LEDCount)
#define NODE_IN(type, name) type name
#define NODE_OUT(type, name) type name

247
foldhaus_patterns.cpp Normal file
View File

@ -0,0 +1,247 @@
NODE_STRUCT(multiply_data)
{
NODE_IN(r32, A);
NODE_IN(r32, B);
NODE_OUT(r32, Result);
};
NODE_PROC(MultiplyNodeProc, multiply_data)
{
Data->Result = Data->A * Data->B;
}
NODE_STRUCT(add_data)
{
NODE_IN(v4, A);
NODE_IN(v4, B);
NODE_OUT(v4, Result);
};
NODE_PROC(AddNodeProc, add_data)
{
Data->Result = Data->A + Data->B;
}
//////////////////////////////////////
//
// OLD - Pre Node System
//
///////////////////////////////////////
PATTERN_PUSH_COLOR_PROC(PushColor_Override)
{
Colors[LED->Index].R = R;
Colors[LED->Index].G = G;
Colors[LED->Index].B = B;
}
PATTERN_PUSH_COLOR_PROC(PushColor_Add)
{
Colors[LED->Index].R += R;
Colors[LED->Index].G += G;
Colors[LED->Index].B += B;
}
PATTERN_PUSH_COLOR_PROC(PushColor_Multiply)
{
Colors[LED->Index].R *= R;
Colors[LED->Index].G *= G;
Colors[LED->Index].B *= B;
}
inline u32
PackColorStruct (u8* Channels)
{
u32 Result = 0;
Result |= (*Channels++ << 24);
Result |= (*Channels++ << 16);
Result |= (*Channels++ << 8);
Result |= (255 << 0); // Alpha
return Result;
};
inline u8
ToColorU8 (r32 V)
{
return (u8)(V * 255.f);
}
inline u32
PackColorStructFromVector (v4 Color)
{
u32 Result = ((ToColorU8(Color.r) << 24) |
(ToColorU8(Color.g) << 16) |
(ToColorU8(Color.b) << 8) |
(ToColorU8(Color.a) << 0));
return Result;
}
internal void
InitLEDPatternSystem (led_pattern_system* PatternSystem, memory_arena* ParentStorage,
s32 MaxPatternsCount, s32 PatternWorkingMemoryStorageSize)
{
PatternSystem->Patterns = PushArray(ParentStorage, led_pattern, MaxPatternsCount);
PatternSystem->PatternsUsed = 0;
PatternSystem->PatternsMax = MaxPatternsCount;
InitMemoryArena(&PatternSystem->PatternWorkingMemoryStorage, PushSize(ParentStorage, PatternWorkingMemoryStorageSize), PatternWorkingMemoryStorageSize, 0);
}
internal pattern_index_id_key
AddPattern (led_pattern_system* PatternSystem,
pattern_registry_entry* PatternSpec)
{
Assert(PatternSystem->PatternsUsed < PatternSystem->PatternsMax);
pattern_index_id_key Result = {};
led_pattern* NewPattern = &PatternSystem->Patterns[PatternSystem->PatternsUsed];
NewPattern->ID = PatternSystem->IDAccumulator++;
NewPattern->Name = PatternSpec->Name;
NewPattern->UpdateProc = PatternSpec->Update;
PatternSpec->Init(NewPattern, &PatternSystem->PatternWorkingMemoryStorage);
Result.Index = PatternSystem->PatternsUsed++;
Result.ID = NewPattern->ID;
return Result;
}
internal b32
RemovePattern (pattern_index_id_key Key, led_pattern_system* PatternSystem)
{
b32 Result = false;
s32 ActualIndex = -1;
if (PatternSystem->Patterns[Key.Index].ID == Key.ID)
{
ActualIndex = Key.Index;
}
else
{
for (s32 i = 0; i < PatternSystem->PatternsUsed; i++)
{
if (PatternSystem->Patterns[i].ID == Key.ID)
{
ActualIndex = i;
break;
}
}
}
if (ActualIndex >= 0)
{
for (s32 j = ActualIndex; j < PatternSystem->PatternsUsed - 1; j++)
{
PatternSystem->Patterns[j] = PatternSystem->Patterns[j + 1];
}
PatternSystem->PatternsUsed--;
Result = true;
}
else
{
Result = false;
}
return Result;
}
internal led_pattern*
FindPatternAndUpdateIDKey (pattern_index_id_key* Key, led_pattern_system* PatternSystem)
{
led_pattern* Result = 0;
if (Key->Index < PatternSystem->PatternsUsed &&
PatternSystem->Patterns[Key->Index].ID == Key->ID)
{
Result = &PatternSystem->Patterns[Key->Index];
}
else
{
for (s32 i = 0; i < PatternSystem->PatternsUsed; i++)
{
if (PatternSystem->Patterns[i].ID == Key->ID)
{
Result = &PatternSystem->Patterns[i];
Key->Index = i;
break;
}
}
}
return Result;
}
#if 0
internal void
UpdateAllPatterns_ (patterns_update_list* UpdateList,
led_pattern_system* PatternSystem,
pattern_led* LEDs,
s32 LEDsCount,
r32 DeltaTime,
memory_arena* Transient)
{
pattern_push_color_proc* PushColorProc = 0;
for (s32 PatternKeyIdx = 0; PatternKeyIdx < UpdateList->Used; PatternKeyIdx++)
{
pattern_update_list_entry ListEntry = UpdateList->Patterns[PatternKeyIdx];
pattern_index_id_key Key = ListEntry.Key;
led_pattern* Pattern = FindPatternAndUpdateIDKey(&Key, PatternSystem);
if (!Pattern)
{
Pattern = FindPatternAndUpdateIDKey(&Key, PatternSystem);
}
Pattern->UpdateProc(LEDs, LEDsCount,
Pattern->Memory,
DeltaTime, ListEntry.PushColorProc);
}
if (UpdateList->Next)
{
UpdateAllPatterns(UpdateList->Next, PatternSystem, LEDs, LEDsCount, DeltaTime, Transient);
}
}
#endif
internal void
UpdateAllPatterns (patterns_update_list* UpdateList,
led_pattern_system* PatternSystem,
led_buffer* LEDBuffer,
r32 DeltaTime,
memory_arena* Transient)
{
pattern_push_color_proc* PushColorProc = 0;
for (s32 PatternKeyIdx = 0; PatternKeyIdx < UpdateList->Used; PatternKeyIdx++)
{
pattern_update_list_entry ListEntry = UpdateList->Patterns[PatternKeyIdx];
pattern_index_id_key Key = ListEntry.Key;
led_pattern* Pattern = FindPatternAndUpdateIDKey(&Key, PatternSystem);
if (!Pattern)
{
Pattern = FindPatternAndUpdateIDKey(&Key, PatternSystem);
}
led_buffer* LEDBufferIter = LEDBuffer;
while(LEDBufferIter)
{
Pattern->UpdateProc(LEDBufferIter->LEDs, LEDBufferIter->Colors, LEDBufferIter->Count,
Pattern->Memory,
DeltaTime, ListEntry.PushColorProc);
LEDBufferIter = LEDBufferIter->Next;
}
}
if (UpdateList->Next)
{
UpdateAllPatterns(UpdateList->Next, PatternSystem, LEDBuffer, DeltaTime, Transient);
}
}

78
foldhaus_patterns.h Normal file
View File

@ -0,0 +1,78 @@
typedef s32 pattern_id;
typedef struct pattern_led pattern_led;
#define PATTERN_INIT_PROC(name) void name(led_pattern* Pattern, memory_arena* Storage)
typedef PATTERN_INIT_PROC(pattern_init_proc);
#define PATTERN_PUSH_COLOR_PROC(name) void name(led* LED, sacn_pixel* Colors, u8 R, u8 G, u8 B)
typedef PATTERN_PUSH_COLOR_PROC(pattern_push_color_proc);
#define PATTERN_UPDATE_PROC(name) void name(led* LEDs, sacn_pixel* Colors, s32 LEDCount, void* Memory, r32 DeltaTime, pattern_push_color_proc PushColor)
typedef PATTERN_UPDATE_PROC(pattern_update_proc);
struct pattern_registry_entry
{
char* Name;
pattern_init_proc* Init;
pattern_update_proc* Update;
};
struct pattern_led
{
s32 LocationInSendBuffer;
u32 Color;
v3 Position;
};
enum pattern_selector_combine_operation
{
PatternSelectorCombine_Invalid,
PatternSelectorCombine_Override,
PatternSelectorCombine_Add,
PatternSelectorCombine_Multiply,
PatternSelectorCombine_Count,
};
char* PatternSelectorOperationsText[] = {
"Invalid",
"Override",
"Add",
"Multiply",
"Count",
};
struct pattern_index_id_key
{
s32 Index;
pattern_id ID;
};
struct led_pattern
{
pattern_id ID;
void* Memory;
pattern_update_proc* UpdateProc;
char* Name;
};
struct led_pattern_system
{
// TODO(Peter): Need to think about how this grows
led_pattern* Patterns;
s32 PatternsUsed;
s32 PatternsMax;
// TODO(Peter): Need to think about how this can have a free list as well of some sort.
// It might be inexpensive enough to just compress this memory whenever we remove a pattern
memory_arena PatternWorkingMemoryStorage;
pattern_id IDAccumulator;
};

115
foldhaus_platform.h Normal file
View File

@ -0,0 +1,115 @@
#include "gs_language.h"
#include "gs_platform.h"
#include "foldhaus_memory.h"
#include "gs_string.h"
#include "gs_input.h"
#include "foldhaus_debug.h"
global_variable debug_services* GlobalDebugServices;
#include "gs_vector_matrix.h"
#include "foldhaus_renderer.h"
typedef struct context context;
// Application Functions
#define INITIALIZE_APPLICATION(name) void name(context Context)
typedef INITIALIZE_APPLICATION(initialize_application);
#define UPDATE_AND_RENDER(name) void name(context Context, input Input, render_command_buffer* RenderBuffer)
typedef UPDATE_AND_RENDER(update_and_render);
#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices)
typedef RELOAD_STATIC_DATA(reload_static_data);
#define CLEANUP_APPLICATION(name) void name(context Context)
typedef CLEANUP_APPLICATION(cleanup_application);
// Platform Functions
// Worker Threads
#define THREADED_WORK_PROC(name) void name(s32 ThreadID, void* Data)
typedef THREADED_WORK_PROC(threaded_work_proc);
typedef struct work_queue work_queue;
#define PUSH_WORK_ON_QUEUE(name) void name(work_queue* Queue, threaded_work_proc* WorkProc, void* Data)
typedef PUSH_WORK_ON_QUEUE(push_work_on_queue);
#define DO_QUEUE_WORK_UNTIL_DONE(name) void name(work_queue* Queue, s32 ThreadID)
typedef DO_QUEUE_WORK_UNTIL_DONE(do_queue_work_until_done);
#define RESET_WORK_QUEUE(name) void name(work_queue* Queue)
typedef RESET_WORK_QUEUE(reset_work_queue);
struct worker_thread_job
{
void* Data;
threaded_work_proc* WorkProc;
};
struct work_queue
{
HANDLE SemaphoreHandle;
u32 JobsMax;
u32 volatile JobsCount;
u32 volatile NextJobIndex;
u32 volatile JobsCompleted;
worker_thread_job Jobs[256];
// Work Queue
push_work_on_queue* PushWorkOnQueue;
do_queue_work_until_done* DoQueueWorkUntilDone;
reset_work_queue* ResetWorkQueue;
};
RESET_WORK_QUEUE(ResetWorkQueue)
{
for (u32 i = 0; i < Queue->JobsMax; i++)
{
Queue->Jobs[i].Data = 0;
Queue->Jobs[i].WorkProc = 0;
}
Queue->JobsCount = 0;
Queue->NextJobIndex = 0;
Queue->JobsCompleted = 0;
}
struct context
{
u8* MemoryBase;
u32 MemorySize;
b32 WindowIsVisible;
r32 WindowWidth;
r32 WindowHeight;
r32 DeltaTime;
// Application Services
initialize_application* InitializeApplication;
reload_static_data* ReloadStaticData;
update_and_render* UpdateAndRender;
cleanup_application* CleanupApplication;
// Platform Services
work_queue* GeneralWorkQueue;
platform_alloc* PlatformAlloc;
platform_free* PlatformFree;
platform_read_entire_file* PlatformReadEntireFile;
platform_write_entire_file* PlatformWriteEntireFile;
platform_get_file_path* PlatformGetFilePath;
platform_get_gpu_texture_handle* PlatformGetGPUTextureHandle;
platform_get_socket_handle* PlatformGetSocketHandle;
platform_get_send_address* PlatformGetSendAddress;
platform_set_socket_option* PlatformSetSocketOption;
platform_send_to* PlatformSendTo;
platform_close_socket* PlatformCloseSocket;
};

175
foldhaus_renderer.cpp Normal file
View File

@ -0,0 +1,175 @@
internal render_command_buffer
AllocateRenderCommandBuffer (u8* Memory, s32 Size)
{
render_command_buffer Result = {};
Result.CommandMemory = Memory;
Result.CommandMemoryUsed = 0;
Result.CommandMemorySize = Size;
return Result;
}
internal void
Render3DQuadBatch (u8* CommandData, s32 TriCount)
{
DEBUG_TRACK_FUNCTION;
v4* Vertecies = (v4*)(CommandData + BATCH_3D_VERTECIES_OFFSET(TriCount));
v2* UVs = (v2*)(CommandData + BATCH_3D_UVS_OFFSET(TriCount));
v4* Colors = (v4*)(CommandData + BATCH_3D_COLORS_OFFSET(TriCount));
#if IMMEDIATE_MODE_RENDERING
glBegin(GL_TRIANGLES);
for (s32 Tri = 0; Tri < TriCount; Tri++)
{
v4 P0 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 0)];
v4 P1 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 1)];
v4 P2 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 2)];
v2 UV0 = UVs[BATCH_3D_UV_INDEX(Tri, 0)];
v2 UV1 = UVs[BATCH_3D_UV_INDEX(Tri, 1)];
v2 UV2 = UVs[BATCH_3D_UV_INDEX(Tri, 2)];
v4 C0 = Colors[BATCH_3D_COLOR_INDEX(Tri, 0)];
v4 C1 = Colors[BATCH_3D_COLOR_INDEX(Tri, 1)];
v4 C2 = Colors[BATCH_3D_COLOR_INDEX(Tri, 2)];
OpenGLDraw3DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
}
glEnd();
#else
OpenGLRenderTriBuffer((u8*)Vertecies, 4, UVs, 2, Colors, 4, TriCount * 3);
#endif
}
internal void
Render2DQuadBatch (u8* CommandData, s32 QuadCount)
{
DEBUG_TRACK_FUNCTION;
v2* Vertecies = (v2*)(CommandData + BATCH_2D_VERTECIES_OFFSET(QuadCount));
v2* UVs = (v2*)(CommandData + BATCH_2D_UVS_OFFSET(QuadCount));
v4* Colors = (v4*)(CommandData + BATCH_2D_COLORS_OFFSET(QuadCount));
#if IMMEDIATE_MODE_RENDERING
for (s32 Quad = 0; Quad < QuadCount; Quad++)
{
for (s32 Tri = 0; Tri < 2; Tri++)
{
v2 P0 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 0)];
v2 P1 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 1)];
v2 P2 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 2)];
v2 UV0 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 0)];
v2 UV1 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 1)];
v2 UV2 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 2)];
v4 C0 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 0)];
v4 C1 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 1)];
v4 C2 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 2)];
OpenGLDraw2DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
}
}
#else
OpenGLRenderTriBuffer((u8*)Vertecies, 2, UVs, 2, Colors, 4, QuadCount * 2 * 3);
#endif
}
internal void
RenderCommandBuffer (render_command_buffer CommandBuffer)
{
DEBUG_TRACK_FUNCTION;
glViewport(0, 0, CommandBuffer.ViewWidth, CommandBuffer.ViewHeight);
glMatrixMode(GL_TEXTURE_2D);
glLoadIdentity();
glClearColor(0.1f, 0.1f, 0.1f, 1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
b32 GLTextureEnabled = false;
u8* CurrentPosition = CommandBuffer.CommandMemory;
while(CurrentPosition < CommandBuffer.CommandMemory + CommandBuffer.CommandMemoryUsed)
{
render_command_header* CommandHeader = (render_command_header*)CurrentPosition;
CurrentPosition += sizeof(render_command_header);
switch (CommandHeader->Type)
{
case RenderCommand_render_command_set_render_mode:
{
render_command_set_render_mode* Command = (render_command_set_render_mode*)(CommandHeader + 1);
LoadModelView(Command->ModelView.E);
LoadProjection(Command->Projection.E);
if (Command->UseDepthBuffer)
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
}
else
{
glDisable(GL_DEPTH_TEST);
}
CurrentPosition += sizeof(render_command_set_render_mode);
}break;
case RenderCommand_render_command_clear_screen:
{
render_command_clear_screen* Command = (render_command_clear_screen*)(CommandHeader + 1);
ClearRenderBuffer();
CurrentPosition += sizeof(render_command_clear_screen);
}break;
case RenderCommand_render_batch_command_quad_2d:
{
render_batch_command_quad_2d* Command = (render_batch_command_quad_2d*)(CommandHeader + 1);
if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; }
u8* CommandData = (u8*)(Command + 1);
Render2DQuadBatch(CommandData, Command->QuadCount);
CurrentPosition += sizeof(render_batch_command_quad_2d) + Command->DataSize;
}break;
case RenderCommand_render_batch_command_quad_3d:
{
render_batch_command_quad_3d* Command = (render_batch_command_quad_3d*)(CommandHeader + 1);
if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; }
u8* CommandData = (u8*)(Command + 1);
Render3DQuadBatch(CommandData, Command->QuadCount * 2);
CurrentPosition += sizeof(render_batch_command_quad_3d) + Command->DataSize;
}break;
case RenderCommand_render_batch_command_texture_2d:
{
render_batch_command_texture_2d* Command = (render_batch_command_texture_2d*)(CommandHeader + 1);
if (!GLTextureEnabled) { glEnable(GL_TEXTURE_2D); GLTextureEnabled = true; }
Assert(Command->Texture.Handle > 0);
glBindTexture(GL_TEXTURE_2D, Command->Texture.Handle);
u8* CommandData = (u8*)(Command + 1);
Render2DQuadBatch(CommandData, Command->QuadCount);
CurrentPosition += sizeof(render_batch_command_texture_2d) + Command->DataSize;
}break;
default:
{
InvalidCodePath;
}break;
}
}
}
internal void
ClearRenderBuffer (render_command_buffer* Buffer)
{
Buffer->CommandMemoryUsed = 0;
}

552
foldhaus_renderer.h Normal file
View File

@ -0,0 +1,552 @@
#define IMMEDIATE_MODE_RENDERING 0
struct camera
{
r32 FieldOfView;
r32 AspectRatio;
r32 Near, Far;
v3 Position;
v3 LookAt;
};
inline m44
GetCameraModelViewMatrix (camera Camera)
{
// Forward
v4 CamForward = V4(Normalize(Camera.Position - Camera.LookAt), 0);
// Right
v4 CamRight = Normalize(Cross(v4{0, 1, 0, 0}, CamForward));
// Up
v4 CamUp = Normalize(Cross(CamForward, CamRight));
r32 X = Camera.Position.x;
r32 Y = Camera.Position.y;
r32 Z = Camera.Position.z;
m44 RotationMatrix = M44(
CamRight.x, CamUp.x, CamForward.x, 0,
CamRight.y, CamUp.y, CamForward.y, 0,
CamRight.z, CamUp.z, CamForward.z, 0,
0, 0, 0, 1);
m44 PositionMatrix = M44(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-X, -Y, -Z, 1
);
m44 ModelViewMatrix = PositionMatrix * RotationMatrix;
return ModelViewMatrix;
}
inline m44
GetCameraPerspectiveProjectionMatrix(camera Camera)
{
r32 Top = Camera.Near * GSTan((Camera.FieldOfView / 2.0f));
r32 Bottom = -Top;
r32 Right = Top * Camera.AspectRatio;
r32 Left = -Right;
r32 A = ((Right + Left) / (Right - Left));
r32 B = ((Top + Bottom) / (Top - Bottom));
r32 C = -((Camera.Far + Camera.Near) / (Camera.Far - Camera.Near));
r32 D = -((2 * Camera.Far * Camera.Near) / (Camera.Far - Camera.Near));
r32 E = ((2 * Camera.Near) / (Right - Left));
r32 F = ((2 * Camera.Near) / (Top - Bottom));
m44 PerspectiveProjectionMatrix =
{
E, 0, A, 0,
0, F, B, 0,
0, 0, C, D,
0, 0, -1, 0
};
return PerspectiveProjectionMatrix;
}
// Render Commands
// Discriminated Union
enum render_command_type
{
RenderCommand_Invalid,
RenderCommand_render_command_clear_screen,
RenderCommand_render_command_set_render_mode,
RenderCommand_render_batch_command_quad_2d,
RenderCommand_render_batch_command_quad_3d,
RenderCommand_render_batch_command_texture_2d,
RenderCommand_render_command_texture_3d,
RenderCommand_Count
};
struct render_command_header
{
render_command_type Type;
};
// NOTE(Peter): Just to keep with the rest of the system
struct render_command_clear_screen {};
struct render_quad_2d
{
v2 Min, Max;
};
struct render_quad_3d
{
v4 P0, P1, P2, P3;
};
struct render_texture
{
// TODO(Peter): Is all this necessary?
u8* Memory;
s32 Handle;
s32 Width;
s32 Height;
s32 BytesPerPixel;
s32 Stride;
};
#define BATCH_3D_SIZE(tricount) (((sizeof(v4) + sizeof(v2) + sizeof(v4)) * 3) * tricount)
#define BATCH_3D_VERTECIES_OFFSET(tricount) (0 * tricount)
#define BATCH_3D_UVS_OFFSET(tricount) (BATCH_3D_VERTECIES_OFFSET(tricount) + ((sizeof(v4) * 3) * tricount))
#define BATCH_3D_COLORS_OFFSET(tricount) (BATCH_3D_UVS_OFFSET(tricount) + ((sizeof(v2) * 3) * tricount))
#define BATCH_3D_VERTEX_INDEX(tri, v) ((tri * 3) + v)
#define BATCH_3D_UV_INDEX(tri, v) ((tri * 3) + v)
#define BATCH_3D_COLOR_INDEX(tri, v) ((tri * 3) + v)
#define BATCH_2D_SIZE(quadcount) (((sizeof(v2) + sizeof(v2) + sizeof(v4)) * 3) * 2 * quadcount)
#define BATCH_2D_VERTECIES_OFFSET(quadcount) (0 * quadcount)
#define BATCH_2D_UVS_OFFSET(quadcount) (BATCH_2D_VERTECIES_OFFSET(quadcount) + ((sizeof(v2) * 3) * 2 * quadcount))
#define BATCH_2D_COLORS_OFFSET(quadcount) (BATCH_2D_UVS_OFFSET(quadcount) + ((sizeof(v2) * 3) * 2 * quadcount))
#define BATCH_2D_VERTEX_INDEX(quad, tri, v) ((quad * 6) + (tri * 3) + v)
#define BATCH_2D_UV_INDEX(quad, tri, v) ((quad * 6) + (tri * 3) + v)
#define BATCH_2D_COLOR_INDEX(quad, tri, v) ((quad * 6) + (tri * 3) + v)
struct render_quad_batch_constructor
{
s32 Max;
s32 Count;
v4* Vertecies;
v2* UVs;
v4* ColorsV;
};
struct render_batch_command_quad_2d
{
s32 QuadCount;
s32 DataSize;
// NOTE(Peter): The data immediately follows the command in memory
};
struct render_batch_command_quad_3d
{
s32 QuadCount;
s32 DataSize;
// NOTE(Peter): The data immediately follows the command in memory
};
struct render_command_texture_2d
{
render_quad_2d Quad;
render_quad_2d UV;
v4 Color;
render_texture Texture;
};
struct render_batch_command_texture_2d
{
s32 QuadCount;
s32 DataSize;
render_texture Texture;
};
struct render_command_texture_3d
{
render_quad_3d Quad;
v4 Color;
render_texture Texture;
};
struct render_command_set_render_mode
{
m44 ModelView;
m44 Projection;
r32 ViewWidth, ViewHeight;
b32 UseDepthBuffer;
};
struct render_command_buffer
{
u8* CommandMemory;
s32 CommandMemoryUsed;
s32 CommandMemorySize;
s32 ViewWidth;
s32 ViewHeight;
};
///
// Utility
///
internal u32
PackColorStructU8 (u8 R, u8 G, u8 B, u8 A)
{
u32 Result = (u32)(A << 24 |
R << 16 |
G << 8 |
B<< 0);
return Result;
}
internal u32
PackColorStructR32 (r32 In_R, r32 In_G, r32 In_B, r32 In_A)
{
Assert ((In_R >= 0.0f && In_R <= 1.0f) &&
(In_G >= 0.0f && In_G <= 1.0f) &&
(In_B >= 0.0f && In_B <= 1.0f) &&
(In_A >= 0.0f && In_A <= 1.0f));
u8 R = (u8)(255 * In_R);
u8 G = (u8)(255 * In_G);
u8 B = (u8)(255 * In_B);
u8 A = (u8)(255 * In_A);
u32 Result = (u32)(A << 24 |
R << 16 |
G << 8 |
B<< 0);
return Result;
}
// Batch
internal s32
PushQuad3DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, s32 QuadCount, u8* MemStart, b32 UseIntegerColor = false)
{
s32 TriCount = QuadCount * 2;
s32 DataSize = BATCH_3D_SIZE(TriCount);
Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
Constructor->Max = TriCount;
Constructor->Count = 0;
Constructor->Vertecies = (v4*)(MemStart + BATCH_3D_VERTECIES_OFFSET(TriCount));
Constructor->UVs = (v2*)(MemStart + BATCH_3D_UVS_OFFSET(TriCount));
Constructor->ColorsV = (v4*)(MemStart + BATCH_3D_COLORS_OFFSET(TriCount));
Buffer->CommandMemoryUsed += DataSize;
return DataSize;
}
internal s32
PushQuad2DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, s32 QuadCount, u8* MemStart)
{
s32 DataSize = BATCH_2D_SIZE(QuadCount);
Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
GSZeroMemory(MemStart, DataSize);
Constructor->Max = QuadCount;
Constructor->Count = 0;
Constructor->Vertecies = (v4*)(MemStart + BATCH_2D_VERTECIES_OFFSET(QuadCount));
Constructor->UVs = (v2*)(MemStart + BATCH_2D_UVS_OFFSET(QuadCount));
Constructor->ColorsV = (v4*)(MemStart + BATCH_2D_COLORS_OFFSET(QuadCount));
Buffer->CommandMemoryUsed += DataSize;
return DataSize;
}
internal s32
ThreadSafeIncrementQuadConstructorCount (render_quad_batch_constructor* Constructor)
{
s32 Result = InterlockedIncrement((long*)&Constructor->Count);
// NOTE(Peter): Have to decrement the value by one.
// Interlocked Increment acts as (++Constructor->Count), not (Constructor->Count++) which
// is what we wanted;
// This was causing the first triangle to be garbage data.
Result -= 1;
return Result;
}
inline void
PushTri3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v2 UV0, v2 UV1, v2 UV2, v4 Color)
{
s32 Tri = ThreadSafeIncrementQuadConstructorCount(Constructor);
// Vertecies
Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 0)] = P0;
Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 1)] = P1;
Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 2)] = P2;
// UVs
Constructor->UVs[BATCH_3D_UV_INDEX(Tri, 0)] = UV0;
Constructor->UVs[BATCH_3D_UV_INDEX(Tri, 1)] = UV1;
Constructor->UVs[BATCH_3D_UV_INDEX(Tri, 2)] = UV1;
// Color V0
Constructor->ColorsV[BATCH_3D_COLOR_INDEX(Tri, 0)] = Color;
Constructor->ColorsV[BATCH_3D_COLOR_INDEX(Tri, 1)] = Color;
Constructor->ColorsV[BATCH_3D_COLOR_INDEX(Tri, 2)] = Color;
};
internal void
PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v4 P3, v2 UVMin, v2 UVMax, v4 Color)
{
Assert(Constructor->Count < Constructor->Max);
PushTri3DOnBatch(Constructor, P0, P1, P2, UVMin, v2{UVMax.x, UVMin.y}, UVMax, Color);
PushTri3DOnBatch(Constructor, P0, P2, P3, UVMin, UVMax, v2{UVMin.x, UVMax.y}, Color);
}
internal void
PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v4 P3, v4 Color)
{
PushQuad3DOnBatch(Constructor, P0, P1, P2, P3, v2{0, 0}, v2{1, 1}, Color);
}
internal void
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor,
v2 P0, v2 P1, v2 P2, v2 P3,
v2 UV0, v2 UV1, v2 UV2, v2 UV3,
v4 C0, v4 C1, v4 C2, v4 C3)
{
s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor);
v2* Vertecies = (v2*)Constructor->Vertecies;
// Tri 1
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2;
// Tri 2
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3;
// Tri 1 UVs
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UV0;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = UV1;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UV2;
// Tri 2 UVs
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UV0;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UV2;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = UV3;
// Tri 1 Colors
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = C0;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = C1;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = C2;
// Tri 2 Colors
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = C0;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = C2;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = C3;
}
internal void
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, v2 P2, v2 P3, v2 UVMin, v2 UVMax, v4 Color)
{
s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor);
v2* Vertecies = (v2*)Constructor->Vertecies;
// Tri 1
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2;
// Tri 2
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2;
Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3;
// Tri 1 UVs
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UVMin;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = v2{UVMax.x, UVMin.y};
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UVMax;
// Tri 2 UVs
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UVMin;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UVMax;
Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = v2{UVMin.x, UVMax.y};
// Tri 1 Colors
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = Color;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = Color;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = Color;
// Tri 2 Colors
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = Color;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = Color;
Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = Color;
}
internal void
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 Min, v2 Max, v4 Color)
{
PushQuad2DOnBatch(Constructor, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y},
v2{0, 0}, v2{1, 1}, Color);
}
internal void
PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 Thickness, v4 Color)
{
r32 HalfThickness = Thickness / 2.0f;
v2 Perpendicular = Normalize(PerpendicularCCW(P1 - P0)) * HalfThickness;
PushQuad2DOnBatch(Constructor, P0 - Perpendicular, P1 - Perpendicular, P1 + Perpendicular, P0 + Perpendicular,
v2{0, 0}, v2{1, 1}, Color);
}
// Commands
#define PushRenderCommand(buffer, type) (type*) PushRenderCommand_(buffer, RenderCommand_##type, sizeof(type) + sizeof(render_command_header))
internal u8*
PushRenderCommand_ (render_command_buffer* CommandBuffer, render_command_type CommandType, s32 CommandSize)
{
Assert(CommandBuffer->CommandMemoryUsed + CommandSize <= CommandBuffer->CommandMemorySize);
render_command_header* Header = (render_command_header*)(CommandBuffer->CommandMemory + CommandBuffer->CommandMemoryUsed);
Header->Type = CommandType;
u8* Result = (u8*)(Header + 1);
CommandBuffer->CommandMemoryUsed += CommandSize;
return Result;
}
internal void
PushRenderPerspective (render_command_buffer* Buffer, s32 ViewWidth, s32 ViewHeight, camera Camera)
{
render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
Command->ModelView = GetCameraModelViewMatrix(Camera);
Command->Projection = GetCameraPerspectiveProjectionMatrix(Camera);
Command->ViewWidth;
Command->ViewHeight;
Command->UseDepthBuffer = true;
}
internal void
PushRenderOrthographic (render_command_buffer* Buffer, s32 ViewWidth, s32 ViewHeight)
{
render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
Command->ModelView = m44{
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
r32 a = 2.0f / ViewWidth;
r32 b = 2.0f / ViewHeight;
Command->Projection = m44{
a, 0, 0, 0,
0, b, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1
};
Command->ViewWidth;
Command->ViewHeight;
Command->UseDepthBuffer = false;;
}
internal void
PushRenderClearScreen (render_command_buffer* Buffer)
{
render_command_clear_screen* Command = PushRenderCommand(Buffer, render_command_clear_screen);
}
internal render_quad_batch_constructor
PushRenderQuad2DBatch(render_command_buffer* Buffer, s32 QuadCount)
{
render_quad_batch_constructor Result = {};
render_batch_command_quad_2d* Command = PushRenderCommand(Buffer, render_batch_command_quad_2d);
Command->QuadCount = QuadCount;
Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, (u8*)(Command + 1));
return Result;
}
internal void
PushRenderQuad2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color)
{
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
PushQuad2DOnBatch(&Batch, Min, Max, Color);
}
internal void
PushRenderLine2D (render_command_buffer* Buffer, v2 P0, v2 P1, r32 Thickness, v4 Color)
{
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
PushLine2DOnBatch(&Batch, P0, P1, Thickness, Color);
}
internal render_quad_batch_constructor
PushRenderQuad3DBatch(render_command_buffer* Buffer, s32 QuadCount)
{
render_quad_batch_constructor Result = {};
render_batch_command_quad_3d* Command = PushRenderCommand(Buffer, render_batch_command_quad_3d);
Command->QuadCount = QuadCount;
Command->DataSize = PushQuad3DBatch(Buffer, &Result, QuadCount, (u8*)(Command + 1));
return Result;
}
internal void
PushRenderQuad3D (render_command_buffer* Buffer, v4 A, v4 B, v4 C, v4 D, v4 Color)
{
render_quad_batch_constructor Batch = PushRenderQuad3DBatch(Buffer, 1);
PushQuad3DOnBatch(&Batch, A, B, C, D, Color);
}
internal void
PushRenderCameraFacingQuad (render_command_buffer* Buffer, v4 Center, v2 Dimensions, v4 Color)
{
// TODO(Peter): Turn this into an actual camera facing quad
v4 A = v4{Center.x - Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w};
v4 B = v4{Center.x + Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w};
v4 C = v4{Center.x + Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w};
v4 D = v4{Center.x - Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w};
PushRenderQuad3D(Buffer, A, B, C, D, Color);
}
internal render_quad_batch_constructor
PushRenderTexture2DBatch(render_command_buffer* Buffer, s32 QuadCount,
render_texture Texture)
{
render_quad_batch_constructor Result = {};
render_batch_command_texture_2d* Command = PushRenderCommand(Buffer, render_batch_command_texture_2d);
Command->QuadCount = QuadCount;
Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, (u8*)(Command + 1));
Command->Texture = Texture;
return Result;
}
internal void
PushRenderTexture2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color,
v2 UVMin, v2 UVMax,
render_texture* Texture)
{
render_quad_batch_constructor Batch = PushRenderTexture2DBatch(Buffer, 1, *Texture);
PushQuad2DOnBatch(&Batch, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y},
UVMin, UVMax, Color);
}
internal void
PushRenderBoundingBox2D (render_command_buffer* Buffer, v2 Min, v2 Max, r32 Thickness, v4 Color)
{
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 4);
PushQuad2DOnBatch(&Batch, Min, v2{Min.x + Thickness, Max.y}, Color);
PushQuad2DOnBatch(&Batch, v2{Min.x, Max.y - Thickness}, Max, Color);
PushQuad2DOnBatch(&Batch, v2{Max.x - Thickness, Min.y}, Max, Color);
PushQuad2DOnBatch(&Batch, Min, v2{Max.x, Min.y + Thickness}, Color);
}

606
foldhaus_sacn.h Normal file
View File

@ -0,0 +1,606 @@
#define NETWORKINTID_INVALID -1
#define DEFAULT_STREAMING_ACN_PORT 5568
#define IP_ADDRESS_BYTES 16
#define STARTCODE_DMX 0
/*
* a description of the address space being used
*/
#define PREAMBLE_SIZE_ADDR 0
#define POSTAMBLE_SIZE_ADDR 2
#define ACN_IDENTIFIER_ADDR 4
#define ROOT_FLAGS_AND_LENGTH_ADDR 16
#define ROOT_VECTOR_ADDR 18
#define CID_ADDR 22
#define FRAMING_FLAGS_AND_LENGTH_ADDR 38
#define FRAMING_VECTOR_ADDR 40
#define SOURCE_NAME_ADDR 44
#define PRIORITY_ADDR 108
#define RESERVED_ADDR 109
#define SEQ_NUM_ADDR 111
#define OPTIONS_ADDR 112
#define UNIVERSE_ADDR 113
#define DMP_FLAGS_AND_LENGTH_ADDR 115
#define DMP_VECTOR_ADDR 117
#define DMP_ADDRESS_AND_DATA_ADDR 118
#define FIRST_PROPERTY_ADDRESS_ADDR 119
#define ADDRESS_INC_ADDR 121
#define PROP_COUNT_ADDR 123
#define START_CODE_ADDR 125
#define PROP_VALUES_ADDR (START_CODE_ADDR + 1)
/*
* common sizes
*/
#define STREAM_HEADER_SIZE 126
#define STREAM_BODY_SIZE 512
#define SOURCE_NAME_SIZE 64
#define RLP_PREAMBLE_SIZE 16
#define RLP_POSTAMBLE_SIZE 0
#define ACN_IDENTIFIER_SIZE 12
/*
* data definitions
*/
#define ACN_IDENTIFIER "ASC-E1.17\0\0\0"
#define ROOT_VECTOR 4
#define FRAMING_VECTOR 2
#define DMP_VECTOR 2
#define ADDRESS_AND_DATA_FORMAT 0xa1
#define ADDRESS_INC 1
#define DMP_FIRST_PROPERTY_ADDRESS_FORCE 0
#define RESERVED_VALUE 0
//for support of the early draft
#define DRAFT_STREAM_HEADER_SIZE 90
#define DRAFT_SOURCE_NAME_SIZE 32
//for support of the early draft
#define DRAFT_ROOT_VECTOR 3
const u32 VHD_MAXFLAGBYTES = 7; //The maximum amount of bytes used to pack the flags, len, and vector
const u32 VHD_MAXLEN = 0x0fffff; //The maximum packet length is 20 bytes long
const u32 VHD_MAXMINLENGTH = 4095; //The highest length that will fit in the "smallest" length pack
//Defines for the VHD flags
const u8 VHD_L_FLAG = 0x80;
const u8 VHD_V_FLAG = 0x40;
const u8 VHD_H_FLAG = 0x20;
const u8 VHD_D_FLAG = 0x10;
#define CID_Bytes 16
struct cid
{
u8 Bytes[CID_Bytes];
};
struct sacn_universe
{
s16 Universe;
u8* StartPositionInSendBuffer;
s32 SizeInSendBuffer;
s32 OffsetInSendBuffer;
s32 BeginPixelCopyFromOffset;
platform_network_address_handle SendAddress;
};
struct sacn_send_buffer
{
u8* Memory;
s32 Size;
sacn_send_buffer* Next;
};
struct sacn_universe_buffer
{
sacn_universe* Universes;
s32 Used;
s32 Max;
sacn_universe_buffer* Next;
};
struct streaming_acn
{
memory_arena Memory;
// These get created and freed together
sacn_universe_buffer* UniverseBuffer;
sacn_send_buffer* SendBuffer;
platform_socket_handle SendSocket;
cid CID;
s32 SequenceIterator;
};
// SACN Data Header Functions
internal void InitStreamHeader (u8* Buffer, s32 BufferSize, u16 SlotCount, u8 StartCode, u16 Universe, u8 Priority, u16 Reserved, u8 Options, const char* SourceName, cid CID);
internal void SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft);
internal void VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData);
internal u8* VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength);
internal cid StringToCID_ (const char* String);
#define CalculateSendBufferSize(UniverseCount) ((UniverseCount * (STREAM_HEADER_SIZE + STREAM_BODY_SIZE)) + sizeof(sacn_send_buffer))
#define CalculateUniverseBufferSize(UniverseCount) ((UniverseCount * sizeof(sacn_universe)) + sizeof(sacn_universe_buffer))
// Utility
struct sacn_pixel
{
u8 R;
u8 G;
u8 B;
};
//
internal sacn_universe*
SACNGetUniverse (s32 UniverseNumber, streaming_acn* SACN)
{
sacn_universe* Result = 0;
sacn_universe_buffer* Header = SACN->UniverseBuffer;
while (Header)
{
sacn_universe* Cursor = Header->Universes;
for (s32 i = 0; i < Header->Used; i++)
{
if (Cursor->Universe == UniverseNumber)
{
Result = Cursor;
break;
}
Cursor++;
}
Header = Header->Next;
}
return Result;
}
internal void
SACNPushSendBufferOnList (sacn_send_buffer* ListHead, sacn_send_buffer* NewBuffer)
{
if (ListHead->Next)
{
SACNPushSendBufferOnList(ListHead->Next, NewBuffer);
}
else
{
ListHead->Next = NewBuffer;
}
}
internal sacn_send_buffer*
SACNRemoveSendBufferFromList (sacn_send_buffer* List, sacn_send_buffer* Entry)
{
sacn_send_buffer* ListHead = 0;
if (List != Entry && List->Next)
{
ListHead = SACNRemoveSendBufferFromList(List->Next, Entry);
}
else if (List == Entry)
{
ListHead = Entry->Next;
}
else
{
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
InvalidCodePath;
}
return ListHead;
}
internal void
SACNPushUniverseBufferOnList (sacn_universe_buffer* ListHead, sacn_universe_buffer* NewBuffer)
{
if (ListHead->Next)
{
SACNPushUniverseBufferOnList(ListHead->Next, NewBuffer);
}
else
{
ListHead->Next = NewBuffer;
}
}
internal sacn_universe_buffer*
SACNRemoveUniverseBufferFromList (sacn_universe_buffer* List, sacn_universe_buffer* Entry)
{
sacn_universe_buffer* ListHead = 0;
if (List != Entry && List->Next)
{
ListHead = SACNRemoveUniverseBufferFromList(List->Next, Entry);
}
else if (List == Entry)
{
ListHead = Entry->Next;
}
else
{
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
InvalidCodePath;
}
return ListHead;
}
struct sacn_add_universes_result
{
sacn_send_buffer* NewSendBuffer;
sacn_universe_buffer* NewUniverseBuffer;
};
internal sacn_add_universes_result
SACNAddUniverses(s32* Universes, s32 UniversesLength, streaming_acn* SACN, context Context)
{
sacn_add_universes_result Result = {};
// Determine which universes are already registered and not to be readded.
// NOTE(Peter): This might create funky behaviour if two sculptures start sending data to the same universe
// but I'm not sure its incorrect behavior. I think, eventually, we will want to spit out a report from
// this function that says what universes were duplicated. We might want to display this information to the user
// in a way that they don't have to exit out of every single time they load the software. Not sure
s32 UniversesToAdd = 0;
for (s32 i = 0; i < UniversesLength; i++)
{
sacn_universe* UniverseExists = SACNGetUniverse(Universes[i], SACN);
if (UniverseExists)
{
Universes[i] = -1;
}
else
{
UniversesToAdd++;
}
}
// Push On New Send and Universe Buffers
s32 SendBufferSize = CalculateSendBufferSize(UniversesToAdd);
u8* SendBufferMemory = PushArray(&SACN->Memory, u8, SendBufferSize);
sacn_send_buffer* SendBufferHeader = (sacn_send_buffer*)SendBufferMemory;
SendBufferHeader->Memory = (u8*)(SendBufferHeader + 1);
SendBufferHeader->Size = SendBufferSize - sizeof(sacn_send_buffer);
SendBufferHeader->Next = 0;
if (SACN->SendBuffer)
{
SACNPushSendBufferOnList(SACN->SendBuffer, SendBufferHeader);
}
else
{
SACN->SendBuffer = SendBufferHeader;
}
s32 UniverseBufferSize = CalculateUniverseBufferSize(UniversesToAdd);
u8* UniverseBufferMemory = PushArray(&SACN->Memory, u8, UniverseBufferSize);
sacn_universe_buffer* UniverseBufferHeader = (sacn_universe_buffer*)UniverseBufferMemory;
UniverseBufferHeader->Universes = (sacn_universe*)(UniverseBufferHeader + 1);
UniverseBufferHeader->Used = 0;
UniverseBufferHeader->Max = UniversesToAdd;
if (SACN->UniverseBuffer)
{
SACNPushUniverseBufferOnList(SACN->UniverseBuffer, UniverseBufferHeader);
}
else
{
SACN->UniverseBuffer = UniverseBufferHeader;
}
// Add each of the valid universes
for (s32 j = 0; j < UniversesLength; j++)
{
if (Universes[j] >= 0)
{
Assert(UniverseBufferHeader->Used < UniverseBufferHeader->Max);
s32 Index = UniverseBufferHeader->Used++;
s32 UniverseID = Universes[j];
UniverseBufferHeader->Universes[Index].Universe = UniverseID;
UniverseBufferHeader->Universes[Index].SizeInSendBuffer = STREAM_HEADER_SIZE + STREAM_BODY_SIZE;
UniverseBufferHeader->Universes[Index].BeginPixelCopyFromOffset = -1;
// Configure how the universe looks into the pixel color buffer
s32 SendBufferOffset = (Index * (STREAM_HEADER_SIZE + STREAM_BODY_SIZE));
u8* SendBufferStartPosition = SendBufferHeader->Memory + SendBufferOffset;
UniverseBufferHeader->Universes[Index].OffsetInSendBuffer = SendBufferOffset;
UniverseBufferHeader->Universes[Index].StartPositionInSendBuffer = SendBufferStartPosition;
// Set up the Send Address
u8 MulticastAddressBuffer[IP_ADDRESS_BYTES];
GSMemSet(MulticastAddressBuffer, 0, IP_ADDRESS_BYTES);
MulticastAddressBuffer[12] = 239;
MulticastAddressBuffer[13] = 255;
PackB2(MulticastAddressBuffer + 14, UniverseID);
u_long V4Address = (u_long)UpackB4(MulticastAddressBuffer + IP_ADDRESS_BYTES - sizeof(u32));
GSMemSet(&UniverseBufferHeader->Universes[Index].SendAddress, 0, sizeof(sockaddr_in));
UniverseBufferHeader->Universes[Index].SendAddress = Context.PlatformGetSendAddress(
AF_INET,
HostToNetU16(DEFAULT_STREAMING_ACN_PORT),
HostToNetU32(V4Address));
#if 0 // Old Net Code
UniverseBufferHeader->Universes[Index].SendAddress.sin_family = AF_INET;
UniverseBufferHeader->Universes[Index].SendAddress.sin_port = HostToNetU16(DEFAULT_STREAMING_ACN_PORT);
UniverseBufferHeader->Universes[Index].SendAddress.sin_addr.s_addr = HostToNetU32(V4Address);
#endif
s32 SlotCount = 512;
InitStreamHeader(UniverseBufferHeader->Universes[Index].StartPositionInSendBuffer,
UniverseBufferHeader->Universes[Index].SizeInSendBuffer,
SlotCount,
STARTCODE_DMX,
UniverseID,
0,
0, // Reserved
0, // Options
"Source 1",
SACN->CID
);
}
}
Result.NewUniverseBuffer = UniverseBufferHeader;
Result.NewSendBuffer= SendBufferHeader;
return Result;
}
internal void
SACNRemoveUniverseAndSendBuffer(streaming_acn* SACN, sacn_universe_buffer* Universes, sacn_send_buffer* SendBuffer)
{
SACN->UniverseBuffer = SACNRemoveUniverseBufferFromList(SACN->UniverseBuffer, Universes);
SACN->SendBuffer = SACNRemoveSendBufferFromList(SACN->SendBuffer, SendBuffer);
}
internal streaming_acn
InitializeSACN (platform_alloc* PlatformAlloc, context Context)
{
streaming_acn SACN = {};
InitMemoryArena(&SACN.Memory, 0, 0, PlatformAlloc);
SACN.SendSocket = Context.PlatformGetSocketHandle(AF_INET, SOCK_DGRAM, 0);
int Multicast_TimeToLive = 20;
int Error = Context.PlatformSetSocketOption(SACN.SendSocket, IPPROTO_IP, IP_MULTICAST_TTL,
(const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
SACN.CID = StringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
SACN.UniverseBuffer = 0;
SACN.SendBuffer = 0;
return SACN;
}
internal void
SACNSendDataToUniverse (streaming_acn* SACN, sacn_universe* Universe, platform_send_to* PlatformSendTo)
{
//DEBUG_TRACK_FUNCTION;
u8* StartPositionInSendBuffer = (u8*)Universe->StartPositionInSendBuffer;
SetStreamHeaderSequence_(StartPositionInSendBuffer, SACN->SequenceIterator, false);
PlatformSendTo(SACN->SendSocket, Universe->SendAddress, (const char*)StartPositionInSendBuffer, Universe->SizeInSendBuffer, 0);
#if 0 // Old Network Code
// TODO(Peter): HUGE NOTE!!!!!!!!
// This needs to be put on a separate thread. The sendto call is really slowing us down.
s32 LengthSent = sendto(SACN->SendSocket, (const char*)StartPositionInSendBuffer, Universe->SizeInSendBuffer,
0, (sockaddr*)(&Universe->SendAddress), sizeof(sockaddr_in));
if (LengthSent == SOCKET_ERROR)
{
s32 LastSocketError = WSAGetLastError();
InvalidCodePath;
}
#endif
}
internal void
SACNCleanup(streaming_acn* SACN, context Context)
{
Context.PlatformCloseSocket(SACN->SendSocket);
}
///////////////////////////////////////////////
//
// SACN Data Header Functions
//
///////////////////////////////////////////////
internal void
InitStreamHeader (u8* Buffer, s32 BufferSize,
u16 SlotCount,
u8 StartCode,
u16 Universe,
u8 Priority,
u16 Reserved,
u8 Options,
const char* SourceName,
cid CID
)
{
u8* Cursor = Buffer;
// Preamble Size
Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE);
Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE);
memcpy(Cursor, ACN_IDENTIFIER, ACN_IDENTIFIER_SIZE);
Cursor += ACN_IDENTIFIER_SIZE;
// TODO(Peter): If you never use this anywhere else, go back and remove the parameters
VHD_PackFlags_(Cursor, false, false, false);
Cursor = VHD_PackLength_(Cursor,
STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount,
false);
// root vector
Cursor = PackB4(Cursor, ROOT_VECTOR);
// CID Pack
for (s32 i = 0; i < CID_Bytes; i++)
{
*Cursor++ = CID.Bytes[i];
}
VHD_PackFlags_(Cursor, false, false, false);
Cursor = VHD_PackLength_(Cursor,
STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount,
false);
// framing vector
Cursor = PackB4(Cursor, FRAMING_VECTOR);
// framing source name
strncpy((char*)Cursor, SourceName, SOURCE_NAME_SIZE);
Cursor[SOURCE_NAME_SIZE - 1] = '\0';
Cursor += SOURCE_NAME_SIZE;
// priority
Cursor = PackB1(Cursor, Priority);
// reserved
Cursor = PackB2(Cursor, Reserved);
// Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data
Cursor = PackB1(Cursor, 0);
// Options
Cursor = PackB1(Cursor, Options);
// Universe
Cursor = PackB2(Cursor, Universe);
VHD_PackFlags_(Cursor, false, false, false);
Cursor = VHD_PackLength_(Cursor,
STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount,
false);
// DMP Vector
Cursor = PackB1(Cursor, DMP_VECTOR);
// DMP Address and data type
Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT);
// DMP first property address
Cursor = PackB2(Cursor, 0);
// DMP Address Increment
Cursor = PackB2(Cursor, ADDRESS_INC);
// Property Value Count -- Includes one byte for start code
Cursor = PackB2(Cursor, SlotCount + 1);
Cursor = PackB1(Cursor, StartCode);
s32 DiffSize = Cursor - Buffer;
if (Cursor - Buffer != STREAM_HEADER_SIZE)
{
InvalidCodePath;
}
}
internal void
SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft)
{
DEBUG_TRACK_FUNCTION;
PackB1(Buffer + SEQ_NUM_ADDR, Sequence);
}
internal void
VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData)
{
u8* Cursor = Buffer;
u8 NewByte = UpackB1(Cursor) & 0x8f;
if (!InheritVec) { NewByte |= VHD_V_FLAG; }
if (!InheritHead) { NewByte |= VHD_H_FLAG; }
if (!InheritData) { NewByte |= VHD_D_FLAG; }
PackB1(Cursor, NewByte);
}
internal u8*
VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength)
{
u8* Cursor = Buffer;
u32 AdjustedLength = Length;
if (IncludeLength)
{
if (Length + 1 > VHD_MAXMINLENGTH)
{
AdjustedLength += 2;
}
else
{
AdjustedLength += 1;
}
}
// Mask out the length bits to keep flags intact
u8 NewByte = UpackB1(Cursor) & 0x70;
if (AdjustedLength > VHD_MAXMINLENGTH)
{
NewByte |= VHD_L_FLAG;
}
u8 PackBuffer[4];
PackB4(PackBuffer, AdjustedLength);
if (AdjustedLength <= VHD_MAXMINLENGTH)
{
NewByte |= (PackBuffer[2] & 0x0f);
Cursor = PackB1(Cursor, NewByte);
Cursor = PackB1(Cursor, PackBuffer[3]);
}
else
{
NewByte |= (PackBuffer[1] & 0x0f);
Cursor = PackB1(Cursor, PackBuffer[2]);
Cursor = PackB1(Cursor, PackBuffer[3]);
}
return Cursor;
}
internal cid
StringToCID_ (const char* String)
{
cid Result = {};
const char* Src = String;
u8* Dest = &Result.Bytes[0];
b32 FirstNibble = true;
while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes))
{
u8 Offset = 0;
if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; }
else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; }
else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; }
if (Offset != 0)
{
if (FirstNibble)
{
*Dest = (u8)(*Src - Offset);
*Dest <<= 4;
FirstNibble = false;
}
else
{
*Dest |= (*Src - Offset);
Dest++;
FirstNibble = true;
}
}
Src++;
}
return Result;
}

33
foldhaus_sacn_view.cpp Normal file
View File

@ -0,0 +1,33 @@
internal void
DrawSACNUniversePixels (render_command_buffer* RenderBuffer, sacn_universe* ToDraw,
v2 TopLeft, v2 Dimension)
{
Assert(ToDraw);
s32 PixelsPerRow = 21;
r32 PixelDim = Dimension.x / PixelsPerRow;
v2 PixelSize = v2{PixelDim, PixelDim};
v2 PixelRegister = TopLeft;
v4 DisplayColor = {0, 0, 0, 1};
s32 PixelsToDraw = ToDraw->SizeInSendBuffer - STREAM_HEADER_SIZE;
render_quad_batch_constructor BatchConstructor = PushRenderQuad2DBatch(RenderBuffer, PixelsToDraw);
u8* ColorCursor = (u8*)ToDraw->StartPositionInSendBuffer + STREAM_HEADER_SIZE;
s32 PixelsDrawn = 0;
for (s32 i = 0; i < PixelsToDraw; i++)
{
PixelRegister.x = TopLeft.x + (PixelsDrawn % PixelsPerRow) * PixelDim;
PixelRegister.y = TopLeft.y - (PixelsDrawn / PixelsPerRow) * PixelDim;
r32 Value = *ColorCursor++ / 255.f;
DisplayColor.r = Value;
DisplayColor.g = Value;
DisplayColor.b = Value;
PushQuad2DOnBatch(&BatchConstructor, PixelRegister, PixelRegister + PixelSize, DisplayColor);
++PixelsDrawn;
}
}

View File

@ -0,0 +1,367 @@
#define DEBUG
#define DEBUG_TRACK_SCOPE(name)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gs/gs_language.h"
#include "gs/gs_string.h"
#include "../meta/gs_meta_lexer.h"
#include "gs/gs_vector.h"
#define STRING_BUFFER_SIZE 512
struct string_buffer
{
char* Memory;
s32 Size;
string_buffer* Next;
};
struct string_writer
{
char* Cursor;
s32 UsedInString;
string_buffer* Buffer;
};
internal string_buffer*
GrowStringBuffer (string_buffer* Buffer)
{
string_buffer* Result;
if (Buffer->Next)
{
Result = GrowStringBuffer(Buffer->Next);
}
else
{
Result = (string_buffer*)malloc(sizeof(string_buffer));
Result->Memory = (char*)malloc(sizeof(char) * STRING_BUFFER_SIZE);
memset(Result->Memory, 0, STRING_BUFFER_SIZE);
Result->Size = STRING_BUFFER_SIZE;
Result->Next = 0;
Buffer->Next = Result;
}
return Result;
}
internal void
WriteString(string_writer* Writer, char* String, s32 Length)
{
char* Src = String;
char* Dst = Writer->Cursor;
s32 LengthWritten = 0;
while (*Src && Writer->UsedInString < Writer->Buffer->Size &&LengthWritten < Length)
{
LengthWritten++;
*Dst++ = *Src++;
Writer->UsedInString++;
}
Writer->Cursor = Dst;
if (*Src && Writer->UsedInString == Writer->Buffer->Size)
{
*(Dst - 1) = 0; // Null terminate the buffer
Writer->Buffer = GrowStringBuffer(Writer->Buffer);
Writer->Cursor = Writer->Buffer->Memory;
Writer->UsedInString = 0;
WriteString(Writer, (Src - 1), (Length - LengthWritten) + 1);
}
}
struct control_box_pairs
{
s32 Start;
s32 End;
};
struct extra_strips
{
s32 BoxID;
v3 Start;
v3 End;
};
struct control_box
{
s32 ID;
s32 Neighbors[6];
r32 X;
r32 Y;
r32 Z;
char* Address;
};
int main(int ArgCount, char* Args[])
{
FILE* OldFilePtr = fopen("F:/data/radia_old.fold", "r");
if (!OldFilePtr)
{
InvalidCodePath;
}
fseek(OldFilePtr, 0, SEEK_END);
s32 OldFileSize = ftell(OldFilePtr);
fseek(OldFilePtr, 0, SEEK_SET);
char* OldFile = (char*)malloc(sizeof(char) * OldFileSize);
fread(OldFile, 1, OldFileSize, OldFilePtr);
fclose(OldFilePtr);
s32 ControlBoxPairsUsed = 0;
control_box_pairs* ControlBoxPairs = (control_box_pairs*)malloc(sizeof(control_box_pairs) * 512);
s32 ControlBoxesUsed = 0;
control_box* ControlBoxes = (control_box*)malloc(sizeof(control_box) * 64);
s32 ExtraStripsUsed = 0;
extra_strips* ExtraStrips = (extra_strips*)malloc(sizeof(extra_strips) * (42 * 4));
control_box_pairs* NextControlBoxPair = &ControlBoxPairs[0];
control_box* NextControlBox = &ControlBoxes[0];
extra_strips* NextStrip = &ExtraStrips[0];
tokenizer Tokenizer = {};
Tokenizer.At = OldFile;
while(*Tokenizer.At)
{
// Parse a Control Box
memset(NextControlBox->Neighbors, -1, 6);
s32 NeighborsAdded = 0;
control_box_pairs* StartPair = NextControlBoxPair;
s32 PairsCount = 0;
if (StringsEqual(Tokenizer.At, "EOF"))
{
break;
}
EatToCharacterInclusive(&Tokenizer, '{');
EatWhitespace(&Tokenizer);
Assert(StringsEqual(Tokenizer.At, "neighbors: ["));
Tokenizer.At += StringLength("neighbors: [");
// Parse Neighbors
while(*Tokenizer.At && *Tokenizer.At != ']')
{
s32 NeighborIndex = ParseSignedInt(Tokenizer.At);
NextControlBox->Neighbors[NeighborsAdded++] = NeighborIndex;
NextControlBoxPair->End = NeighborIndex;
NextControlBoxPair++;
PairsCount++;
ControlBoxPairsUsed++;
EatNumber(&Tokenizer);
if (*Tokenizer.At == ']')
{
Tokenizer.At += 2; // Eat past "];"
break;
}
else
{
EatToCharacterInclusive(&Tokenizer, ',');
EatWhitespace(&Tokenizer);
}
}
EatWhitespace(&Tokenizer);
//Parse IP
Assert(StringsEqual(Tokenizer.At, "ip: "));
Tokenizer.At += StringLength("ip: ");
NextControlBox->Address = (char*)malloc(sizeof(char) * 13);
memcpy(NextControlBox->Address, Tokenizer.At, 13);
Tokenizer.At += 13;
Tokenizer.At++; // Eat past ";"
// Parse X
EatWhitespace(&Tokenizer);
Assert(StringsEqual(Tokenizer.At, "x: "));
Tokenizer.At += StringLength("x: ");
NextControlBox->X = ParseFloat(Tokenizer.At);
EatToCharacterInclusive(&Tokenizer, ';');
// Parse Y
EatWhitespace(&Tokenizer);
Assert(StringsEqual(Tokenizer.At, "y: "));
Tokenizer.At += StringLength("y: ");
NextControlBox->Y = ParseFloat(Tokenizer.At);
EatToCharacterInclusive(&Tokenizer, ';');
// Parse Z
EatWhitespace(&Tokenizer);
Assert(StringsEqual(Tokenizer.At, "z: "));
Tokenizer.At += StringLength("z: ");
NextControlBox->Z = ParseFloat(Tokenizer.At);
EatToCharacterInclusive(&Tokenizer, ';');
// Parse ID
EatWhitespace(&Tokenizer);
Assert(StringsEqual(Tokenizer.At, "id: "));
Tokenizer.At += StringLength("id: ");
NextControlBox->ID = ParseSignedInt(Tokenizer.At);
EatToCharacterInclusive(&Tokenizer, ';');
control_box_pairs* PairCursor = StartPair;
for(s32 i = 0; i < PairsCount; i++)
{
PairCursor->Start = NextControlBox->ID;
PairCursor++;
}
NextControlBox++;
ControlBoxesUsed++;
EatToCharacterInclusive(&Tokenizer, ';');
EatWhitespace(&Tokenizer);
}
// Add Spikes
#define SPIKE_LEDS 346
for (s32 sp = 0; sp < ControlBoxesUsed; sp++)
{
control_box* Box = &ControlBoxes[sp];
control_box* NeighborA = &ControlBoxes[Box->Neighbors[0]];
control_box* NeighborB = &ControlBoxes[Box->Neighbors[1]];
v3 SpikeCenter = v3{Box->X, Box->Y, Box->Z};
v3 StripPitch = Normalize(SpikeCenter) * ((2.f/8.f) / SPIKE_LEDS);
v3 ToNA = Normalize(v3{NeighborA->X, NeighborA->Y, NeighborA->Z} - SpikeCenter);
v3 ToNB = Normalize(v3{NeighborB->X, NeighborB->Y, NeighborB->Z} - SpikeCenter);
v3 StripAOutStart = SpikeCenter + (ToNA * .01f);
v3 StripAOutEnd = StripAOutStart + (StripPitch * SPIKE_LEDS);
v3 StripBOutStart = SpikeCenter + (ToNB * .01f);
v3 StripBOutEnd = StripBOutStart + (StripPitch * SPIKE_LEDS);
v3 StripAInStart = StripAOutEnd - (ToNA * .02f);
v3 StripAInEnd = StripAOutStart - (ToNA * .02f);
v3 StripBInStart = StripBOutEnd - (ToNA * .02f);
v3 StripBInEnd = StripBOutStart - (ToNA * .02f);
NextStrip->BoxID = Box->ID;
NextStrip->Start = StripAOutStart;
NextStrip->End = StripAOutEnd;
NextStrip++;
ExtraStripsUsed++;
NextStrip->BoxID = Box->ID;
NextStrip->Start = StripAInStart;
NextStrip->End = StripAInEnd;
NextStrip++;
ExtraStripsUsed++;
NextStrip->BoxID = Box->ID;
NextStrip->Start = StripBOutStart;
NextStrip->End = StripBOutEnd;
NextStrip++;
ExtraStripsUsed++;
NextStrip->BoxID = Box->ID;
NextStrip->Start = StripBInStart;
NextStrip->End = StripBInEnd;
NextStrip++;
ExtraStripsUsed++;
}
string_buffer OutputFileBuffer = {};
OutputFileBuffer.Memory = (char*)malloc(sizeof(char) * STRING_BUFFER_SIZE);
OutputFileBuffer.Size = STRING_BUFFER_SIZE;
OutputFileBuffer.Next = 0;
string_writer RefWriter = {};
RefWriter.Cursor = OutputFileBuffer.Memory;
RefWriter.UsedInString = 0;
RefWriter.Buffer = &OutputFileBuffer;
string_writer* Writer = &RefWriter;
char StringBuffer[512];
s32 Len = 0;
Len = sprintf_s(StringBuffer, 512, "control_box_count %d\n", ControlBoxesUsed);
WriteString(Writer, StringBuffer, Len);
Len = sprintf_s(StringBuffer, 512, "led_strip_count %d\n\n", ControlBoxPairsUsed);
WriteString(Writer, StringBuffer, Len);
for (s32 c = 0; c < ControlBoxesUsed; c++)
{
control_box* Box = ControlBoxes + c;
Len = sprintf_s(StringBuffer, 512,
"control_box { %d, \"%s\", (%f, %f, %f) }\n",
Box->ID, Box->Address,
Box->X, Box->Y, Box->Z);
WriteString(Writer, StringBuffer, Len);
}
WriteString(Writer, "\n", 1);
#define UNIVERSES_PER_BOX 25
s32 UniversesPerBox[64];
for (s32 u = 0; u < 64; u++)
{
UniversesPerBox[u] = UNIVERSES_PER_BOX * u;
}
char LEDStripFormatString[] = "led_strip { %d, %d, %d, INTERPOLATE_POINTS, (%f, %f, %f), (%f, %f, %f), 144 } \n";
for (s32 s = 0; s < ControlBoxPairsUsed; s++)
{
control_box_pairs* Pair = ControlBoxPairs + s;
s32 Universe = UniversesPerBox[Pair->Start];
UniversesPerBox[Pair->Start]++;
r32 StartX = ControlBoxes[Pair->Start].X;
r32 StartY = ControlBoxes[Pair->Start].Y;
r32 StartZ = ControlBoxes[Pair->Start].Z;
r32 EndX = ControlBoxes[Pair->End].X;
r32 EndY = ControlBoxes[Pair->End].Y;
r32 EndZ = ControlBoxes[Pair->End].Z;
Len = sprintf_s(StringBuffer, 512,
LEDStripFormatString,
Pair->Start, Universe, 0,
StartX, StartY, StartZ,
EndX, EndY, EndZ);
WriteString(Writer, StringBuffer, Len);
}
WriteString(Writer, "\n", 1);
for (s32 sp = 0; sp < ExtraStripsUsed; sp++)
{
extra_strips* Strip = ExtraStrips + sp;
s32 Universe = UniversesPerBox[Strip->BoxID];
UniversesPerBox[Strip->BoxID]++;
Len = sprintf_s(StringBuffer, 512,
LEDStripFormatString,
Strip->BoxID, Universe, 0,
Strip->Start.x, Strip->Start.y, Strip->Start.z,
Strip->End.x, Strip->End.y, Strip->End.z);
WriteString(Writer, StringBuffer, Len);
}
WriteString(Writer, "END_OF_ASSEMBLY_FILE", StringLength("END_OF_ASSEMBLY_FILE"));
*Writer->Cursor = 0;
FILE* OutputFile = fopen("F:/data/radialumia.fold", "w");
string_buffer* BufferCursor = &OutputFileBuffer;
while(BufferCursor)
{
fprintf(OutputFile, BufferCursor->Memory);
BufferCursor = BufferCursor->Next;
}
fclose(OutputFile);
return 0;
}

View File

@ -0,0 +1,68 @@
enum node_type
{
NodeType_OutputNode,
NodeType_MultiplyNodeProc,
NodeType_AddNodeProc,
NodeType_FloatValueProc,
NodeType_SolidColorProc,
NodeType_MultiplyPatterns,
NodeType_VerticalColorFadeProc,
NodeType_Count,
};
node_struct_member MemberList_multiply_data[] = {
{ MemberType_r32, "A", (u64)&((multiply_data*)0)->A, IsInputMember },
{ MemberType_r32, "B", (u64)&((multiply_data*)0)->B, IsInputMember },
{ MemberType_r32, "Result", (u64)&((multiply_data*)0)->Result, IsOutputMember},
};
node_struct_member MemberList_add_data[] = {
{ MemberType_v4, "A", (u64)&((add_data*)0)->A, IsInputMember },
{ MemberType_v4, "B", (u64)&((add_data*)0)->B, IsInputMember },
{ MemberType_v4, "Result", (u64)&((add_data*)0)->Result, IsOutputMember},
};
node_struct_member MemberList_float_value_data[] = {
{ MemberType_r32, "Value", (u64)&((float_value_data*)0)->Value, IsInputMember },
{ MemberType_r32, "Result", (u64)&((float_value_data*)0)->Result, IsOutputMember},
};
node_struct_member MemberList_solid_color_data[] = {
{ MemberType_v4, "Color", (u64)&((solid_color_data*)0)->Color, IsInputMember },
{ MemberType_NODE_COLOR_BUFFER, "LEDs", (u64)&((solid_color_data*)0)->LEDs, IsInputMember | IsOutputMember},
};
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},
};
node_struct_member MemberList_vertical_color_fade_data[] = {
{ MemberType_v4, "Color", (u64)&((vertical_color_fade_data*)0)->Color, IsInputMember },
{ MemberType_r32, "Min", (u64)&((vertical_color_fade_data*)0)->Min, IsInputMember },
{ MemberType_r32, "Max", (u64)&((vertical_color_fade_data*)0)->Max, IsInputMember },
};
node_specification NodeSpecifications[] = {
{ NodeType_MultiplyNodeProc, "MultiplyNodeProc", 16, MemberList_multiply_data, 12, 3, false},
{ NodeType_AddNodeProc, "AddNodeProc", 11, MemberList_add_data, 48, 3, false},
{ NodeType_FloatValueProc, "FloatValueProc", 14, MemberList_float_value_data, 8, 2, false},
{ NodeType_SolidColorProc, "SolidColorProc", 14, MemberList_solid_color_data, 36, 2, false},
{ NodeType_MultiplyPatterns, "MultiplyPatterns", 16, MemberList_multiply_patterns_data, 60, 3, false},
{ NodeType_VerticalColorFadeProc, "VerticalColorFadeProc", 21, MemberList_vertical_color_fade_data, 24, 3, true},
};
s32 NodeSpecificationsCount = 6;
internal void CallNodeProc(interface_node* Node, u8* Data, led* LEDs, sacn_pixel* Colors, s32 LEDCount)
{
switch (Node->Type)
{
case NodeType_MultiplyNodeProc: { MultiplyNodeProc((multiply_data*)Data); } break;
case NodeType_AddNodeProc: { AddNodeProc((add_data*)Data); } break;
case NodeType_FloatValueProc: { FloatValueProc((float_value_data*)Data); } break;
case NodeType_SolidColorProc: { SolidColorProc((solid_color_data*)Data); } break;
case NodeType_MultiplyPatterns: { MultiplyPatterns((multiply_patterns_data*)Data); } break;
case NodeType_VerticalColorFadeProc: { VerticalColorFadeProc((vertical_color_fade_data*)Data, LEDs, Colors, LEDCount); } break;
}
}

View File

@ -0,0 +1,22 @@
#define GENERATED_NODE_TYPES \
NodeType_MultiplyNodeProc, \
NodeType_AddNodeProc, \
NodeType_FloatValueProc, \
NodeType_SolidColorProc, \
NodeType_VerticalColorFadeProc
#define GENERATED_NODE_SPECS \
{ NodeType_MultiplyNodeProc, "MultiplyNodeProc", 16, MemberList_multiply_data, 12, 3, 2, 1, false}, \
{ NodeType_AddNodeProc, "AddNodeProc", 11, MemberList_add_data, 48, 3, 2, 1, false}, \
{ NodeType_FloatValueProc, "FloatValueProc", 14, MemberList_float_value_data, 8, 2, 1, 1, false}, \
{ NodeType_SolidColorProc, "SolidColorProc", 14, MemberList_solid_color_data, 16, 1, 1, 0, true}, \
{ NodeType_VerticalColorFadeProc, "VerticalColorFadeProc", 21, MemberList_vertical_color_fade_data, 24, 3, 3, 0, true}
#define GENERATED_NODE_SPECS_COUNT 5
#define EVALUATE_GENERATED_NODES \
case NodeType_MultiplyNodeProc: { MultiplyNodeProc((multiply_data*)NodeData); } break; \
case NodeType_AddNodeProc: { AddNodeProc((add_data*)NodeData); } break; \
case NodeType_FloatValueProc: { FloatValueProc((float_value_data*)NodeData); } break; \
case NodeType_SolidColorProc: { SolidColorProc((solid_color_data*)NodeData, LEDs, Colors, LEDCount); } break; \
case NodeType_VerticalColorFadeProc: { VerticalColorFadeProc((vertical_color_fade_data*)NodeData, LEDs, Colors, LEDCount); } break; \

6
gs_font.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef GS_FONT_H
#define GS_FONT_H
#endif

4
gs_input.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef GS_INPUT_H
#define GS_INPUT_H
#endif

400
gs_language.h Normal file
View File

@ -0,0 +1,400 @@
#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
#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 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
gs_memory.h Normal file
View File

@ -0,0 +1,461 @@
#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

171
gs_platform.h Normal file
View File

@ -0,0 +1,171 @@
#ifndef GS_PLATFORM_H
struct platform_memory_result
{
u8* Base;
s32 Size;
s32 Error;
};
#define PLATFORM_MEMORY_NO_ERROR 0
#define PLATFORM_ALLOC(name) platform_memory_result name(s32 Size)
typedef PLATFORM_ALLOC(platform_alloc);
#define PLATFORM_FREE(name) b32 name(u8* Base, s32 Size)
typedef PLATFORM_FREE(platform_free);
#define PLATFORM_READ_ENTIRE_FILE(name) platform_memory_result name(char* Path)
typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file);
#define PLATFORM_WRITE_ENTIRE_FILE(name) b32 name(char* Path, u8* Contents, s32 Size)
typedef PLATFORM_WRITE_ENTIRE_FILE(platform_write_entire_file);
#define PLATFORM_GET_FILE_PATH(name)
typedef PLATFORM_GET_FILE_PATH(platform_get_file_path);
#define PLATFORM_GET_GPU_TEXTURE_HANDLE(name)
typedef PLATFORM_GET_GPU_TEXTURE_HANDLE(platform_get_gpu_texture_handle);
#define PLATFORM_GET_SOCKET_HANDLE(name)
typedef PLATFORM_GET_SOCKET_HANDLE(platform_get_socket_handle);
#define PLATFORM_GET_SEND_ADDRESS(name)
typedef PLATFORM_GET_SEND_ADDRESS(platform_get_send_address);
#define PLATFORM_SET_SOCKET_OPTION(name)
typedef PLATFORM_SET_SOCKET_OPTION(platform_set_socket_option);
#define PLATFORM_SEND_TO(name)
typedef PLATFORM_SEND_TO(platform_send_to);
#define PLATFORM_CLOSE_SOCKET(name)
typedef PLATFORM_CLOSE_SOCKET(platform_close_socket);
enum key_code
{
KeyCode_Invalid,
KeyCode_Esc,
KeyCode_Space,
KeyCode_Tab,
KeyCode_CapsLock,
KeyCode_LeftShift, KeyCode_RightShift,
KeyCode_LeftCtrl, KeyCode_RightCtrl,
KeyCode_Fn,
KeyCode_Alt,
KeyCode_PageUp, KeyCode_PageDown,
KeyCode_Backspace, KeyCode_Delete,
KeyCode_Enter,
// Function Keys
KeyCode_F0, KeyCode_F1, KeyCode_F2, KeyCode_F3, KeyCode_F4, KeyCode_F5, KeyCode_F6, KeyCode_F7,
KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12,
// Letters
KeyCode_a, KeyCode_b, KeyCode_c, KeyCode_d, KeyCode_e, KeyCode_f, KeyCode_g, KeyCode_h,
KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p,
KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x,
KeyCode_y, KeyCode_z,
KeyCode_A, KeyCode_B, KeyCode_C, KeyCode_D, KeyCode_E, KeyCode_F, KeyCode_G, KeyCode_H,
KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P,
KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X,
KeyCode_Y, KeyCode_Z,
// Numbers
KeyCode_0, KeyCode_1, KeyCode_2, KeyCode_3, KeyCode_4, KeyCode_5, KeyCode_6, KeyCode_7,
KeyCode_8, KeyCode_9,
KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5,
KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9,
// Symbols
KeyCode_Bang, KeyCode_At, KeyCode_Pound, KeyCode_Dollar, KeyCode_Percent, KeyCode_Carrot,
KeyCode_Ampersand, KeyCode_Star, KeyCode_LeftParen, KeyCode_RightParen, KeyCode_Minus, KeyCode_Plus,
KeyCode_Equals, KeyCode_Underscore, KeyCode_LeftBrace, KeyCode_RightBrace, KeyCode_LeftBracket,
KeyCode_RightBracket, KeyCode_Colon, KeyCode_SemiColon, KeyCode_SingleQuote, KeyCode_DoubleQuote,
KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period,
KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote,
// Arrows
KeyCode_UpArrow,
KeyCode_DownArrow,
KeyCode_LeftArrow,
KeyCode_RightArrow,
// Mouse
// NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions
KeyCode_MouseLeftButton,
KeyCode_MouseMiddleButton,
KeyCode_MouseRightButton,
KeyCode_Count,
};
enum modifier_flags
{
Modifier_Shift = 1 << 0,
Modifier_Ctrl = 1 << 1,
Modifier_Alt = 1 << 2,
Modifier_Sys = 1 << 3, // NOTE(Peter): this is the windows key
};
#define INPUT_FRAME_STRING_LENGTH 32
struct input_frame
{
b32 KeysDown[(int)KeyCode_Count];
s32 StringInputUsed;
char StringInput[INPUT_FRAME_STRING_LENGTH];
};
struct input
{
input_frame Frames[2];
input_frame* New;
input_frame* Old;
};
internal void InitializeInput (input* Input);
internal void SwapInputBuffers (input* Input);
internal void
InitializeInput (input* Input)
{
*(Input) = {};
Input->New = &Input->Frames[0];
Input->Old = &Input->Frames[1];
}
internal void
SwapInputBuffers (input* Input)
{
input_frame* NowOld = Input->New;
Input->New = Input->Old;
Input->Old = NowOld;
for (s32 Key = 0; Key < KeyCode_Count; Key++) { Input->New->KeysDown[Key] = false; }
Input->New->StringInputUsed = 0;
}
internal b32
KeyDown (input Input, key_code Key)
{
return Input.New->KeysDown[Key];
}
internal b32
KeyTransitionedDown (input Input, key_code Key)
{
return Input.New->KeysDown[Key] && !Input.Old->KeysDown[Key];
}
internal b32
KeyTransitionedUp (input Input, key_code Key)
{
return !Input.New->KeysDown[Key] && Input.Old->KeysDown[Key];
}
#define GS_PLATFORM_H
#endif // GS_PLATFORM_H

1709
gs_string.h Normal file

File diff suppressed because it is too large Load Diff

1398
gs_vector_matrix.h Normal file

File diff suppressed because it is too large Load Diff

831
gs_win32.h Normal file
View File

@ -0,0 +1,831 @@
#ifndef GS_WIN32_H
#ifdef DEBUG
#define DEBUG_GET_MESSAGE_NAME(string, message) sprintf(string, message);
#else
#define DEBUG_GET_MESSAGE_NAME(string, message)
#endif
struct win32_state
{
b32 Initialized;
b32 Running;
};
struct win32_window_info
{
char* Name;
char* ClassName;
s32 Width;
s32 Height;
WNDPROC WindowEventsHandler; // If this is left null, Win32HandleWindowsEvents will be used
};
struct win32_opengl_window_info
{
s32 ColorBits;
s32 AlphaBits;
s32 DepthBits;
HGLRC RenderContext;
};
struct win32_window
{
win32_window_info Info;
WNDCLASS Class;
HWND Handle;
HDC DeviceContext;
// TODO(peter): Make this a union?
win32_opengl_window_info OpenGLInfo;
};
struct handle_window_msg_result
{
b32 NeedsUpdate;
#ifdef DEBUG
char MessageType[128];
#endif
};
global_variable win32_state GlobalWin32State;
// Utility
internal s32 Win32StringLength(char* String);
internal s32 Win32ConcatStrings(s32 ALen, char* A, s32 BLen, char* B, s32 DestLen, char* Dest);
// Windowing & Graphics
struct win32_offscreen_buffer
{
u8* Memory;
s32 Width;
s32 Height;
s32 Pitch;
s32 BytesPerPixel;
BITMAPINFO Info;
};
internal void InitializeWin32();
internal win32_window CreateWin32Window (char* WindowName, char* WindowClassName, s32 Width, s32 Height);
LRESULT CALLBACK Win32HandleWindowsEvents (HWND WindowHandle, UINT Msg, WPARAM wParam, LPARAM lParam);
internal handle_window_msg_result HandleWindowsMessage (HWND WindowHandle, MSG Message);
internal void Win32UpdateWindowDimension(win32_window* Window);
internal void Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height);
internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer, win32_window Window);
// Memory
internal platform_memory_result Win32Alloc(s32 Size);
internal b32 Win32Free(u8* Base, s32 Size);
// File IO
internal platform_memory_result ReadEntireFile(char* Path);
internal b32 WriteEntireFile(char* Path, u8* Contents, s32 Size);
internal FILETIME GetFileLastWriteTime(char* Path);
// DLL
struct win32_dll_refresh
{
FILETIME LastWriteTime;
HMODULE DLL;
b32 IsValid;
char SourceDLLPath[MAX_PATH];
char WorkingDLLPath[MAX_PATH];
char LockFilePath[MAX_PATH];
};
struct executable_path
{
char Path[MAX_PATH];
s32 PathLength;
s32 IndexOfLastSlash;
};
internal executable_path GetApplicationPath();
internal b32 LoadApplicationDLL(char* DLLName, win32_dll_refresh* DLLResult);
internal void UnloadApplicationDLL(win32_dll_refresh* DLL);
internal win32_dll_refresh InitializeDLLHotReloading(char* SourceDLLName, char* WorkingDLLFileName, char* LockFileName);
internal b32 HotLoadDLL(win32_dll_refresh* DLL);
///
// Utils
///
internal s32
Win32StringLength(char* String)
{
char* At = String;
while (*At) { At++; };
return At - String;
}
internal s32
Win32ConcatStrings(s32 ALen, char* A, s32 BLen, char* B, s32 DestLen, char* Dest)
{
char* Dst = Dest;
char* AAt = A;
for (s32 a = 0; a < ALen; a++)
{
*Dst++ = *AAt++;
}
char* BAt = B;
for (s32 b = 0; b < BLen; b++)
{
*Dst++ = *BAt++;
}
return Dst - Dest;
}
///
// Windowing
///
internal void
InitializeWin32 ()
{
GlobalWin32State = {};
GlobalWin32State.Running = false;
GlobalWin32State.Initialized = true;
}
internal win32_window
CreateWin32Window (HINSTANCE HInstance, win32_window_info Info)
{
win32_window Result = {};
Result.Info = Info;
Result.Class = {};
Result.Class.style = CS_HREDRAW | CS_VREDRAW;
if (Info.WindowEventsHandler)
{
Result.Class.lpfnWndProc = Info.WindowEventsHandler;
}
else
{
Result.Class.lpfnWndProc = Win32HandleWindowsEvents;
}
Result.Class.hInstance = HInstance;
Result.Class.lpszClassName = Info.ClassName;
if (RegisterClass(&Result.Class))
{
Result.Handle = CreateWindowEx(
0,
Result.Class.lpszClassName,
Info.Name,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
Info.Width,
Info.Height,
0,
0,
HInstance,
0);
Result.DeviceContext = GetDC(Result.Handle);
}
return Result;
};
internal void
CreateOpenGLWindowContext (win32_opengl_window_info Info, win32_window* Window)
{
// Setup pixel format
{
PIXELFORMATDESCRIPTOR PixelFormatDesc = { 0 };
// TODO: Program seems to work perfectly fine without all other params except dwFlags.
// Can we skip other params for the sake of brevity?
PixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
PixelFormatDesc.nVersion = 1;
PixelFormatDesc.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
PixelFormatDesc.iPixelType = PFD_TYPE_RGBA;// TODO(Peter): include this in win32_opengl_window_info?
PixelFormatDesc.cColorBits = Info.ColorBits;
PixelFormatDesc.cAlphaBits = Info.AlphaBits;
PixelFormatDesc.cDepthBits = Info.DepthBits;
PixelFormatDesc.dwLayerMask = PFD_MAIN_PLANE; // TODO(Peter): include this in win32_opengl_window_info?
//
s32 PixelFormat = ChoosePixelFormat(Window->DeviceContext, &PixelFormatDesc);
if (!PixelFormat) { InvalidCodePath; } // TODO: Log: Choose pixel format failed
if (!SetPixelFormat(Window->DeviceContext, PixelFormat, &PixelFormatDesc)) { InvalidCodePath; } // TODO: Log: Set pixel format failed
}
// Create rendering context
{
// TODO: Create "proper" context?
// https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation
Info.RenderContext = wglCreateContext(Window->DeviceContext);
wglMakeCurrent(Window->DeviceContext, Info.RenderContext);
// TODO(Peter): do we want this?
/*
glGetIntegerv(GL_MAJOR_VERSION, );
glGetIntegerv(GL_MINOR_VERSION, );
(char*)glGetString(GL_VENDOR);
(char*)glGetString(GL_RENDERER);
*/
}
Window->OpenGLInfo = Info;
}
struct handle_window_event_result
{
LRESULT Result;
b32 Handled;
};
internal handle_window_event_result
HandleWindowEventUnlessWouldUseDefault (HWND WindowHandle, UINT Msg, WPARAM wParam, LPARAM lParam)
{
handle_window_event_result Result = {};
Result.Handled = false;
switch (Msg)
{
case WM_SIZE:
{
//ResizeDIBSection();
Result.Handled = true;
}break;
case WM_CLOSE:
{
Result.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam);
Result.Handled = true;
}break;
case WM_DESTROY:
{
GlobalWin32State.Running = false;
Result.Handled = true;
}break;
case WM_PAINT:
{
PAINTSTRUCT PaintStruct;
HDC DeviceContext;
b32 PaintResult;
DeviceContext = BeginPaint(WindowHandle, &PaintStruct);
PaintResult = EndPaint(WindowHandle, &PaintStruct);
Result.Handled = true;
}break;
}
return Result;
}
LRESULT CALLBACK
Win32HandleWindowsEvents (
HWND WindowHandle,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)
{
handle_window_event_result EventResult = HandleWindowEventUnlessWouldUseDefault(
WindowHandle,
Msg,
wParam,
lParam);
if (!EventResult.Handled)
{
EventResult.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam);
}
return EventResult.Result;
}
#define WIN32_SHOULD_TRANSLATE_TO_CHAR -1
static int
Win32GetKeyIndex (int Win32VirtualKey, bool NumpadValid, bool TranslateToChar)
{
int Result = WIN32_SHOULD_TRANSLATE_TO_CHAR;
if (Win32VirtualKey == VK_ESCAPE) { Result = (int)KeyCode_Esc; }
if (!TranslateToChar)
{
if (Win32VirtualKey == VK_SPACE) { Result = (int)KeyCode_Space; }
}
if (Win32VirtualKey == VK_CAPITAL) { Result = (int)KeyCode_CapsLock; }
else if (Win32VirtualKey == VK_TAB) { Result = (int)KeyCode_Tab; }
else if (Win32VirtualKey == VK_LSHIFT) { Result = (int)KeyCode_LeftShift; }
else if (Win32VirtualKey == VK_RSHIFT) { Result = (int)KeyCode_RightShift; }
else if (Win32VirtualKey == VK_LCONTROL) { Result = (int)KeyCode_LeftCtrl; }
else if (Win32VirtualKey == VK_RCONTROL) { Result = (int)KeyCode_RightCtrl; }
// TODO(Peter): support the function key?
//else if (Win32VirtualKey == VK_) { Result = (int)KeyCode_Fn; }
else if (Win32VirtualKey == VK_MENU) { Result = (int)KeyCode_Alt; }
else if (Win32VirtualKey == VK_PRIOR) { Result = (int)KeyCode_PageUp; }
else if (Win32VirtualKey == VK_NEXT) { Result = (int)KeyCode_PageDown; }
else if (Win32VirtualKey == VK_BACK) { Result = (int)KeyCode_Backspace; }
else if (Win32VirtualKey == VK_DELETE) { Result = (int)KeyCode_Delete; }
else if (Win32VirtualKey == VK_RETURN) { Result = (int)KeyCode_Enter; }
else if (Win32VirtualKey == VK_F1) { Result = (int)KeyCode_F1; }
else if (Win32VirtualKey == VK_F2) { Result = (int)KeyCode_F2; }
else if (Win32VirtualKey == VK_F3) { Result = (int)KeyCode_F3; }
else if (Win32VirtualKey == VK_F4) { Result = (int)KeyCode_F4; }
else if (Win32VirtualKey == VK_F5) { Result = (int)KeyCode_F5; }
else if (Win32VirtualKey == VK_F6) { Result = (int)KeyCode_F6; }
else if (Win32VirtualKey == VK_F7) { Result = (int)KeyCode_F7; }
else if (Win32VirtualKey == VK_F8) { Result = (int)KeyCode_F8; }
else if (Win32VirtualKey == VK_F9) { Result = (int)KeyCode_F9; }
else if (Win32VirtualKey == VK_F10) { Result = (int)KeyCode_F10; }
else if (Win32VirtualKey == VK_F11) { Result = (int)KeyCode_F11; }
else if (Win32VirtualKey == VK_F12) { Result = (int)KeyCode_F12; }
if (!TranslateToChar)
{
if (Win32VirtualKey == 0x30) { Result = (int)KeyCode_0; }
else if (Win32VirtualKey == 0x31) { Result = (int)KeyCode_1; }
else if (Win32VirtualKey == 0x32) { Result = (int)KeyCode_2; }
else if (Win32VirtualKey == 0x33) { Result = (int)KeyCode_3; }
else if (Win32VirtualKey == 0x34) { Result = (int)KeyCode_4; }
else if (Win32VirtualKey == 0x35) { Result = (int)KeyCode_5; }
else if (Win32VirtualKey == 0x36) { Result = (int)KeyCode_6; }
else if (Win32VirtualKey == 0x37) { Result = (int)KeyCode_7; }
else if (Win32VirtualKey == 0x38) { Result = (int)KeyCode_8; }
else if (Win32VirtualKey == 0x39) { Result = (int)KeyCode_9; }
else if (Win32VirtualKey == 0x41) { Result = (int)KeyCode_A; }
else if (Win32VirtualKey == 0x42) { Result = (int)KeyCode_B; }
else if (Win32VirtualKey == 0x43) { Result = (int)KeyCode_C; }
else if (Win32VirtualKey == 0x44) { Result = (int)KeyCode_D; }
else if (Win32VirtualKey == 0x45) { Result = (int)KeyCode_E; }
else if (Win32VirtualKey == 0x46) { Result = (int)KeyCode_F; }
else if (Win32VirtualKey == 0x47) { Result = (int)KeyCode_G; }
else if (Win32VirtualKey == 0x48) { Result = (int)KeyCode_H; }
else if (Win32VirtualKey == 0x49) { Result = (int)KeyCode_I; }
else if (Win32VirtualKey == 0x4A) { Result = (int)KeyCode_J; }
else if (Win32VirtualKey == 0x4B) { Result = (int)KeyCode_K; }
else if (Win32VirtualKey == 0x4C) { Result = (int)KeyCode_L; }
else if (Win32VirtualKey == 0x4D) { Result = (int)KeyCode_M; }
else if (Win32VirtualKey == 0x4E) { Result = (int)KeyCode_N; }
else if (Win32VirtualKey == 0x4F) { Result = (int)KeyCode_O; }
else if (Win32VirtualKey == 0x50) { Result = (int)KeyCode_P; }
else if (Win32VirtualKey == 0x51) { Result = (int)KeyCode_Q; }
else if (Win32VirtualKey == 0x52) { Result = (int)KeyCode_R; }
else if (Win32VirtualKey == 0x53) { Result = (int)KeyCode_S; }
else if (Win32VirtualKey == 0x54) { Result = (int)KeyCode_T; }
else if (Win32VirtualKey == 0x55) { Result = (int)KeyCode_U; }
else if (Win32VirtualKey == 0x56) { Result = (int)KeyCode_V; }
else if (Win32VirtualKey == 0x57) { Result = (int)KeyCode_W; }
else if (Win32VirtualKey == 0x58) { Result = (int)KeyCode_X; }
else if (Win32VirtualKey == 0x59) { Result = (int)KeyCode_Y; }
else if (Win32VirtualKey == 0x5A) { Result = (int)KeyCode_Z; }
}
if (NumpadValid)
{
if (Win32VirtualKey == VK_NUMPAD0) { Result = (int)KeyCode_Num0; }
else if (Win32VirtualKey == VK_NUMPAD1) { Result = (int)KeyCode_Num1; }
else if (Win32VirtualKey == VK_NUMPAD2) { Result = (int)KeyCode_Num2; }
else if (Win32VirtualKey == VK_NUMPAD3) { Result = (int)KeyCode_Num3; }
else if (Win32VirtualKey == VK_NUMPAD4) { Result = (int)KeyCode_Num4; }
else if (Win32VirtualKey == VK_NUMPAD5) { Result = (int)KeyCode_Num5; }
else if (Win32VirtualKey == VK_NUMPAD6) { Result = (int)KeyCode_Num6; }
else if (Win32VirtualKey == VK_NUMPAD7) { Result = (int)KeyCode_Num7; }
else if (Win32VirtualKey == VK_NUMPAD8) { Result = (int)KeyCode_Num8; }
else if (Win32VirtualKey == VK_NUMPAD9) { Result = (int)KeyCode_Num9; }
}
if (Win32VirtualKey == VK_UP) { Result = (int)KeyCode_UpArrow; }
else if (Win32VirtualKey == VK_DOWN) { Result = (int)KeyCode_DownArrow; }
else if (Win32VirtualKey == VK_LEFT) { Result = (int)KeyCode_LeftArrow; }
else if (Win32VirtualKey == VK_RIGHT) { Result = (int)KeyCode_RightArrow; }
return Result;
}
internal handle_window_msg_result
HandleWindowsMessage (
HWND WindowHandle,
MSG Message)
{
handle_window_msg_result Result = {};
Result.NeedsUpdate = 0;
switch (Message.message)
{
case WM_HOTKEY:
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_HOTKEY ");
}break;
case WM_MOUSEWHEEL:
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_MOUSEWHEEL ");
int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam);
/*
Input.New->MouseScroll = MouseWheel;
Result.NeedsUpdate = true;
*/
}break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_MOUSEBUTTON ");
/*
Input.New->KeyStates[KeyCode_MouseLeftButton] = (GetKeyState(VK_LBUTTON) & (1 << 15)) != 0;
Input.New->KeyStates[KeyCode_MouseMiddleButton] = (GetKeyState(VK_MBUTTON) & (1 << 15)) != 0;
Input.New->KeyStates[KeyCode_MouseRightButton] = (GetKeyState(VK_RBUTTON) & (1 << 15)) != 0;
// NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are
// VK_XBUTTON1 and VK_XBUTTON2
if (KeyTransitionedDown(KeyCode_MouseLeftButton, Input))
{
Input.MouseDownX = Input.New->MouseX;
Input.MouseDownY = Input.New->MouseY;
}
Result.NeedsUpdate = true;*/
}break;
case WM_MOUSEMOVE:
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_MOUSEMOVE ");
POINT MousePos;
GetCursorPos(&MousePos);
ScreenToClient(WindowHandle, &MousePos);
/*
Input.New->MouseX = MousePos.x;
Input.New->MouseY = App.WindowHeight - MousePos.y;
Result.NeedsUpdate = true;
*/
}break;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:
{
int VirtualKey = (int)Message.wParam;
bool KeyDown = (Message.lParam & (1 << 31)) == 0;
int KeyIndex = Win32GetKeyIndex(VirtualKey, true, true);
/*
if (KeyIndex >= 0)
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent ");
Input.New->KeyStates[KeyIndex] = KeyDown;
Result.NeedsUpdate = true;
}
else
{
if (Input.TranslateInputToCharValues && KeyDown)
{
// NOTE(Peter): Took this out b/c we're translating the WM_CHAR messages
// in the message pump, and if we do it here as well, character producing
// key messages get put on the message queue twice
TranslateMessage(&Message);
DispatchMessage(&Message);
}
else
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent ");
// NOTE(Peter): This is so that when you lift up a key that was generating a WM_CHAR,
// the app still has a chance to respond to it.
Result.NeedsUpdate = true;
}
}
*/
}break;
case WM_CHAR:
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_CHAR ");
/*
char TranslatedChar = (char)Message.wParam;
int KeyIndex = GetKeyIndexFromChar(TranslatedChar);
if (KeyIndex >= 0)
{
// NOTE(Peter): Always setting this to true becuase windows is stupid and doesn't
// pass the press/release bit through correctly. So now the KEYDOWN/KEYUP Messages above
// only translate the message to a WM_CHAR message if its a key down. Since we clear all
// keystates to false at the beginning of an input frame, this will make transitions
// get registered correctly.
Input.New->KeyStates[KeyIndex] = true;
Result.NeedsUpdate = true;
}
else
{
printf("Translated Char Not Recognized: %c\n", TranslatedChar);
//InvalidCodePath;
}
*/
}break;
default:
{
DEBUG_GET_MESSAGE_NAME(Result.MessageType, "Unhandled WM Event ");
TranslateMessage(&Message);
DispatchMessage(&Message);
}break;
}
return Result;
}
internal void
Win32UpdateWindowDimension(win32_window* Window)
{
RECT ClientRect;
GetClientRect(Window->Handle, &ClientRect);
Window->Info.Width = ClientRect.right - ClientRect.left;
Window->Info.Height = ClientRect.bottom - ClientRect.top;
}
internal void
Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height)
{
if(Buffer->Memory)
{
VirtualFree(Buffer->Memory, 0, MEM_RELEASE);
}
Buffer->Width = Width;
Buffer->Height = Height;
int BytesPerPixel = 4;
Buffer->BytesPerPixel = BytesPerPixel;
Buffer->Info.bmiHeader.biSize = sizeof(Buffer->Info.bmiHeader);
Buffer->Info.bmiHeader.biWidth = Buffer->Width;
Buffer->Info.bmiHeader.biHeight = -Buffer->Height; // Top down, not bottom up
Buffer->Info.bmiHeader.biPlanes = 1;
Buffer->Info.bmiHeader.biBitCount = 32;
Buffer->Info.bmiHeader.biCompression = BI_RGB;
int BitmapMemorySize = (Buffer->Width*Buffer->Height)*BytesPerPixel;
Buffer->Memory = (u8*)VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
Buffer->Pitch = Width*BytesPerPixel;
}
internal void
Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer, win32_window Window)
{
StretchDIBits(Window.DeviceContext,
0, 0, Buffer->Width, Buffer->Height,
0, 0, Buffer->Width, Buffer->Height,
Buffer->Memory,
&Buffer->Info,
DIB_RGB_COLORS, SRCCOPY);
}
///
// Memory
///
internal win32_memory_op_result
Win32Alloc(s32 Size)
{
win32_memory_op_result Result = {};
Result.Success = false;
Result.Base = (u8*)VirtualAlloc(NULL, Size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (Result.Base)
{
Result.Size = Size;
Result.Success = true;
}
return Result;
}
internal b32
Win32Free(u8* Base, s32 Size)
{
b32 Result = VirtualFree(Base, Size, MEM_RELEASE);
return Result;
}
// File IO
internal win32_memory_op_result
ReadEntireFile(char* Path)
{
win32_memory_op_result Result = {};
Result.Success = false;
HANDLE FileHandle = CreateFileA (
Path,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (FileHandle != INVALID_HANDLE_VALUE)
{
DWORD FileSize = GetFileSize(FileHandle, NULL);
Result.Base = (u8*)VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (Result.Base)
{
Result.Size = FileSize;
s32 BytesRead = 0;
if (ReadFile(FileHandle, (LPVOID)Result.Base, FileSize, (LPDWORD)(&BytesRead), NULL))
{
Result.Success = true;
}
else
{
Result.Size = 0;
}
}
CloseHandle(FileHandle);
}
else
{
// TODO(Peter): failure
}
return Result;
}
internal b32
WriteEntireFile (char* Path, u8* Contents, s32 Size)
{
b32 Result = false;
HANDLE FileHandle = CreateFileA (
Path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (FileHandle != INVALID_HANDLE_VALUE)
{
DWORD BytesWritten = 0;
b32 WriteSuccess = WriteFile(FileHandle,
Contents, Size,
&BytesWritten,
NULL);
if (WriteSuccess && BytesWritten == (u32)Size)
{
CloseHandle(FileHandle);
Result = true;
}
else
{
Result = false;
}
}
else
{
Result = false;
}
return Result;
}
internal FILETIME
GetFileLastWriteTime(char* Path)
{
FILETIME Result = {};
WIN32_FIND_DATA FindData = {};
HANDLE FileHandle = FindFirstFileA(Path, &FindData);
if (FileHandle != INVALID_HANDLE_VALUE)
{
Result = FindData.ftLastWriteTime;
FindClose(FileHandle);
}
else
{
// TODO(Peter): Error handling
}
return Result;
}
///
// DLL
///
internal executable_path
GetApplicationPath()
{
executable_path ExePath = {};
ExePath.PathLength = GetModuleFileNameA(0, ExePath.Path, MAX_PATH);
u32 CharactersScanned = 0;
u32 IndexOfLastSlash = 0;
char *Scan = ExePath.Path;
while(*Scan)
{
if (*Scan == '\\')
{
ExePath.IndexOfLastSlash = CharactersScanned + 1;
}
Scan++;
CharactersScanned++;
}
return ExePath;
}
internal b32
LoadApplicationDLL(char* DLLName, win32_dll_refresh* DLLResult)
{
b32 Success = false;
Assert(DLLResult->DLL == 0);
DLLResult->DLL = LoadLibraryA(DLLName); // TODO(Peter): Error checking
if (DLLResult->DLL)
{
Success = true;
DLLResult->IsValid = true;
}
return Success;
}
internal void
UnloadApplicationDLL(win32_dll_refresh* DLL)
{
if (DLL->DLL)
{
FreeLibrary(DLL->DLL);
}
DLL->DLL = 0;
DLL->IsValid = false;
}
internal win32_dll_refresh
InitializeDLLHotReloading(char* SourceDLLName,
char* WorkingDLLFileName,
char* LockFileName)
{
win32_dll_refresh Result = {};
Result.IsValid = false;
executable_path ExePath = GetApplicationPath();
Win32ConcatStrings(ExePath.IndexOfLastSlash, ExePath.Path,
Win32StringLength(SourceDLLName), SourceDLLName,
MAX_PATH, Result.SourceDLLPath);
Win32ConcatStrings(ExePath.IndexOfLastSlash, ExePath.Path,
Win32StringLength(WorkingDLLFileName), WorkingDLLFileName,
MAX_PATH, Result.WorkingDLLPath);
Win32ConcatStrings(ExePath.IndexOfLastSlash, ExePath.Path,
Win32StringLength(LockFileName), LockFileName,
MAX_PATH, Result.LockFilePath);
return Result;
}
internal b32
HotLoadDLL(win32_dll_refresh* DLL)
{
b32 DidReload = false;
FILETIME UpdatedLastWriteTime = GetFileLastWriteTime(DLL->SourceDLLPath);
if (CompareFileTime(&UpdatedLastWriteTime, &DLL->LastWriteTime))
{
WIN32_FILE_ATTRIBUTE_DATA Ignored;
if (!GetFileAttributesEx(DLL->LockFilePath, GetFileExInfoStandard, &Ignored))
{
UnloadApplicationDLL(DLL);
CopyFileA(DLL->SourceDLLPath, DLL->WorkingDLLPath, FALSE);
LoadApplicationDLL(DLL->WorkingDLLPath, DLL);
DLL->LastWriteTime = UpdatedLastWriteTime;
DidReload = true;
}
}
return DidReload;
}
#define GS_WIN32_H
#endif // GS_WIN32_H

586
interface.h Normal file
View File

@ -0,0 +1,586 @@
// NOTE(Peter): This stuff was all a test to see how I could do panel splitting. Thinking about moving away
// from that for now. Might return later if necessary
// TODO(Peter): Finish this if necessary
struct interface_region
{
v2 Min, Max;
union
{
struct
{
interface_region* A;
interface_region* B;
};
struct
{
interface_region* Left;
interface_region* Right;
};
struct
{
interface_region* Top;
interface_region* Bottom;
};
};
};
struct interface_tracker
{
memory_arena* Storage;
interface_region RootRegion;
};
enum interface_region_split
{
InterfaceRegionSplit_Vertical,
InterfaceRegionSplit_Horizontal,
};
inline s32
RegionWidth (interface_region Region)
{
s32 Result = Region.Max.x - Region.Min.x;
return Result;
}
inline s32
RegionHeight (interface_region Region)
{
s32 Result = Region.Max.y - Region.Min.y;
return Result;
}
internal void
SplitRegion (interface_tracker* Tracker, interface_region* Parent, s32 SplitPosition, interface_region_split SplitDirection)
{
if (!Parent->A)
{
interface_region* A = PushStruct(Tracker->Storage, interface_region);
A->A = 0;
A->B = 0;
Parent->A = A;
}
Parent->A->Min = Parent->Min;
Parent->A->Max = Parent->Max;
if (!Parent->B)
{
interface_region* B = PushStruct(Tracker->Storage, interface_region);
B->A = 0;
B->B = 0;
Parent->B = B;
}
Parent->B->Min = Parent->Min;
Parent->B->Max = Parent->Max;
switch (SplitDirection)
{
case InterfaceRegionSplit_Vertical:
{
Parent->Left->Max.x = Parent->Min.x + SplitPosition;
Parent->Right->Min.x = Parent->Min.x + SplitPosition;
}break;
case InterfaceRegionSplit_Horizontal:
{
Parent->Bottom->Max.y = Parent->Min.y + SplitPosition;
Parent->Top->Min.y = Parent->Min.y + SplitPosition;
}break;
}
}
internal interface_tracker
CreateInterfaceTracker (memory_arena* Storage, s32 ScreenWidth, s32 ScreenHeight)
{
interface_tracker Result = {};
Result.Storage = Storage;
Result.RootRegion.A = 0;
Result.RootRegion.B = 0;
Result.RootRegion.Min = v2{0, 0};
Result.RootRegion.Max = v2{(r32)ScreenWidth, (r32)ScreenHeight};
return Result;
}
internal v2
DrawString (render_command_buffer* RenderBuffer, string String, bitmap_font* Font, s32 PointSize, v2 Position, v4 Color)
{
DEBUG_TRACK_FUNCTION;
v2 LowerRight = Position;
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length,
*Font->Atlas);
r32 FontScale = (r32)PointSize / Font->PixelHeight;
v2 RegisterPosition = Position;
char* C = String.Memory;
for (s32 i = 0; i < String.Length; i++)
{
s32 GlyphDataIndex = GetCharacterIndexInFont(*C, Font);
character_data Data = Font->CharacterLUT_Values[GlyphDataIndex];
r32 MinX = RegisterPosition.x + Data.RegisterXOffset * FontScale;
r32 MinY = RegisterPosition.y + Data.BaselineYOffset * FontScale;
r32 MaxX = MinX + (Data.X1 - Data.X0) * FontScale;
r32 MaxY = MinY + (Data.Y1 - Data.Y0) * FontScale;
PushQuad2DOnBatch(&BatchConstructor, v2{MinX, MinY}, v2{MaxX, MinY}, v2{MaxX, MaxY}, v2{MinX, MaxY}, Data.AtlasMinUV, Data.AtlasMaxUV, Color);
RegisterPosition.x += Data.Advance * Font->FontScale * FontScale;
C++;
}
LowerRight.x = RegisterPosition.x;
return LowerRight;
}
struct interface_config
{
v4 PanelBGColors[4];
v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected;
v4 TextColor;
bitmap_font* Font;
r32 FontSize;
v2 Margin;
};
struct button_result
{
b32 Pressed;
r32 Advance;
};
internal button_result
EvaluateButton_ (render_command_buffer* RenderBuffer, v2 Min, v2 Max, string Label, interface_config Config, input Input, v4 BGColor)
{
button_result Result = {};
Result.Pressed = false;
v2 MousePos = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY};
if (PointIsInRange(MousePos, Min, Max))
{
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton))
{
Result.Pressed = true;
}
else
{
BGColor = Config.ButtonColor_Active;
}
}
PushRenderQuad2D(RenderBuffer, Min, Max, BGColor);
DrawString(RenderBuffer, Label, Config.Font, Config.Font->PixelHeight, Min + Config.Margin, Config.TextColor);
Result.Advance = (Max.y - Min.y) + Config.Margin.y;
return Result;
}
internal button_result
EvaluateButton (render_command_buffer* RenderBuffer, v2 Min, v2 Max, string Label, interface_config Config, input Input)
{
button_result Result = EvaluateButton_(RenderBuffer, Min, Max, Label, Config, Input, Config.ButtonColor_Inactive);
return Result;
}
internal button_result
EvaluateSelectableButton (render_command_buffer* RenderBuffer, v2 Min, v2 Max, string Label, interface_config Config, input Input, b32 Selected)
{
v4 BGColor = Config.ButtonColor_Inactive;
if (Selected)
{
BGColor = Config.ButtonColor_Selected;
}
button_result Result = EvaluateButton_(RenderBuffer, Min, Max, Label, Config, Input, BGColor);
return Result;
}
struct multi_option_label_result
{
b32 Pressed;
s32 IndexPressed;
r32 Advance;
};
internal multi_option_label_result
EvaluateMultiOptionLabel (render_command_buffer* RenderBuffer,
v2 Min, v2 Max, string Label, string Options[],
interface_config Config, input Input)
{
multi_option_label_result Result = {};
Result.Pressed = false;
DrawString(RenderBuffer, Label, Config.Font, 14, Min + Config.Margin, Config.TextColor);
r32 ButtonSide = (Max.y - Min.y) - (2 * Config.Margin.y);
v2 ButtonDim = v2{ButtonSide, ButtonSide};
v2 ButtonPos = Max - (ButtonDim + Config.Margin);
for (s32 b = 0; b < sizeof(Options) / sizeof(Options[0]); b++)
{
button_result Button = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
Options[b], Config, Input);
if (Button.Pressed)
{
Result.Pressed = true;
Result.IndexPressed = b;
}
}
Result.Advance = (Max.y - Min.y) + Config.Margin.y;
return Result;
}
// NOTE(Peter): returns IndexPressed = -1 if the button itself is pressed, as opposed
// to one of its options
internal multi_option_label_result
EvaluateMultiOptionButton (render_command_buffer* RenderBuffer, v2 Min, v2 Max, string Text, string Options[], b32 Selected,
interface_config Config, input Input)
{
multi_option_label_result Result = {};
Result.Pressed = false;
s32 OptionsCount = sizeof(Options) / sizeof(Options[0]);
r32 ButtonSide = (Max.y - Min.y) - (2 * Config.Margin.y);
v2 ButtonDim = v2{ButtonSide, ButtonSide};
v2 FirstButtonPos = Max - ((ButtonDim + Config.Margin) * OptionsCount);
v2 NewMax = v2{FirstButtonPos.x - Config.Margin.x, Max.y};
button_result MainButton = EvaluateSelectableButton(RenderBuffer, Min, NewMax, Text, Config, Input, Selected);
if (MainButton.Pressed)
{
Result.Pressed = true;
Result.IndexPressed = -1;
}
v2 ButtonPos = Max - (ButtonDim + Config.Margin);
for (s32 b = 0; b < OptionsCount; b++)
{
button_result Button = EvaluateButton(RenderBuffer, ButtonPos, ButtonPos + ButtonDim,
Options[b], Config, Input);
if (Button.Pressed)
{
Result.Pressed = true;
Result.IndexPressed = b;
}
}
Result.Advance = (Max.y - Min.y) + Config.Margin.y;
return Result;
}
struct slider_result
{
r32 Percent;
r32 Advance;
};
internal slider_result
EvaluateSlider (render_command_buffer* RenderBuffer, v2 Min, v2 Max, string Label, r32 Percent, interface_config Config, input Input)
{
slider_result Result = {};
v4 BGColor = Config.ButtonColor_Inactive;
v4 FillColor = Config.ButtonColor_Selected;
r32 DisplayPercent = Percent;
v2 MousePos = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY};
if (PointIsInRange(MousePos, Min, Max))
{
BGColor = Config.ButtonColor_Active;
}
if (KeyDown(Input, KeyCode_MouseLeftButton))
{
v2 MouseDownPos = v2{(r32)Input.MouseDownX, (r32)Input.MouseDownY};
if (PointIsInRange(MouseDownPos, Min, Max))
{
r32 TempFillPercent = (MousePos.y - Min.y) / (Max.y - Min.y);
DisplayPercent = GSClamp(0.0f, TempFillPercent, 1.0f);
}
}
r32 FillHeight = ((Max.y - Min.y) - 4) * DisplayPercent;
PushRenderQuad2D(RenderBuffer, Min, Max, BGColor);
PushRenderQuad2D(RenderBuffer, Min + v2{2, 2}, v2{Max.x - 2, Min.y + 2 + FillHeight}, FillColor);
// TODO(Peter): display the actual value of the slider
DrawString(RenderBuffer, Label, Config.Font, 14, Min, Config.TextColor);
Result.Percent = DisplayPercent;
Result.Advance = (Max.y - Min.y) + Config.Margin.y;
return Result;
}
struct panel_result
{
s32 Depth;
v2 NextPanelMin;
v2 ChildMin, ChildMax;
};
internal panel_result
EvaluatePanel (render_command_buffer* RenderBuffer, v2 Min, v2 Max, s32 Depth, interface_config Config, input Input)
{
panel_result Result = {};
Result.Depth = Depth;
Result.ChildMin = Min + Config.Margin;
Result.ChildMax = Max - Config.Margin;
Result.NextPanelMin = v2{Max.x, Min.y};
v4 BG = Config.PanelBGColors[Depth];
PushRenderQuad2D(RenderBuffer, Min, Max, BG);
return Result;
}
internal panel_result
EvaluatePanel (render_command_buffer* RenderBuffer, v2 Min, v2 Max, string Label, s32 Depth, interface_config Config, input Input)
{
panel_result Result = EvaluatePanel(RenderBuffer, Min, Max, Depth, Config, Input);
v2 TextPos = v2{
Min.x + Config.Margin.x,
Max.y - (Config.Font->NewLineYOffset + Config.Margin.y)
};
DrawString(RenderBuffer, Label, Config.Font, 14, TextPos, Config.TextColor);
Result.ChildMax = v2{Max.x, TextPos.y} - Config.Margin;
return Result;
}
internal panel_result
EvaluatePanel(render_command_buffer* RenderBuffer, panel_result* ParentPanel, r32 Height, string Title, interface_config Config, input Input)
{
v2 Min = v2{ParentPanel->ChildMin.x, ParentPanel->ChildMax.y - Height};
v2 Max = ParentPanel->ChildMax;
panel_result Result = EvaluatePanel(RenderBuffer, Min, Max, Title, ParentPanel->Depth + 1, Config, Input);
ParentPanel->ChildMax.y = Min.y - Config.Margin.y;
return Result;
}
enum selection_state
{
Selection_None,
Selection_Selected,
Selection_Deselected,
};
struct scroll_list_result
{
s32 IndexSelected;
s32 StartIndex;
selection_state Selection;
};
internal scroll_list_result
DrawOptionsList(render_command_buffer* RenderBuffer, v2 Min, v2 Max,
string* Options, s32 OptionsCount,
s32 Start, interface_config Config, input Input)
{
scroll_list_result Result = {};
Result.IndexSelected = -1;
Result.StartIndex = Start;
Result.Selection = Selection_None;
r32 OptionHeight = Config.Font->NewLineYOffset + (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++)
{
button_result Button = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax,
*OptionCursor,
Config, Input);
if (Button.Pressed)
{
Result.IndexSelected = Start + i;
Result.Selection = Selection_Selected;
}
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, Input);
button_result Up = EvaluateButton(RenderBuffer, v2{Min.x + HalfWidthWithMargin + Config.Margin.x, Min.y},
v2{Max.x, Min.y + OptionHeight},
UpArrowString, Config, Input);
if (Down.Pressed)
{
Result.StartIndex += 1;
}
if (Up.Pressed)
{
Result.StartIndex -= 1;
}
Result.StartIndex = GSClamp(0, Result.StartIndex, OptionsCount);
return Result;
}
internal scroll_list_result
DrawSelectableOptionsList(render_command_buffer* RenderBuffer, v2 Min, v2 Max,
string* Options, s32 OptionsCount,
s32 Start, s32 Selected, interface_config Config, input Input)
{
scroll_list_result Result = {};
Result.IndexSelected = Selected;
Result.StartIndex = Start;
Result.Selection = Selection_None;
r32 OptionHeight = Config.Font->NewLineYOffset + (2 * Config.Margin.y);
r32 OptionOffset = OptionHeight + Config.Margin.y;
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, Input, (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, Input);
button_result Up = EvaluateButton(RenderBuffer, v2{Min.x + HalfWidthWithMargin + Config.Margin.x, Min.y},
v2{Max.x, Min.y + OptionHeight},
UpArrowString, Config, Input);
if (Down.Pressed)
{
Result.StartIndex += 1;
}
if (Up.Pressed)
{
Result.StartIndex -= 1;
}
Result.StartIndex = GSClamp(0, Result.StartIndex, OptionsCount);
return Result;
}
internal r32
EvaluateColorChannelSlider (render_command_buffer* RenderBuffer, v4 ChannelMask, v2 Min, v2 Max, r32 Current, input Input)
{
r32 Result = Current;
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(RenderBuffer, 2);
v4 LeftColor = ChannelMask * 0;
LeftColor.a = 1.f;
v4 RightColor = ChannelMask;
PushQuad2DOnBatch(&Batch,
Min, v2{Max.x, Min.y}, Max, v2{Min.x, Max.y},
v2{0, 0}, v2{1, 0}, v2{1, 1}, v2{0, 1},
LeftColor, RightColor, RightColor, LeftColor);
if (KeyDown(Input, KeyCode_MouseLeftButton))
{
v2 MouseDownPos = v2{(r32)Input.MouseDownX, (r32)Input.MouseDownY};
if (PointIsInRange(MouseDownPos, Min, Max))
{
Result = ((r32)Input.New->MouseX - Min.x) / (Max.x - Min.x);
Result = GSClamp01(Result);
}
}
r32 DragBarWidth = 8;
v2 DragBarMin = v2{GSLerp(Min.x, Max.x, Result) - (DragBarWidth / 2), Min.y - 2};
v2 DragBarMax = DragBarMin + v2{DragBarWidth, (Max.y - Min.y) + 4};
PushQuad2DOnBatch(&Batch, DragBarMin, DragBarMax, v4{.3f, .3f, .3f, 1.f});
return Result;
}
internal b32
EvaluateColorPicker (render_command_buffer* RenderBuffer, v4* Value, v2 PanelMin, interface_config Config, input Input)
{
b32 ShouldClose = false;
v2 PanelMax = v2{400, 500};
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton) &&
!PointIsInRange(v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY}, PanelMin, PanelMax))
{
ShouldClose = true;
}
else
{
PushRenderQuad2D(RenderBuffer, PanelMin, PanelMax, v4{.5f, .5f, .5f, 1.f});
v2 TitleMin = v2{PanelMin.x + 5, PanelMax.y - (Config.Font->PixelHeight + 5)};
DrawString(RenderBuffer, MakeStringLiteral("Color Picker"), Config.Font, Config.Font->PixelHeight,
TitleMin, WhiteV4);
v2 SliderDim = v2{(PanelMax.x - PanelMin.x) - 20, 32};
// channel sliders
v2 SliderMin = TitleMin - v2{0, SliderDim.y + 10};
Value->r = EvaluateColorChannelSlider(RenderBuffer, RedV4, SliderMin, SliderMin + SliderDim, Value->r, Input);
SliderMin.y -= SliderDim.y + 10;
Value->g = EvaluateColorChannelSlider(RenderBuffer, GreenV4, SliderMin, SliderMin + SliderDim, Value->g, Input);
SliderMin.y -= SliderDim.y + 10;
Value->b = EvaluateColorChannelSlider(RenderBuffer, BlueV4, SliderMin, SliderMin + SliderDim, Value->b, Input);
SliderMin.y -= SliderDim.y + 10;
Value->a = EvaluateColorChannelSlider(RenderBuffer, WhiteV4, SliderMin, SliderMin + SliderDim, Value->a, Input);
// Output Color Display
SliderMin.y -= 100;
PushRenderQuad2D(RenderBuffer, SliderMin, SliderMin + v2{75, 75}, *Value);
}
return ShouldClose;
}

6
patterns_registry.h Normal file
View File

@ -0,0 +1,6 @@
pattern_registry_entry PatternRegistry[] =
{
{"Solid", SolidPatternInitProc, SolidPatternUpdateProc},
{"Rainbow", InitRainbowPatternProc, RainbowPatternProc},
{"Radial", InitRadialProc, UpdateRadialProc},
};

711
sse_mathfun.h Normal file
View File

@ -0,0 +1,711 @@
/* SIMD (SSE1+MMX or SSE2) implementation of sin, cos, exp and log
Inspired by Intel Approximate Math library, and based on the
corresponding algorithms of the cephes math library
The default is to use the SSE1 version. If you define USE_SSE2 the
the SSE2 intrinsics will be used in place of the MMX intrinsics. Do
not expect any significant performance improvement with SSE2.
*/
/* Copyright (C) 2007 Julien Pommier
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
(this is the zlib license)
*/
#include <xmmintrin.h>
/* yes I know, the top of this file is quite ugly */
#ifdef _MSC_VER /* visual c++ */
# define ALIGN16_BEG __declspec(align(16))
# define ALIGN16_END
#else /* gcc or icc */
# define ALIGN16_BEG
# define ALIGN16_END __attribute__((aligned(16)))
#endif
/* __m128 is ugly to write */
typedef __m128 v4sf; // vector of 4 float (sse1)
#ifdef USE_SSE2
# include <emmintrin.h>
typedef __m128i v4si; // vector of 4 int (sse2)
#else
typedef __m64 v2si; // vector of 2 int (mmx)
#endif
/* declare some SSE constants -- why can't I figure a better way to do that? */
#define _PS_CONST(Name, Val) \
static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
#define _PI32_CONST(Name, Val) \
static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
#define _PS_CONST_TYPE(Name, Type, Val) \
static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
_PS_CONST(1 , 1.0f);
_PS_CONST(0p5, 0.5f);
/* the smallest non denormalized float number */
_PS_CONST_TYPE(min_norm_pos, int, 0x00800000);
_PS_CONST_TYPE(mant_mask, int, 0x7f800000);
_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000);
_PS_CONST_TYPE(sign_mask, int, (int)0x80000000);
_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000);
_PI32_CONST(1, 1);
_PI32_CONST(inv1, ~1);
_PI32_CONST(2, 2);
_PI32_CONST(4, 4);
_PI32_CONST(0x7f, 0x7f);
_PS_CONST(cephes_SQRTHF, 0.707106781186547524);
_PS_CONST(cephes_log_p0, 7.0376836292E-2);
_PS_CONST(cephes_log_p1, - 1.1514610310E-1);
_PS_CONST(cephes_log_p2, 1.1676998740E-1);
_PS_CONST(cephes_log_p3, - 1.2420140846E-1);
_PS_CONST(cephes_log_p4, + 1.4249322787E-1);
_PS_CONST(cephes_log_p5, - 1.6668057665E-1);
_PS_CONST(cephes_log_p6, + 2.0000714765E-1);
_PS_CONST(cephes_log_p7, - 2.4999993993E-1);
_PS_CONST(cephes_log_p8, + 3.3333331174E-1);
_PS_CONST(cephes_log_q1, -2.12194440e-4);
_PS_CONST(cephes_log_q2, 0.693359375);
#ifndef USE_SSE2
typedef union xmm_mm_union {
__m128 xmm;
__m64 mm[2];
} xmm_mm_union;
#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) { \
xmm_mm_union u; u.xmm = xmm_; \
mm0_ = u.mm[0]; \
mm1_ = u.mm[1]; \
}
#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) { \
xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm; \
}
#endif // USE_SSE2
/* natural logarithm computed for 4 simultaneous float
return NaN for x <= 0
*/
v4sf log_ps(v4sf x) {
#ifdef USE_SSE2
v4si emm0;
#else
v2si mm0, mm1;
#endif
v4sf one = *(v4sf*)_ps_1;
v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps());
x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos); /* cut off denormalized stuff */
#ifndef USE_SSE2
/* part 1: x = frexpf(x, &e); */
COPY_XMM_TO_MM(x, mm0, mm1);
mm0 = _mm_srli_pi32(mm0, 23);
mm1 = _mm_srli_pi32(mm1, 23);
#else
emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23);
#endif
/* keep only the fractional part */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask);
x = _mm_or_ps(x, *(v4sf*)_ps_0p5);
#ifndef USE_SSE2
/* now e=mm0:mm1 contain the really base-2 exponent */
mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f);
mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f);
v4sf e = _mm_cvtpi32x2_ps(mm0, mm1);
_mm_empty(); /* bye bye mmx */
#else
emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f);
v4sf e = _mm_cvtepi32_ps(emm0);
#endif
e = _mm_add_ps(e, one);
/* part2:
if( x < SQRTHF ) {
e -= 1;
x = x + x - 1.0;
} else { x = x - 1.0; }
*/
v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF);
v4sf tmp = _mm_and_ps(x, mask);
x = _mm_sub_ps(x, one);
e = _mm_sub_ps(e, _mm_and_ps(one, mask));
x = _mm_add_ps(x, tmp);
v4sf z = _mm_mul_ps(x,x);
v4sf y = *(v4sf*)_ps_cephes_log_p0;
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8);
y = _mm_mul_ps(y, x);
y = _mm_mul_ps(y, z);
tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1);
y = _mm_add_ps(y, tmp);
tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2);
x = _mm_add_ps(x, y);
x = _mm_add_ps(x, tmp);
x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN
return x;
}
_PS_CONST(exp_hi, 88.3762626647949f);
_PS_CONST(exp_lo, -88.3762626647949f);
_PS_CONST(cephes_LOG2EF, 1.44269504088896341);
_PS_CONST(cephes_exp_C1, 0.693359375);
_PS_CONST(cephes_exp_C2, -2.12194440e-4);
_PS_CONST(cephes_exp_p0, 1.9875691500E-4);
_PS_CONST(cephes_exp_p1, 1.3981999507E-3);
_PS_CONST(cephes_exp_p2, 8.3334519073E-3);
_PS_CONST(cephes_exp_p3, 4.1665795894E-2);
_PS_CONST(cephes_exp_p4, 1.6666665459E-1);
_PS_CONST(cephes_exp_p5, 5.0000001201E-1);
v4sf exp_ps(v4sf x) {
v4sf tmp = _mm_setzero_ps(), fx;
#ifdef USE_SSE2
v4si emm0;
#else
v2si mm0, mm1;
#endif
v4sf one = *(v4sf*)_ps_1;
x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi);
x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo);
/* express exp(x) as exp(g + n*log(2)) */
fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF);
fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5);
/* how to perform a floorf with SSE: just below */
#ifndef USE_SSE2
/* step 1 : cast to int */
tmp = _mm_movehl_ps(tmp, fx);
mm0 = _mm_cvttps_pi32(fx);
mm1 = _mm_cvttps_pi32(tmp);
/* step 2 : cast back to float */
tmp = _mm_cvtpi32x2_ps(mm0, mm1);
#else
emm0 = _mm_cvttps_epi32(fx);
tmp = _mm_cvtepi32_ps(emm0);
#endif
/* if greater, substract 1 */
v4sf mask = _mm_cmpgt_ps(tmp, fx);
mask = _mm_and_ps(mask, one);
fx = _mm_sub_ps(tmp, mask);
tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1);
v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2);
x = _mm_sub_ps(x, tmp);
x = _mm_sub_ps(x, z);
z = _mm_mul_ps(x,x);
v4sf y = *(v4sf*)_ps_cephes_exp_p0;
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, x);
y = _mm_add_ps(y, one);
/* build 2^n */
#ifndef USE_SSE2
z = _mm_movehl_ps(z, fx);
mm0 = _mm_cvttps_pi32(fx);
mm1 = _mm_cvttps_pi32(z);
mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f);
mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f);
mm0 = _mm_slli_pi32(mm0, 23);
mm1 = _mm_slli_pi32(mm1, 23);
v4sf pow2n;
COPY_MM_TO_XMM(mm0, mm1, pow2n);
_mm_empty();
#else
emm0 = _mm_cvttps_epi32(fx);
emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f);
emm0 = _mm_slli_epi32(emm0, 23);
v4sf pow2n = _mm_castsi128_ps(emm0);
#endif
y = _mm_mul_ps(y, pow2n);
return y;
}
_PS_CONST(minus_cephes_DP1, -0.78515625);
_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4);
_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8);
_PS_CONST(sincof_p0, -1.9515295891E-4);
_PS_CONST(sincof_p1, 8.3321608736E-3);
_PS_CONST(sincof_p2, -1.6666654611E-1);
_PS_CONST(coscof_p0, 2.443315711809948E-005);
_PS_CONST(coscof_p1, -1.388731625493765E-003);
_PS_CONST(coscof_p2, 4.166664568298827E-002);
_PS_CONST(cephes_FOPI, 1.27323954473516); // 4 / M_PI
/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so
it runs also on old athlons XPs and the pentium III of your grand
mother.
The code is the exact rewriting of the cephes sinf function.
Precision is excellent as long as x < 8192 (I did not bother to
take into account the special handling they have for greater values
-- it does not return garbage for arguments over 8192, though, but
the extra precision is missing).
Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
surprising but correct result.
Performance is also surprisingly good, 1.33 times faster than the
macos vsinf SSE2 function, and 1.5 times faster than the
__vrs4_sinf of amd's ACML (which is only available in 64 bits). Not
too bad for an SSE1 function (with no special tuning) !
However the latter libraries probably have a much better handling of NaN,
Inf, denormalized and other special arguments..
On my core 1 duo, the execution of this function takes approximately 95 cycles.
From what I have observed on the experiments with Intel AMath lib, switching to an
SSE2 version would improve the perf by only 10%.
Since it is based on SSE intrinsics, it has to be compiled at -O2 to
deliver full speed.
*/
v4sf sin_ps(v4sf x) { // any x
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
#ifdef USE_SSE2
v4si emm0, emm2;
#else
v2si mm0, mm1, mm2, mm3;
#endif
sign_bit = x;
/* take the absolute value */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
/* extract the sign bit (upper one) */
sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
#ifdef USE_SSE2
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
y = _mm_cvtepi32_ps(emm2);
/* get the swap sign flag */
emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
emm0 = _mm_slli_epi32(emm0, 29);
/* get the polynom selection mask
there is one polynom for 0 <= x <= Pi/4
and another one for Pi/4<x<=Pi/2
Both branches will be computed.
*/
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
v4sf swap_sign_bit = _mm_castsi128_ps(emm0);
v4sf poly_mask = _mm_castsi128_ps(emm2);
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
#else
/* store the integer part of y in mm0:mm1 */
xmm2 = _mm_movehl_ps(xmm2, y);
mm2 = _mm_cvttps_pi32(y);
mm3 = _mm_cvttps_pi32(xmm2);
/* j=(j+1) & (~1) (see the cephes sources) */
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
y = _mm_cvtpi32x2_ps(mm2, mm3);
/* get the swap sign flag */
mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
mm0 = _mm_slli_pi32(mm0, 29);
mm1 = _mm_slli_pi32(mm1, 29);
/* get the polynom selection mask */
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
v4sf swap_sign_bit, poly_mask;
COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit);
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
_mm_empty(); /* good-bye mmx */
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
y = *(v4sf*)_ps_coscof_p0;
v4sf z = _mm_mul_ps(x,x);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(v4sf*)_ps_1);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
v4sf y2 = *(v4sf*)_ps_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
y = _mm_andnot_ps(xmm3, y);
y = _mm_add_ps(y,y2);
/* update the sign */
y = _mm_xor_ps(y, sign_bit);
return y;
}
/* almost the same as sin_ps */
v4sf cos_ps(v4sf x) { // any x
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, y;
#ifdef USE_SSE2
v4si emm0, emm2;
#else
v2si mm0, mm1, mm2, mm3;
#endif
/* take the absolute value */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
#ifdef USE_SSE2
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
y = _mm_cvtepi32_ps(emm2);
emm2 = _mm_sub_epi32(emm2, *(v4si*)_pi32_2);
/* get the swap sign flag */
emm0 = _mm_andnot_si128(emm2, *(v4si*)_pi32_4);
emm0 = _mm_slli_epi32(emm0, 29);
/* get the polynom selection mask */
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
v4sf sign_bit = _mm_castsi128_ps(emm0);
v4sf poly_mask = _mm_castsi128_ps(emm2);
#else
/* store the integer part of y in mm0:mm1 */
xmm2 = _mm_movehl_ps(xmm2, y);
mm2 = _mm_cvttps_pi32(y);
mm3 = _mm_cvttps_pi32(xmm2);
/* j=(j+1) & (~1) (see the cephes sources) */
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
y = _mm_cvtpi32x2_ps(mm2, mm3);
mm2 = _mm_sub_pi32(mm2, *(v2si*)_pi32_2);
mm3 = _mm_sub_pi32(mm3, *(v2si*)_pi32_2);
/* get the swap sign flag in mm0:mm1 and the
polynom selection mask in mm2:mm3 */
mm0 = _mm_andnot_si64(mm2, *(v2si*)_pi32_4);
mm1 = _mm_andnot_si64(mm3, *(v2si*)_pi32_4);
mm0 = _mm_slli_pi32(mm0, 29);
mm1 = _mm_slli_pi32(mm1, 29);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
v4sf sign_bit, poly_mask;
COPY_MM_TO_XMM(mm0, mm1, sign_bit);
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
_mm_empty(); /* good-bye mmx */
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
y = *(v4sf*)_ps_coscof_p0;
v4sf z = _mm_mul_ps(x,x);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(v4sf*)_ps_1);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
v4sf y2 = *(v4sf*)_ps_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
y = _mm_andnot_ps(xmm3, y);
y = _mm_add_ps(y,y2);
/* update the sign */
y = _mm_xor_ps(y, sign_bit);
return y;
}
/* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them..
it is almost as fast, and gives you a free cosine with your sine */
void sincos_ps(v4sf x, v4sf *s, v4sf *c) {
v4sf xmm1, xmm2, xmm3 = _mm_setzero_ps(), sign_bit_sin, y;
#ifdef USE_SSE2
v4si emm0, emm2, emm4;
#else
v2si mm0, mm1, mm2, mm3, mm4, mm5;
#endif
sign_bit_sin = x;
/* take the absolute value */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
/* extract the sign bit (upper one) */
sign_bit_sin = _mm_and_ps(sign_bit_sin, *(v4sf*)_ps_sign_mask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
#ifdef USE_SSE2
/* store the integer part of y in emm2 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
y = _mm_cvtepi32_ps(emm2);
emm4 = emm2;
/* get the swap sign flag for the sine */
emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
emm0 = _mm_slli_epi32(emm0, 29);
v4sf swap_sign_bit_sin = _mm_castsi128_ps(emm0);
/* get the polynom selection mask for the sine*/
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
v4sf poly_mask = _mm_castsi128_ps(emm2);
#else
/* store the integer part of y in mm2:mm3 */
xmm3 = _mm_movehl_ps(xmm3, y);
mm2 = _mm_cvttps_pi32(y);
mm3 = _mm_cvttps_pi32(xmm3);
/* j=(j+1) & (~1) (see the cephes sources) */
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
y = _mm_cvtpi32x2_ps(mm2, mm3);
mm4 = mm2;
mm5 = mm3;
/* get the swap sign flag for the sine */
mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
mm0 = _mm_slli_pi32(mm0, 29);
mm1 = _mm_slli_pi32(mm1, 29);
v4sf swap_sign_bit_sin;
COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit_sin);
/* get the polynom selection mask for the sine */
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
v4sf poly_mask;
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
#ifdef USE_SSE2
emm4 = _mm_sub_epi32(emm4, *(v4si*)_pi32_2);
emm4 = _mm_andnot_si128(emm4, *(v4si*)_pi32_4);
emm4 = _mm_slli_epi32(emm4, 29);
v4sf sign_bit_cos = _mm_castsi128_ps(emm4);
#else
/* get the sign flag for the cosine */
mm4 = _mm_sub_pi32(mm4, *(v2si*)_pi32_2);
mm5 = _mm_sub_pi32(mm5, *(v2si*)_pi32_2);
mm4 = _mm_andnot_si64(mm4, *(v2si*)_pi32_4);
mm5 = _mm_andnot_si64(mm5, *(v2si*)_pi32_4);
mm4 = _mm_slli_pi32(mm4, 29);
mm5 = _mm_slli_pi32(mm5, 29);
v4sf sign_bit_cos;
COPY_MM_TO_XMM(mm4, mm5, sign_bit_cos);
_mm_empty(); /* good-bye mmx */
#endif
sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
v4sf z = _mm_mul_ps(x,x);
y = *(v4sf*)_ps_coscof_p0;
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(v4sf*)_ps_1);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
v4sf y2 = *(v4sf*)_ps_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
v4sf ysin2 = _mm_and_ps(xmm3, y2);
v4sf ysin1 = _mm_andnot_ps(xmm3, y);
y2 = _mm_sub_ps(y2,ysin2);
y = _mm_sub_ps(y, ysin1);
xmm1 = _mm_add_ps(ysin1,ysin2);
xmm2 = _mm_add_ps(y,y2);
/* update the sign */
*s = _mm_xor_ps(xmm1, sign_bit_sin);
*c = _mm_xor_ps(xmm2, sign_bit_cos);
}

360
sse_mathfun_extension.h Normal file
View File

@ -0,0 +1,360 @@
/*
sse_mathfun_extension.h - zlib license
Written by Tolga Mizrak 2016
Extension of sse_mathfun.h, which is written by Julien Pommier
Based on the corresponding algorithms of the cephes math library
This is written as an extension to sse_mathfun.h instead of modifying it, just because I didn't want
to maintain a modified version of the original library. This way switching to a newer version of the
library won't be a hassle.
Note that non SSE2 implementations of tan_ps, atan_ps, cot_ps and atan2_ps are not implemented yet.
As such, currently you need to #define USE_SSE2 to compile.
With tan_ps, cot_ps you get good precision on input ranges that are further away from the domain
borders (-PI/2, PI/2 for tan and 0, 1 for cot). See the results on the deviations for these
functions on my machine:
checking tan on [-0.25*Pi, 0.25*Pi]
max deviation from tanf(x): 1.19209e-07 at 0.250000006957*Pi, max deviation from cephes_tan(x):
5.96046e-08
->> precision OK for the tan_ps <<-
checking tan on [-0.49*Pi, 0.49*Pi]
max deviation from tanf(x): 3.8147e-06 at -0.490000009841*Pi, max deviation from cephes_tan(x):
9.53674e-07
->> precision OK for the tan_ps <<-
checking cot on [0.2*Pi, 0.7*Pi]
max deviation from cotf(x): 1.19209e-07 at 0.204303119606*Pi, max deviation from cephes_cot(x):
1.19209e-07
->> precision OK for the cot_ps <<-
checking cot on [0.01*Pi, 0.99*Pi]
max deviation from cotf(x): 3.8147e-06 at 0.987876517942*Pi, max deviation from cephes_cot(x):
9.53674e-07
->> precision OK for the cot_ps <<-
With atan_ps and atan2_ps you get pretty good precision, atan_ps max deviation is < 2e-7 and
atan2_ps max deviation is < 2.5e-7
*/
/* Copyright (C) 2016 Tolga Mizrak
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
(this is the zlib license)
*/
#pragma once
#ifndef _SSE_MATHFUN_EXTENSION_H_INCLUDED_
#define _SSE_MATHFUN_EXTENSION_H_INCLUDED_
#ifndef USE_SSE2
#error sse1 & mmx version not implemented
#endif
#ifdef _MSC_VER
#pragma warning( push )
/* warning C4838: conversion from 'double' to 'const float' requires a narrowing conversion */
#pragma warning( disable : 4838 )
/* warning C4305: 'initializing': truncation from 'double' to 'const float' */
#pragma warning( disable : 4305 )
#endif
#include "sse_mathfun.h"
_PS_CONST( 0, 0 );
_PS_CONST( 2, 2 );
_PI32_CONST( neg1, 1 );
_PS_CONST( tancof_p0, 9.38540185543E-3 );
_PS_CONST( tancof_p1, 3.11992232697E-3 );
_PS_CONST( tancof_p2, 2.44301354525E-2 );
_PS_CONST( tancof_p3, 5.34112807005E-2 );
_PS_CONST( tancof_p4, 1.33387994085E-1 );
_PS_CONST( tancof_p5, 3.33331568548E-1 );
_PS_CONST( tancot_eps, 1.0e-4 );
v4sf tancot_ps( v4sf x, int cotFlag )
{
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
#ifdef USE_SSE2
v4si emm2;
#else
#endif
sign_bit = x;
/* take the absolute value */
x = _mm_and_ps( x, *(v4sf*)_ps_inv_sign_mask );
/* extract the sign bit (upper one) */
sign_bit = _mm_and_ps( sign_bit, *(v4sf*)_ps_sign_mask );
/* scale by 4/Pi */
y = _mm_mul_ps( x, *(v4sf*)_ps_cephes_FOPI );
#ifdef USE_SSE2
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32( y );
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32( emm2, *(v4si*)_pi32_1 );
emm2 = _mm_and_si128( emm2, *(v4si*)_pi32_inv1 );
y = _mm_cvtepi32_ps( emm2 );
emm2 = _mm_and_si128( emm2, *(v4si*)_pi32_2 );
emm2 = _mm_cmpeq_epi32( emm2, _mm_setzero_si128() );
v4sf poly_mask = _mm_castsi128_ps( emm2 );
#else
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps( y, xmm1 );
xmm2 = _mm_mul_ps( y, xmm2 );
xmm3 = _mm_mul_ps( y, xmm3 );
v4sf z = _mm_add_ps( x, xmm1 );
z = _mm_add_ps( z, xmm2 );
z = _mm_add_ps( z, xmm3 );
v4sf zz = _mm_mul_ps( z, z );
y = *(v4sf*)_ps_tancof_p0;
y = _mm_mul_ps( y, zz );
y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p1 );
y = _mm_mul_ps( y, zz );
y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p2 );
y = _mm_mul_ps( y, zz );
y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p3 );
y = _mm_mul_ps( y, zz );
y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p4 );
y = _mm_mul_ps( y, zz );
y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p5 );
y = _mm_mul_ps( y, zz );
y = _mm_mul_ps( y, z );
y = _mm_add_ps( y, z );
v4sf y2;
if( cotFlag ) {
y2 = _mm_xor_ps( y, *(v4sf*)_ps_sign_mask );
/* y = _mm_rcp_ps( y ); */
/* using _mm_rcp_ps here loses on way too much precision, better to do a div */
y = _mm_div_ps( *(v4sf*)_ps_1, y );
} else {
/* y2 = _mm_rcp_ps( y ); */
/* using _mm_rcp_ps here loses on way too much precision, better to do a div */
y2 = _mm_div_ps( *(v4sf*)_ps_1, y );
y2 = _mm_xor_ps( y2, *(v4sf*)_ps_sign_mask );
}
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y = _mm_and_ps( xmm3, y );
y2 = _mm_andnot_ps( xmm3, y2 );
y = _mm_or_ps( y, y2 );
/* update the sign */
y = _mm_xor_ps( y, sign_bit );
return y;
}
v4sf tan_ps( v4sf x ) { return tancot_ps( x, 0 ); }
v4sf cot_ps( v4sf x ) { return tancot_ps( x, 1 ); }
_PS_CONST( atanrange_hi, 2.414213562373095 );
_PS_CONST( atanrange_lo, 0.4142135623730950 );
const float PIF = 3.141592653589793238;
const float PIO2F = 1.5707963267948966192;
_PS_CONST( cephes_PIF, 3.141592653589793238 );
_PS_CONST( cephes_PIO2F, 1.5707963267948966192 );
_PS_CONST( cephes_PIO4F, 0.7853981633974483096 );
_PS_CONST( atancof_p0, 8.05374449538e-2 );
_PS_CONST( atancof_p1, 1.38776856032E-1 );
_PS_CONST( atancof_p2, 1.99777106478E-1 );
_PS_CONST( atancof_p3, 3.33329491539E-1 );
v4sf atan_ps( v4sf x )
{
v4sf sign_bit, y;
sign_bit = x;
/* take the absolute value */
x = _mm_and_ps( x, *(v4sf*)_ps_inv_sign_mask );
/* extract the sign bit (upper one) */
sign_bit = _mm_and_ps( sign_bit, *(v4sf*)_ps_sign_mask );
/* range reduction, init x and y depending on range */
#ifdef USE_SSE2
/* x > 2.414213562373095 */
v4sf cmp0 = _mm_cmpgt_ps( x, *(v4sf*)_ps_atanrange_hi );
/* x > 0.4142135623730950 */
v4sf cmp1 = _mm_cmpgt_ps( x, *(v4sf*)_ps_atanrange_lo );
/* x > 0.4142135623730950 && !( x > 2.414213562373095 ) */
v4sf cmp2 = _mm_andnot_ps( cmp0, cmp1 );
/* -( 1.0/x ) */
v4sf y0 = _mm_and_ps( cmp0, *(v4sf*)_ps_cephes_PIO2F );
v4sf x0 = _mm_div_ps( *(v4sf*)_ps_1, x );
x0 = _mm_xor_ps( x0, *(v4sf*)_ps_sign_mask );
v4sf y1 = _mm_and_ps( cmp2, *(v4sf*)_ps_cephes_PIO4F );
/* (x-1.0)/(x+1.0) */
v4sf x1_o = _mm_sub_ps( x, *(v4sf*)_ps_1 );
v4sf x1_u = _mm_add_ps( x, *(v4sf*)_ps_1 );
v4sf x1 = _mm_div_ps( x1_o, x1_u );
v4sf x2 = _mm_and_ps( cmp2, x1 );
x0 = _mm_and_ps( cmp0, x0 );
x2 = _mm_or_ps( x2, x0 );
cmp1 = _mm_or_ps( cmp0, cmp2 );
x2 = _mm_and_ps( cmp1, x2 );
x = _mm_andnot_ps( cmp1, x );
x = _mm_or_ps( x2, x );
y = _mm_or_ps( y0, y1 );
#else
#error sse1 & mmx version not implemented
#endif
v4sf zz = _mm_mul_ps( x, x );
v4sf acc = *(v4sf*)_ps_atancof_p0;
acc = _mm_mul_ps( acc, zz );
acc = _mm_sub_ps( acc, *(v4sf*)_ps_atancof_p1 );
acc = _mm_mul_ps( acc, zz );
acc = _mm_add_ps( acc, *(v4sf*)_ps_atancof_p2 );
acc = _mm_mul_ps( acc, zz );
acc = _mm_sub_ps( acc, *(v4sf*)_ps_atancof_p3 );
acc = _mm_mul_ps( acc, zz );
acc = _mm_mul_ps( acc, x );
acc = _mm_add_ps( acc, x );
y = _mm_add_ps( y, acc );
/* update the sign */
y = _mm_xor_ps( y, sign_bit );
return y;
}
v4sf atan2_ps( v4sf y, v4sf x )
{
v4sf x_eq_0 = _mm_cmpeq_ps( x, *(v4sf*)_ps_0 );
v4sf x_gt_0 = _mm_cmpgt_ps( x, *(v4sf*)_ps_0 );
v4sf x_le_0 = _mm_cmple_ps( x, *(v4sf*)_ps_0 );
v4sf y_eq_0 = _mm_cmpeq_ps( y, *(v4sf*)_ps_0 );
v4sf x_lt_0 = _mm_cmplt_ps( x, *(v4sf*)_ps_0 );
v4sf y_lt_0 = _mm_cmplt_ps( y, *(v4sf*)_ps_0 );
v4sf zero_mask = _mm_and_ps( x_eq_0, y_eq_0 );
v4sf zero_mask_other_case = _mm_and_ps( y_eq_0, x_gt_0 );
zero_mask = _mm_or_ps( zero_mask, zero_mask_other_case );
v4sf pio2_mask = _mm_andnot_ps( y_eq_0, x_eq_0 );
v4sf pio2_mask_sign = _mm_and_ps( y_lt_0, *(v4sf*)_ps_sign_mask );
v4sf pio2_result = *(v4sf*)_ps_cephes_PIO2F;
pio2_result = _mm_xor_ps( pio2_result, pio2_mask_sign );
pio2_result = _mm_and_ps( pio2_mask, pio2_result );
v4sf pi_mask = _mm_and_ps( y_eq_0, x_le_0 );
v4sf pi = *(v4sf*)_ps_cephes_PIF;
v4sf pi_result = _mm_and_ps( pi_mask, pi );
v4sf swap_sign_mask_offset = _mm_and_ps( x_lt_0, y_lt_0 );
swap_sign_mask_offset = _mm_and_ps( swap_sign_mask_offset, *(v4sf*)_ps_sign_mask );
v4sf offset0 = _mm_setzero_ps();
v4sf offset1 = *(v4sf*)_ps_cephes_PIF;
offset1 = _mm_xor_ps( offset1, swap_sign_mask_offset );
v4sf offset = _mm_andnot_ps( x_lt_0, offset0 );
offset = _mm_and_ps( x_lt_0, offset1 );
v4sf arg = _mm_div_ps( y, x );
v4sf atan_result = atan_ps( arg );
atan_result = _mm_add_ps( atan_result, offset );
/* select between zero_result, pio2_result and atan_result */
v4sf result = _mm_andnot_ps( zero_mask, pio2_result );
atan_result = _mm_andnot_ps( pio2_mask, atan_result );
atan_result = _mm_andnot_ps( pio2_mask, atan_result);
result = _mm_or_ps( result, atan_result );
result = _mm_or_ps( result, pi_result );
return result;
}
/* for convenience of calling simd sqrt */
float sqrt_ps( float x )
{
v4sf sse_value = _mm_set_ps1( x );
sse_value = _mm_sqrt_ps( sse_value );
return _mm_cvtss_f32( sse_value );
}
float rsqrt_ps( float x )
{
v4sf sse_value = _mm_set_ps1( x );
sse_value = _mm_rsqrt_ps( sse_value );
return _mm_cvtss_f32( sse_value );
}
/* atan2 implementation using atan, used as a reference to implement atan2_ps */
float atan2_ref( float y, float x )
{
if( x == 0.0f ) {
if( y == 0.0f ) {
return 0.0f;
}
float result = _ps_cephes_PIO2F[0];
if( y < 0.0f ) {
result = -result;
}
return result;
}
if( y == 0.0f ) {
if( x > 0.0f ) {
return 0.0f;
}
return PIF;
}
float offset = 0;
if( x < 0.0f ) {
offset = PIF;
if( y < 0.0f ) {
offset = -offset;
}
}
v4sf val = _mm_set_ps1( y / x );
val = atan_ps( val );
return offset + _mm_cvtss_f32( val );
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

4853
stb/stb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

196
test_patterns.h Normal file
View File

@ -0,0 +1,196 @@
NODE_STRUCT(float_value_data)
{
NODE_IN(r32, Value);
NODE_OUT(r32, Result);
};
NODE_PROC(FloatValueProc, float_value_data)
{
Data->Result = Data->Value;
}
NODE_STRUCT(solid_color_data)
{
NODE_IN(v4, Color);
NODE_COLOR_BUFFER_INOUT;
};
NODE_PROC(SolidColorProc, solid_color_data)
{
u8 R = (u8)(Data->Color.r * 255);
u8 G = (u8)(Data->Color.g * 255);
u8 B = (u8)(Data->Color.b * 255);
led* LED = Data->LEDs;
for (s32 l = 0; l < Data->LEDCount; l++)
{
Assert(LED->Index >= 0 && LED->Index < Data->LEDCount);
Data->Colors[LED->Index].R = R;
Data->Colors[LED->Index].G = G;
Data->Colors[LED->Index].B = B;
LED++;
}
}
NODE_STRUCT(multiply_patterns_data)
{
NODE_COLOR_BUFFER_IN(A);
NODE_COLOR_BUFFER_IN(B);
NODE_COLOR_BUFFER_OUT(Result);
};
NODE_PROC(MultiplyPatterns, multiply_patterns_data)
{
led* LED = Data->ResultLEDs;
for (s32 l = 0; l < Data->ResultLEDCount; l++)
{
Assert(LED->Index >= 0 && LED->Index < Data->ResultLEDCount);
Data->ResultColors[LED->Index].R = (Data->AColors[LED->Index].R + Data->BColors[LED->Index].R) / 2;
Data->ResultColors[LED->Index].G = (Data->AColors[LED->Index].G + Data->BColors[LED->Index].G) / 2;
Data->ResultColors[LED->Index].B = (Data->AColors[LED->Index].B + Data->BColors[LED->Index].B) / 2;
LED++;
}
}
NODE_PATTERN_STRUCT(vertical_color_fade_data)
{
NODE_IN(v4, Color);
NODE_IN(r32, Min);
NODE_IN(r32, Max);
};
NODE_PATTERN_PROC(VerticalColorFadeProc, vertical_color_fade_data)
{
r32 R = (Data->Color.r * 255);
r32 G = (Data->Color.g * 255);
r32 B = (Data->Color.b * 255);
r32 Range = Data->Max - Data->Min;
led* LED = LEDs;
for (s32 l = 0; l < LEDCount; l++)
{
r32 Amount = (LED->Position.y - Data->Min) / Range;
Amount = GSClamp01(1.0f - Amount);
Colors[LED->Index].R = (u8)(R * Amount);
Colors[LED->Index].G = (u8)(G * Amount);
Colors[LED->Index].B = (u8)(B * Amount);
LED++;
}
}
// ^^^ New ^^^
// vvv Old vvv
PATTERN_INIT_PROC(SolidPatternInitProc)
{
Pattern->Memory = (void*)PushArray(Storage, u8, 3);
u8* Color = (u8*)Pattern->Memory;
Color[0] = 0;
Color[1] = 0;
Color[2] = 128;
}
PATTERN_UPDATE_PROC(SolidPatternUpdateProc)
{
u8* Color = (u8*)Memory;
u8 R = Color[0];
u8 G = Color[1];
u8 B = Color[2];
led* LED = LEDs;
for (s32 l = 0; l < LEDCount; l++)
{
PushColor(LED++, Colors, R, G, B);
}
}
struct rainbow_pattern_memory
{
r32 TimeAccumulator;
r32 Period;
};
PATTERN_INIT_PROC(InitRainbowPatternProc)
{
Pattern->Memory = (void*)PushStruct(Storage, rainbow_pattern_memory);
rainbow_pattern_memory* Mem = (rainbow_pattern_memory*)Pattern->Memory;
Mem->TimeAccumulator = 0;
Mem->Period = 6.0f;
}
PATTERN_UPDATE_PROC(RainbowPatternProc)
{
DEBUG_TRACK_SCOPE(RainbowPatternProc);
rainbow_pattern_memory* Mem = (rainbow_pattern_memory*)Memory;
Mem->TimeAccumulator += DeltaTime;
if (Mem->TimeAccumulator >= Mem->Period)
{
Mem->TimeAccumulator -= Mem->Period;
}
r32 Percent = Mem->TimeAccumulator / Mem->Period;
r32 HueAdd = Percent * 360.0f;
r32 HueScale = 360.0f / 100;
led* LED = LEDs;
for (s32 l = 0; l < LEDCount; l++)
{
r32 Hue = (LED->Position.y * HueScale) + HueAdd;
v4 Color = HSVToRGB(v4{Hue, 1, 1, 1}) * .75f;
PushColor(LED++, Colors, (u8)(Color.r * 255), (u8)(Color.g * 255), (u8)(Color.b * 255));
}
}
PATTERN_INIT_PROC(InitRadialProc)
{
Pattern->Memory = (void*)PushStruct(Storage, rainbow_pattern_memory);
rainbow_pattern_memory* Mem = (rainbow_pattern_memory*)Pattern->Memory;
Mem->TimeAccumulator = 0;
Mem->Period = 10.0;
}
PATTERN_UPDATE_PROC(UpdateRadialProc)
{
rainbow_pattern_memory* Mem = (rainbow_pattern_memory*)Memory;
Mem->TimeAccumulator += DeltaTime;
if (Mem->TimeAccumulator >= Mem->Period)
{
Mem->TimeAccumulator -= Mem->Period;
}
r32 Percent = Mem->TimeAccumulator / Mem->Period;
r32 AngleAdd = Percent * PI * 2;
r32 HueAdd = Percent * 360;
v2 DirectionVector = v2{GSSin(AngleAdd), GSCos(AngleAdd)};
led* LED = LEDs;
for (s32 l = 0; l < LEDCount; l++)
{
v4 Color = {0, 0, 0, 1};
if (LED->Position.y >= 70)
{
v2 TwoDPos = v2{LED->Position.x, LED->Position.z};
r32 Angle = Dot(Normalize(TwoDPos), DirectionVector) * .25f;
r32 Hue = Angle * 360 + HueAdd;
Color = HSVToRGB(v4{Hue, 1, 1, 1}) * .9f;
}
else
{
Color = HSVToRGB(v4{HueAdd, 1, 1, 1}) * .9f;
}
PushColor(LED++, Colors, (u8)(Color.r * 255), (u8)(Color.g * 255), (u8)(Color.b * 255));
}
}

222
todo.txt Normal file
View File

@ -0,0 +1,222 @@
TODO FOLDHAUS
Pick Up Where You Left Off
- In a node, you aren't filling in all the LED lists. Some of them (the outputs) are empty
when you reach the pattern proc because theyre outputs.
SOLUTION: Probably want to take the led lists out of each set of colors and just pass them in
once per data struct.
Name
x lumen lab
- Splash screen (like blender) (thisll be fun)
- x Create Image
- - Image importer (stb image? or find a png > bmp converter for the image you have)
- - Display on startup
/Debug
x There's an extra tri/quad sometimes when the renderer starts
x Something is still happening when I reload a dll, it sometimes has an error.
x Fixing the debug system seems to have solved this too. Keep an eye out tho
x Make debug scope tracking thread safe - was throwing an error in stringsequal but that stopped.
x Keep an eye out.
Application
x File Browsing to load sculptures
x Different Memory Layouts
- x General Purpose - ideally growable but shouldn't exhibit any size change outside of like 1000x sculptures, for assets etc.
- x SACN - growable, in its own memory. This is a central system.
- x Sculpture - per scultpure, monolithic memory allocation
- x track each sculpture in general purpose memory, linked list
- x need to move the assemblies structs back up the list when I remove an earlier one (ie. remove 0, shift 1 to 0, etc.);
- More efficient HSV <-> RGB
x Load/Unload sculptures
- x Load Function
- x Unload Function
- x Call From Interface
- - Make sure that we offload unloading until after all jobs are completed. Otherwise they'll try and write
- to data that doesn't exist
- Save and load a session
- - Serialize Channels
- - Serialize Patterns
- Don't render if the window isn't visible
Development
x Reloadable DLL
x Make sure Debug Info Isn't Lost When We Reload the DLL
- Fix your scope time tracker to account for threads.
- Nest scope times so you can see totals/dig in
x Darken the area behind debug text so you can see it.
- Log memory allocations
Data Output
x output the data (check but I think we're doing this)
x Universe view - pannable grid of universes with labels
Interface
x pattern controls
- fullscreen
- In world interface elements
- - Handles for Patterns
- - UI Popups
- - Value modifiers
- Scroll view
- Update the text system - use system fonts
Switch To Nodes
x basic node elements
- x ports (expected value) (in/out)
- x display value
- x connections
- - evaluation nodes (nodes that we start evaluation from)
- - evaluation step (one node at a time)
- - process to execute
x reflection
- x look at struct members and figure out how to draw it (see notes at bottom)
- x associate with a process
x draw nodes on canvas
x interact
- x move around
- x reconnect
- x move connections handles (see casey)
- serialize
- delete nodes
- need a way to add a timer to the nodes system (oscillators)
- probably want to build some build-in nodes so they can have custom behavior
- - static value: color, float, int etc
- - oscillators
- - hue range
Patterns
x arbitrary assemblies
x reload at runtime
- load patterns from a separate dll
x pattern initialization
- multiple active patterns
- - pattern blending
- - only update active patterns
- Parameters
Structure
x load structure from a file
x generate arbitrary assemblies
- multiple assemblies
- motion
x reasses if control boxes are even a necessary structure
Renderer
x Push Buffer Renderer
x Get Headers out of the command structs
x Render Text Batch Command
x Render Quads Batch Command
x vertex buffer
x depth sorting
- Mouse Picking - point at a led and see info about it
x Camera: tumble controls
- Camera: pan
- Camera: zoom
- Camera: leds always face camera
Resource Management
x Manually Load/Unload Textures
- TODO: Need to figure out which textures are currently in graphics memory and which need to be resubmitted
- Icons
Animation
- timeline
- 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
- Channel: Add/remove pattern by name
- Channel: Set Blend Mode
- Channel: Set Current Pattern
- Pattern: Edit parameter values
Optimization
- patterns are asking to be multithreaded
- probably want to convert as much color functions to use u32 Packed Colors
- - Probably want to think about this more. What about supporting different color depths
- for different output devices?
NOTES
Reflection for Nodes
// These used for the reflection system
#define CANVAS_STRUCT(name) struct name
#define CANVAS_INPUT(type, name) type name
#define CANVAS_OUTPUT(type, name) type name
CANVAS_STRUCT(multiply_data)
{
CANVAS_INPUT(r32, A);
CANVAS_INPUT(r32, B);
CANVAS_OUTPUT(r32, C);
}
CANVAS_PROC(multiply_proc)
{
multiply_data* Data = (multiply_data*)Data;
Data->C = Data->A * Data->B;
}
node
{
InputCount 2
node_port_values* Inputs;
OutputCount 1
node_port_values* Outputs;
}
node_port_value
{
type NodeInputType_r32
s32 MemoryOffsetFromHead;
s32 DataSize;
r32 UnconnectedValue;
r32* ConnectedValue; // Overrides Unconnected
}
u8* GetValueAddress (node_port_value* Port)
{
u8* Result = &Port->UnconnectedValue;
if (Port->ConnectedValue)
{
Result = Port->ConnectedValue;
}
return Result;
}
void UpdateCanvasElement (u8* Data, s32 DataSize, node* Node)
{
for (s32 i = 0; i < Node->InputCount; i++)
{
GSMemCopy(GetValueAddress(&Node->Input[i]),
Data + Node->Input[i].MemoryOffsetFromHead,
Node->Input[i].DataSize);
}
}
void InitializeMultiplyNode ()
{
node Multiply = {};
Multiply.InputCount = 2;
Alloc Inputs
Input[0].
}

548
win32_foldhaus.cpp Normal file
View File

@ -0,0 +1,548 @@
#include <Winsock2.h>
#include <ws2tcpip.h>
#include <intrin.h>
#include "foldhaus_platform.h"
#include "gs_win32.h"
#include "gs_opengl.h"
#include "foldhaus_renderer.cpp"
global_variable b32 Running = false;
global_variable b32 WindowIsActive = false;
char DLLName[] = "foldhaus.dll";
char WorkingDLLName[] = "foldhaus_temp.dll";
char DLLLockFileName[] = "lock.tmp";
win32_window MainWindow;
struct worker_thread_entry
{
b32 IsValid;
u32 Index;
};
struct worker_thread_info
{
s32 ID;
HANDLE Handle;
work_queue* Queue;
};
PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue)
{
Assert(Queue->JobsCount < Queue->JobsMax);
worker_thread_job* Job = Queue->Jobs + Queue->JobsCount;
Job->WorkProc = WorkProc;
Job->Data = Data;
// Complete Past Writes before Future Writes
_WriteBarrier();
_mm_sfence();
++Queue->JobsCount;
ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0);
}
internal worker_thread_entry
CompleteAndTakeNextJob(work_queue* Queue, worker_thread_entry Completed)
{
if (Completed.IsValid)
{
InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted);
}
worker_thread_entry Result = {};
Result.IsValid = false;
u32 OriginalNextJobIndex = Queue->NextJobIndex;
while (OriginalNextJobIndex < Queue->JobsCount)
{
u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex,
OriginalNextJobIndex + 1,
OriginalNextJobIndex);
if (Index == OriginalNextJobIndex)
{
Result.Index = Index;
Result.IsValid = true;
break;
}
OriginalNextJobIndex = Queue->NextJobIndex;
}
return Result;
}
DO_QUEUE_WORK_UNTIL_DONE(Win32DoQueueWorkUntilDone)
{
worker_thread_entry Entry = {};
Entry.IsValid = false;
while (Queue->JobsCompleted < Queue->JobsCount)
{
Entry = CompleteAndTakeNextJob(Queue, Entry);
if (Entry.IsValid)
{
Queue->Jobs[Entry.Index].WorkProc(ThreadID, Queue->Jobs[Entry.Index].Data);
}
}
}
DWORD WINAPI
WorkerThreadProc (LPVOID InputThreadInfo)
{
worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo;
worker_thread_entry Entry = {};
Entry.IsValid = false;
while (true)
{
Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry);
if (Entry.IsValid)
{
ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ID,
ThreadInfo->Queue->Jobs[Entry.Index].Data);
}
else
{
WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0);
}
}
return 0;
}
PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle)
{
s32 Handle = SubmitTexture(Width, Height, Memory);
return Handle;
}
struct win32_socket
{
SOCKET Socket;
};
#define SOCKET_DICTIONARY_GROW_SIZE 32
s32 Win32SocketHandleMax;
s32 Win32SocketHandleCount;
win32_socket* SocketValues;
PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle)
{
if (Win32SocketHandleCount >= Win32SocketHandleMax)
{
s32 NewDictionaryMax = Win32SocketHandleMax + SOCKET_DICTIONARY_GROW_SIZE;
s32 NewDictionaryDataSize = NewDictionaryMax * sizeof(win32_socket);
platform_memory_result DictionaryMemory = Win32Alloc(NewDictionaryDataSize);
Assert(DictionaryMemory.Size > 0);
win32_socket* NewValues = (win32_socket*)(DictionaryMemory.Base);
if (SocketValues)
{
GSMemCopy(SocketValues, NewValues, sizeof(win32_socket) * NewDictionaryMax);
Win32Free((u8*)SocketValues, sizeof(win32_socket) * Win32SocketHandleCount);
}
SocketValues = NewValues;
Win32SocketHandleMax = NewDictionaryMax;
}
Assert(Win32SocketHandleCount < Win32SocketHandleMax);
s32 NewSocketIndex = Win32SocketHandleCount++;
SocketValues[NewSocketIndex].Socket = socket(AddressFamily, Type, Protocol);
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);
platform_memory_result DictionaryMemory = Win32Alloc(NewDictionaryDataSize);
Assert(DictionaryMemory.Size > 0);
sockaddr_in* NewValues = (sockaddr_in*)(DictionaryMemory.Base);
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 = Family;
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;
Assert(SocketIndex < Win32SocketHandleCount);
int Error = setsockopt(SocketValues[SocketIndex].Socket, Level, Option, OptionValue, OptionLength);
if (Error == SOCKET_ERROR)
{
Error = WSAGetLastError();
}
return Error;
}
PLATFORM_SEND_TO(Win32SendTo)
{
s32 SocketIndex = (s32)SocketHandle;
Assert(SocketIndex < Win32SocketHandleCount);
s32 AddressIndex = (s32)AddressHandle;
Assert(AddressIndex < Win32NetworkAddressHandleCount);
s32 LengthSent = sendto(SocketValues[SocketIndex].Socket, Buffer, BufferLength, Flags, (sockaddr*)&NetworkAddressValues[AddressIndex], sizeof(sockaddr_in));
if (LengthSent == SOCKET_ERROR)
{
s32 Error = WSAGetLastError();
InvalidCodePath;
}
return LengthSent;
}
PLATFORM_CLOSE_SOCKET(Win32CloseSocket)
{
s32 SocketIndex = (s32)SocketHandle;
Assert(SocketIndex < Win32SocketHandleCount);
closesocket(SocketValues[SocketIndex].Socket);
}
LRESULT CALLBACK
HandleWindowEvents (HWND WindowHandle, UINT Msg, WPARAM WParam, LPARAM LParam)
{
LRESULT Result = 0;
switch (Msg)
{
case WM_SIZE:
{
Win32UpdateWindowDimension(&MainWindow);
//Win32ResizeDIBSection(&GlobalBackbuffer, MainWindow.Info.Width, MainWindow.Info.Height);
}break;
case WM_CLOSE:
{
Result = DefWindowProc(WindowHandle, Msg, WParam, LParam);
Running = false;
}break;
case WM_DESTROY:
{
}break;
case WM_PAINT:
{
PAINTSTRUCT PaintStruct;
HDC DeviceContext;
b32 PaintResult;
DeviceContext = BeginPaint(WindowHandle, &PaintStruct);
PaintResult = EndPaint(WindowHandle, &PaintStruct);
}break;
case WM_ACTIVATE:
{
WindowIsActive = (LOWORD(WParam) == WA_ACTIVE || LOWORD(WParam) == WA_CLICKACTIVE);
}break;
default:
{
Result = DefWindowProc(WindowHandle, Msg, WParam, LParam);
}
}
return Result;
}
internal void
HandleWindowMessage (MSG Message, win32_window* Window, input_frame* InputFrame)
{
switch (Message.message)
{
case WM_MOUSEWHEEL:
{
int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam);
InputFrame->MouseScroll = MouseWheel;
}break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
{
InputFrame->KeysDown[KeyCode_MouseLeftButton] = (GetKeyState(VK_LBUTTON) & (1 << 15)) != 0;
InputFrame->KeysDown[KeyCode_MouseMiddleButton] = (GetKeyState(VK_MBUTTON) & (1 << 15)) != 0;
InputFrame->KeysDown[KeyCode_MouseRightButton] = (GetKeyState(VK_RBUTTON) & (1 << 15)) != 0;
// NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are
// VK_XBUTTON1 and VK_XBUTTON2
}break;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:
{
int VirtualKey = (int)Message.wParam;
bool KeyDown = (Message.lParam & (1 << 31)) == 0;
int KeyIndex = Win32GetKeyIndex(VirtualKey, true, false);
if (KeyIndex == WIN32_SHOULD_TRANSLATE_TO_CHAR)
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
else
{
InputFrame->KeysDown[KeyIndex] = KeyDown;
}
}break;
case WM_CHAR:
{
char Char = (char)Message.wParam;
InputFrame->StringInput[InputFrame->StringInputUsed++] = Char;
}break;
default:
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}break;
}
}
internal void
DebugPrint (char* Format, ...)
{
char Buffer[256];
va_list Args;
va_start(Args, Format);
PrintFInternal(Buffer, 256, Format, Args);
OutputDebugStringA(Buffer);
va_end(Args);
}
internal void
SetApplicationLinks (context* Context, win32_dll_refresh DLL, work_queue* WorkQueue)
{
if (DLL.IsValid)
{
Context->InitializeApplication = (initialize_application*)GetProcAddress(DLL.DLL, "InitializeApplication");
Context->ReloadStaticData = (reload_static_data*)GetProcAddress(DLL.DLL, "ReloadStaticData");
Context->UpdateAndRender = (update_and_render*)GetProcAddress(DLL.DLL, "UpdateAndRender");
Context->CleanupApplication = (cleanup_application*)GetProcAddress(DLL.DLL, "CleanupApplication");
}
else
{
Context->InitializeApplication = 0;
Context->ReloadStaticData = 0;
Context->UpdateAndRender = 0;
Context->CleanupApplication = 0;
}
}
int WINAPI
WinMain (
HINSTANCE HInstance,
HINSTANCE HPrevInstance,
PSTR CmdLineArgs,
INT NCmdShow
)
{
win32_window_info MainWindowInfo = {};
MainWindowInfo.Name = "Foldhaus";
MainWindowInfo.ClassName = "Foldhaus Window Class";
MainWindowInfo.Width = 1440;
MainWindowInfo.Height = 768;
MainWindowInfo.WindowEventsHandler = HandleWindowEvents;
MainWindow = CreateWin32Window (HInstance, MainWindowInfo);
Win32UpdateWindowDimension(&MainWindow);
win32_opengl_window_info OpenGLWindowInfo = {};
OpenGLWindowInfo.ColorBits = 32;
OpenGLWindowInfo.AlphaBits = 8;
OpenGLWindowInfo.DepthBits = 0;
CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow);
s64 PerformanceCountFrequency = GetPerformanceFrequency();
GlobalDebugServices = (debug_services*)malloc(sizeof(debug_services));
InitDebugServices(GlobalDebugServices, (u8*)malloc(Megabytes(8)), Megabytes(8), 1000, PerformanceCountFrequency);
GlobalDebugServices->GetWallClock = GetWallClock;
input Input;
InitializeInput(&Input);
//
// Set up worker threads
//
const s32 WorkerThreadCount = 2;
worker_thread_info* WorkerThreads = 0;
if (WorkerThreadCount > 0)
{
WorkerThreads = (worker_thread_info*)malloc(sizeof(worker_thread_info) * WorkerThreadCount);
}
work_queue WorkQueue = {};
WorkQueue.SemaphoreHandle = CreateSemaphoreEx(0, 0, WorkerThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);
WorkQueue.JobsMax = 256;
WorkQueue.NextJobIndex = 0;
WorkQueue.PushWorkOnQueue = Win32PushWorkOnQueue;
WorkQueue.DoQueueWorkUntilDone = Win32DoQueueWorkUntilDone;
WorkQueue.ResetWorkQueue = ResetWorkQueue;
for (s32 i = 0; i < WorkerThreadCount; i++)
{
// ID = 0 is reserved for this thread
WorkerThreads[i].ID = i + 1;
WorkerThreads[i].Queue = &WorkQueue;
WorkerThreads[i].Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)&WorkerThreads[i], 0, 0);
}
platform_memory_result InitialMemory = Win32Alloc(Megabytes(64));
context Context = {};
Context.MemorySize = InitialMemory.Size;
Context.MemoryBase = InitialMemory.Base;
Context.WindowWidth = MainWindow.Info.Width;
Context.WindowHeight = MainWindow.Info.Height;
// Platform functions
Context.GeneralWorkQueue = &WorkQueue;
Context.PlatformAlloc = Win32Alloc;
Context.PlatformFree = Win32Free;
Context.PlatformReadEntireFile = ReadEntireFile;
Context.PlatformWriteEntireFile = WriteEntireFile;
Context.PlatformGetFilePath = Win32SystemDialogOpenFile;
Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle;
Context.PlatformGetSocketHandle = Win32GetSocketHandle;
Context.PlatformGetSendAddress = Win32GetSendAddress;
Context.PlatformSetSocketOption = Win32SetSocketOption;
Context.PlatformCloseSocket = Win32CloseSocket;
win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName);
if (HotLoadDLL(&DLLRefresh))
{
SetApplicationLinks(&Context, DLLRefresh, &WorkQueue);
Context.ReloadStaticData(Context, GlobalDebugServices);
}
else
{
InvalidCodePath;
}
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 2), &WSAData);
platform_memory_result RenderMemory = Win32Alloc(Megabytes(32));
render_command_buffer RenderBuffer = AllocateRenderCommandBuffer(RenderMemory.Base, RenderMemory.Size);
Context.InitializeApplication(Context);
Running = true;
Context.WindowIsVisible = true;
while (Running)
{
DEBUG_TRACK_SCOPE(MainLoop);
SwapInputBuffers(&Input);
if (HotLoadDLL(&DLLRefresh))
{
SetApplicationLinks(&Context, DLLRefresh, &WorkQueue);
Context.ReloadStaticData(Context, GlobalDebugServices);
}
MSG Message;
while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE))
{
HandleWindowMessage(Message, &MainWindow, Input.New);
}
{ // Mouse Position
POINT MousePos;
GetCursorPos (&MousePos);
ScreenToClient(MainWindow.Handle, &MousePos);
Input.New->MouseX = MousePos.x;
Input.New->MouseY = MainWindow.Info.Height - MousePos.y;
if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton))
{
Input.MouseDownX = Input.New->MouseX;
Input.MouseDownY = Input.New->MouseY;
}
}
// 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.
Context.WindowWidth = MainWindow.Info.Width;
Context.WindowHeight = MainWindow.Info.Height;
Context.DeltaTime = LastFrameSecondsElapsed;
Context.UpdateAndRender(Context, Input, &RenderBuffer);
RenderCommandBuffer(RenderBuffer);
ClearRenderBuffer(&RenderBuffer);
///////////////////////////////////
// Finish Up
//////////////////////////////////
HDC DeviceContext = GetDC(MainWindow.Handle);
SwapBuffers(DeviceContext);
ReleaseDC(MainWindow.Handle, DeviceContext);
s64 FinishedWorkTime = GetWallClock();
r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency);
while (SecondsElapsed < TargetSecondsPerFrame)
{
u32 SleepTime = 1000.0f * (TargetSecondsPerFrame - SecondsElapsed);
Sleep(SleepTime);
SecondsElapsed = GetSecondsElapsed(LastFrameEnd, GetWallClock(), PerformanceCountFrequency);
}
LastFrameSecondsElapsed = SecondsElapsed;
LastFrameEnd = GetWallClock();
}
Context.CleanupApplication(Context);
s32 CleanupResult = 0;
do {
CleanupResult = WSACleanup();
}while(CleanupResult == SOCKET_ERROR);
for (s32 Thread = 0; Thread < WorkerThreadCount; Thread++)
{
TerminateThread(WorkerThreads[Thread].Handle, 0);
}
return 0;
}