First Commit
This commit is contained in:
parent
56648be357
commit
134a0a1e20
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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);
|
||||||
|
}
|
|
@ -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"
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
|
@ -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);
|
||||||
|
}
|
|
@ -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};
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
|
@ -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;
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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; \
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#ifndef GS_INPUT_H
|
||||||
|
|
||||||
|
#define GS_INPUT_H
|
||||||
|
#endif
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
pattern_registry_entry PatternRegistry[] =
|
||||||
|
{
|
||||||
|
{"Solid", SolidPatternInitProc, SolidPatternUpdateProc},
|
||||||
|
{"Rainbow", InitRainbowPatternProc, RainbowPatternProc},
|
||||||
|
{"Radial", InitRadialProc, UpdateRadialProc},
|
||||||
|
};
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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].
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue