Post Burning Man 22 Cleanup
This commit is contained in:
parent
bf8ff5e506
commit
685ac245cc
|
@ -2,7 +2,7 @@ const fs = require("fs");
|
|||
|
||||
const IN_FILE_PATH_PRIMARY = "../run_tree/data/cities_final.json";
|
||||
const IN_FILE_PATH_SECONDARY = "../run_tree/data/cities_secondary_final.json";
|
||||
const OUT_FILE_PATH = "../src_v2/user_space/incenter_gen_cities.h"
|
||||
const OUT_FILE_PATH = "../src/user_space/incenter_gen_cities.h"
|
||||
|
||||
function print_city_desc (city, prefix, dest, gets_own_universe)
|
||||
{
|
||||
|
|
|
@ -3,7 +3,3 @@ set -e
|
|||
SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
$SCRIPT_REL_DIR/build_.sh debug osx arm64
|
||||
# $SCRIPT_REL_DIR/build_.sh debug wasm intel
|
||||
|
||||
# pushd "run_tree/raspi/arm64/debug"
|
||||
# clang -o lumenarium /home/pi/dev/Lumenarium/src_v2/platform/raspi/lumenarium_first_raspi.c -lm
|
||||
# popd
|
||||
|
|
|
@ -95,11 +95,11 @@ Compiler_linux="clang++"
|
|||
|
||||
# Platform Entry Points
|
||||
|
||||
PlatformEntry_win32="src_v2/platform/win32/lumenarium_first_win32.cpp"
|
||||
PlatformEntry_osx="src_v2/platform/osx/lumenarium_first_osx.c"
|
||||
PlatformEntry_wasm="src_v2/platform/wasm/lumenarium_first_wasm.cpp"
|
||||
PlatformEntry_linux="src_v2/platform/linux/lumenarium_first_linux.cpp"
|
||||
PlatformEntry_raspi="src_v2/platform/raspi/lumenarium_first_raspi.c"
|
||||
PlatformEntry_win32="src/platform/win32/lumenarium_first_win32.cpp"
|
||||
PlatformEntry_osx="src/platform/osx/lumenarium_first_osx.c"
|
||||
PlatformEntry_wasm="src/platform/wasm/lumenarium_first_wasm.cpp"
|
||||
PlatformEntry_linux="src/platform/linux/lumenarium_first_linux.cpp"
|
||||
PlatformEntry_raspi="src/platform/raspi/lumenarium_first_raspi.c"
|
||||
|
||||
# Intermediate Outputs
|
||||
|
||||
|
@ -118,7 +118,7 @@ LinkerOutput_linux=""
|
|||
LinkerOutput_raspi="lumenarium"
|
||||
|
||||
# Wasm Sys Root
|
||||
WasmSysRoot="${PROJECT_PATH}/src_v2/platform/wasm/sysroot/"
|
||||
WasmSysRoot="${PROJECT_PATH}/src/platform/wasm/sysroot/"
|
||||
|
||||
# Compiler Flags
|
||||
|
||||
|
@ -197,7 +197,7 @@ LinkerFlags_PROD=""
|
|||
LinkerLibs_win32="user32.lib kernel32.lib gdi32.lib opengl32.lib"
|
||||
# winmm.lib gdi32.lib dsound.lib Ws2_32.lib Comdlg32.lib Winspool.lib"
|
||||
|
||||
LinkerLibs_osx="-framework OpenGL -framework Cocoa -framework IOKit ${PROJECT_PATH}/src_v2/libs/glfw_osx/lib-universal/libglfw3.a"
|
||||
LinkerLibs_osx="-framework OpenGL -framework Cocoa -framework IOKit ${PROJECT_PATH}/src/libs/glfw_osx/lib-universal/libglfw3.a"
|
||||
LinkerLibs_wasm=""
|
||||
LinkerLibs_linux=""
|
||||
LinkerLibs_raspi=""
|
||||
|
@ -354,13 +354,13 @@ then
|
|||
-o $LinkerOutput \
|
||||
$EntryPath
|
||||
cp \
|
||||
"${PROJECT_PATH}/src_v2/platform/wasm/lumenarium_wasm_imports.js" \
|
||||
"${PROJECT_PATH}/src/platform/wasm/lumenarium_wasm_imports.js" \
|
||||
./lumenarium_wasm_imports.js
|
||||
|
||||
else
|
||||
|
||||
# Preprocessing Steps
|
||||
ConvertCsvEntry="${PROJECT_PATH}/src_v2/tools/convert_csv.c"
|
||||
ConvertCsvEntry="${PROJECT_PATH}/src/tools/convert_csv.c"
|
||||
$Compiler \
|
||||
-o convert_csv \
|
||||
$CompilerFlags \
|
||||
|
|
|
@ -16,8 +16,7 @@ blacklist_patterns = {
|
|||
load_paths_base = {
|
||||
{ ".", .relative = true, .recursive = false, },
|
||||
{ "build", .relative = true, .recursive = false, },
|
||||
// { "src", .relative = true, .recursive = true, },
|
||||
{ "src_v2", .relative = true, .recursive = true, },
|
||||
{ "src", .relative = true, .recursive = true, },
|
||||
{ "meta", .relative = true, .recursive = true, },
|
||||
{ "gs_libs", .relative = true, .recursive = true, },
|
||||
};
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,159 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_default_nodes.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_DEFAULT_NODES_H
|
||||
|
||||
//////////////////////////////////
|
||||
//
|
||||
// Values
|
||||
//
|
||||
/////////////////////////////////
|
||||
|
||||
NODE_STRUCT(float_value_data)
|
||||
{
|
||||
NODE_IN(r32, Value);
|
||||
NODE_OUT(r32, Result);
|
||||
};
|
||||
|
||||
NODE_PROC(FloatValue, float_value_data)
|
||||
{
|
||||
Data->Result = Data->Value;
|
||||
}
|
||||
|
||||
NODE_STRUCT(vector_data)
|
||||
{
|
||||
NODE_IN(r32, X);
|
||||
NODE_IN(r32, Y);
|
||||
NODE_IN(r32, Z);
|
||||
NODE_IN(r32, W);
|
||||
NODE_OUT(v4, Result);
|
||||
};
|
||||
|
||||
NODE_PROC(VectorValue, vector_data)
|
||||
{
|
||||
Data->Result = v4{Data->X, Data->Y, Data->Z, Data->W};
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
//
|
||||
// Arithmetic
|
||||
//
|
||||
/////////////////////////////////
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
//
|
||||
// Animators
|
||||
//
|
||||
/////////////////////////////////
|
||||
|
||||
GSMetaTag(node_struct);
|
||||
struct sin_wave_data
|
||||
{
|
||||
GSMetaTag(node_input);
|
||||
r32 Period;
|
||||
|
||||
GSMetaTag(node_input);
|
||||
r32 Min;
|
||||
|
||||
GSMetaTag(node_input);
|
||||
r32 Max;
|
||||
|
||||
GSMetaTag(node_input);
|
||||
r32 Result;
|
||||
|
||||
r32 Accumulator;
|
||||
};
|
||||
|
||||
NODE_PROC(SinWave, sin_wave_data)
|
||||
{
|
||||
Data->Accumulator += DeltaTime;
|
||||
if (Data->Period > 0)
|
||||
{
|
||||
while (Data->Accumulator > Data->Period)
|
||||
{
|
||||
Data->Accumulator -= Data->Period;
|
||||
}
|
||||
|
||||
r32 ActualMin = Min(Data->Min, Data->Max);
|
||||
r32 ActualMax = Max(Data->Min, Data->Max);
|
||||
r32 SinResult = SinR32((Data->Accumulator / Data->Period) * PiR32 * 2);
|
||||
Data->Result = RemapR32(SinResult, -1.f, 1.f, ActualMin, ActualMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
Data->Result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
//
|
||||
// Pattern Mixing
|
||||
//
|
||||
/////////////////////////////////
|
||||
|
||||
GSMetaTag(node_struct);
|
||||
struct multiply_patterns_data
|
||||
{
|
||||
GSMetaTag(node_input);
|
||||
color_buffer A;
|
||||
|
||||
GSMetaTag(node_input);
|
||||
color_buffer B;
|
||||
|
||||
GSMetaTag(node_output);
|
||||
color_buffer Result;
|
||||
};
|
||||
|
||||
NODE_PROC(MultiplyPatterns, multiply_patterns_data)
|
||||
{
|
||||
for (s32 LedIndex = 0; LedIndex < Data->Result.LEDCount; LedIndex++)
|
||||
{
|
||||
Assert(LedIndex >= 0 && LedIndex < Data->Result.LEDCount);
|
||||
|
||||
s32 AR = Data->A.Colors[LedIndex].R;
|
||||
s32 AG = Data->A.Colors[LedIndex].G;
|
||||
s32 AB = Data->A.Colors[LedIndex].B;
|
||||
|
||||
s32 BR = Data->B.Colors[LedIndex].R;
|
||||
s32 BG = Data->B.Colors[LedIndex].G;
|
||||
s32 BB = Data->B.Colors[LedIndex].B;
|
||||
|
||||
s32 RCombined = (AR * BR) / 255;
|
||||
s32 GCombined = (AG * BG) / 255;
|
||||
s32 BCombined = (AB * BB) / 255;
|
||||
|
||||
Data->Result.Colors[LedIndex].R = (u8)RCombined;
|
||||
Data->Result.Colors[LedIndex].G = (u8)GCombined;
|
||||
Data->Result.Colors[LedIndex].B = (u8)BCombined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_DEFAULT_NODES_H
|
||||
#endif // FOLDHAUS_DEFAULT_NODES_H
|
|
@ -1,188 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_node.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_NODE_CPP
|
||||
|
||||
internal b32
|
||||
MemberIsInput(gsm_struct_member_type_info Member)
|
||||
{
|
||||
b32 Result = (0 <= gsm_GetMetaTagIndex(MetaTag_node_input, Member.Tags, Member.TagsCount));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal b32
|
||||
MemberIsOutput(gsm_struct_member_type_info Member)
|
||||
{
|
||||
b32 Result = (0 <= gsm_GetMetaTagIndex(MetaTag_node_output, Member.Tags, Member.TagsCount));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
ClearNodeWorkspaceStorage(pattern_node_workspace* Workspace)
|
||||
{
|
||||
ClearArena(&Workspace->Storage);
|
||||
Workspace->SparseToSortedNodeMap = 0;
|
||||
Workspace->SortedNodeHandles = 0;
|
||||
}
|
||||
|
||||
struct adjacency_list
|
||||
{
|
||||
// TODO(Peter): Can make these as buffers, not single digits later
|
||||
gs_list_handle NodeHandle;
|
||||
adjacency_list* Next;
|
||||
};
|
||||
|
||||
internal u32
|
||||
SortNodeNeighbors(u32 ContiguousNodeIndex, gs_list_handle NodeHandle, adjacency_list** NeighborsLists, b8* NodesVisited, gs_list_handle* SortedNodeHandles, u32 SortedNodesCount, s32* SparseToContiguousNodeMap)
|
||||
{
|
||||
NodesVisited[ContiguousNodeIndex] = true;
|
||||
|
||||
adjacency_list* Neighbor = NeighborsLists[ContiguousNodeIndex];
|
||||
while (Neighbor)
|
||||
{
|
||||
u32 ContiguousNeighborNodeIndex = SparseToContiguousNodeMap[Neighbor->NodeHandle.Index];
|
||||
if (!NodesVisited[ContiguousNeighborNodeIndex])
|
||||
{
|
||||
SortedNodesCount = SortNodeNeighbors(ContiguousNeighborNodeIndex, Neighbor->NodeHandle, NeighborsLists, NodesVisited, SortedNodeHandles, SortedNodesCount, SparseToContiguousNodeMap);
|
||||
}
|
||||
Neighbor = Neighbor->Next;
|
||||
}
|
||||
|
||||
SortedNodeHandles[SortedNodesCount++] = NodeHandle;
|
||||
return SortedNodesCount;
|
||||
}
|
||||
|
||||
internal s32*
|
||||
CreateSparseToContiguousMap (pattern_node_workspace Workspace, gs_memory_arena* Scratch)
|
||||
{
|
||||
s32* Result = PushArray(Scratch, s32, Workspace.Nodes.OnePastLastUsed);
|
||||
s32 ContiguousIndex = 0;
|
||||
for (u32 SparseNodeIndex = 0; SparseNodeIndex < Workspace.Nodes.OnePastLastUsed; SparseNodeIndex++)
|
||||
{
|
||||
gs_list_entry<pattern_node>* Entry = Workspace.Nodes.GetEntryAtIndex(SparseNodeIndex);
|
||||
if (!EntryIsFree(Entry))
|
||||
{
|
||||
Result[SparseNodeIndex] = ContiguousIndex++;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
UpdateSortedNodes(pattern_node_workspace* Workspace, gs_memory_arena* Scratch)
|
||||
{
|
||||
ClearNodeWorkspaceStorage(Workspace);
|
||||
|
||||
u32 NodeCount = Workspace->Nodes.Used;
|
||||
u32 SparseNodeCount = Workspace->Nodes.OnePastLastUsed;
|
||||
|
||||
s32* SparseToContiguousNodeMap = CreateSparseToContiguousMap(*Workspace, &Workspace->Storage);
|
||||
|
||||
// NOTE(Peter): We need to sort this later on so I'm just storing list lengths in this format
|
||||
// to begin with.
|
||||
// NeighborsListLengths[n].Radix = the number of neighbors for the node
|
||||
// NeighborsListLengths[n].ID = the sparse array index of the node
|
||||
gs_radix_entry* NeighborsListLengths = PushArray(Scratch, gs_radix_entry, NodeCount);
|
||||
adjacency_list** NeighborsLists = PushArray(Scratch, adjacency_list*, NodeCount);
|
||||
GSZeroArray(NeighborsLists, adjacency_list*, SparseNodeCount);
|
||||
|
||||
// Fill Radix
|
||||
for (u32 n = 0; n < SparseNodeCount; n++)
|
||||
{
|
||||
s32 ContiguousIndex = SparseToContiguousNodeMap[n];
|
||||
if (ContiguousIndex >= 0)
|
||||
{
|
||||
NeighborsListLengths[ContiguousIndex].Radix = 0;
|
||||
NeighborsListLengths[ContiguousIndex].ID = n;
|
||||
}
|
||||
}
|
||||
|
||||
// Construct Adjaceny List
|
||||
for (u32 c = 0; c < Workspace->Connections.Used; c++)
|
||||
{
|
||||
pattern_node_connection Connection = *Workspace->Connections.GetElementAtIndex(c);
|
||||
|
||||
adjacency_list* ListAddition = PushStruct(Scratch, adjacency_list);
|
||||
ListAddition->NodeHandle = Connection.DownstreamNodeHandle;
|
||||
|
||||
s32 ContiguousNodeIndex = SparseToContiguousNodeMap[Connection.UpstreamNodeHandle.Index];
|
||||
ListAddition->Next = NeighborsLists[ContiguousNodeIndex];
|
||||
NeighborsLists[ContiguousNodeIndex] = ListAddition;
|
||||
|
||||
// Increment the number of neighbors - stored in Radix
|
||||
NeighborsListLengths[ContiguousNodeIndex].Radix++;
|
||||
}
|
||||
|
||||
// Sort by number of neighbors
|
||||
RadixSortInPlace(NeighborsListLengths, Workspace->Nodes.Used);
|
||||
|
||||
char* OutputCharArray = PushArray(Scratch, char, 1024);
|
||||
gs_string Outputgs_string = MakeString(OutputCharArray, 0, 1024);
|
||||
|
||||
PrintF(&Outputgs_string, "Neighbors Lists: \n");
|
||||
for (u32 d = 0; d < Workspace->Nodes.Used; d++)
|
||||
{
|
||||
PrintF(&Outputgs_string, " %d: Node [ %d ] : neighbors { ", d, NeighborsListLengths[d].ID);
|
||||
|
||||
adjacency_list* Neighbors = NeighborsLists[d];
|
||||
while (Neighbors)
|
||||
{
|
||||
PrintF(&Outputgs_string, "%d, ", Neighbors->NodeHandle.Index);
|
||||
Neighbors = Neighbors->Next;
|
||||
}
|
||||
PrintF(&Outputgs_string, " }\n");
|
||||
}
|
||||
NullTerminate(&Outputgs_string);
|
||||
|
||||
// This is a contiguous array.
|
||||
b8* NodesVisited = PushArray(Scratch, b8, NodeCount);
|
||||
GSZeroArray(NodesVisited, b8, NodeCount);
|
||||
|
||||
Workspace->SortedNodeHandles = PushArray(&Workspace->Storage, gs_list_handle, NodeCount);
|
||||
u32 SortedSparseNodeIndeciesUsed = 0;
|
||||
|
||||
for (u32 n = 0; n < Workspace->Nodes.Used; n++)
|
||||
{
|
||||
gs_radix_entry SortedNeighborsCount = NeighborsListLengths[n];
|
||||
u32 NeighborCount = SortedNeighborsCount.Radix;
|
||||
u32 NodeIndex = SortedNeighborsCount.ID;
|
||||
gs_list_handle NodeHandle = Workspace->Nodes.GetEntryAtIndex(NodeIndex)->Handle;
|
||||
u32 ContiguousNodeIndex = SparseToContiguousNodeMap[NodeIndex];
|
||||
|
||||
SortedSparseNodeIndeciesUsed = SortNodeNeighbors(ContiguousNodeIndex, NodeHandle, NeighborsLists, NodesVisited, Workspace->SortedNodeHandles, SortedSparseNodeIndeciesUsed, SparseToContiguousNodeMap);
|
||||
}
|
||||
|
||||
Workspace->SparseToSortedNodeMap = SparseToContiguousNodeMap;
|
||||
for (u32 SortedIndex = 0; SortedIndex < NodeCount; SortedIndex++)
|
||||
{
|
||||
gs_list_handle SortedHandle = Workspace->SortedNodeHandles[SortedIndex];
|
||||
Workspace->SparseToSortedNodeMap[SortedHandle.Index] = SortedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
PushNodeOnWorkspace(s32 NodeSpecificationIndex, pattern_node_workspace* Workspace, gs_memory_arena* Scratch)
|
||||
{
|
||||
pattern_node* NewNode = Workspace->Nodes.TakeElement();
|
||||
NewNode->SpecificationIndex = NodeSpecificationIndex;
|
||||
|
||||
UpdateSortedNodes(Workspace, Scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
PushNodeConnectionOnWorkspace(gs_list_handle UpstreamNodeHandle, u32 UpstreamPortIndex, gs_list_handle DownstreamNodeHandle, u32 DownstreamPortIndex, pattern_node_workspace* Workspace, gs_memory_arena* Scratch)
|
||||
{
|
||||
pattern_node_connection Connection = {};
|
||||
Connection.UpstreamNodeHandle = UpstreamNodeHandle;
|
||||
Connection.DownstreamNodeHandle = DownstreamNodeHandle;
|
||||
Connection.UpstreamPortIndex = UpstreamPortIndex;
|
||||
Connection.DownstreamPortIndex = DownstreamPortIndex;
|
||||
|
||||
Workspace->Connections.PushElementOnBucket(Connection);
|
||||
UpdateSortedNodes(Workspace, Scratch);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_NODE_CPP
|
||||
#endif // FOLDHAUS_NODE_CPP
|
|
@ -1,134 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_node.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_NODE_H
|
||||
|
||||
typedef enum node_type node_type;
|
||||
|
||||
#define IsInputMember 1 << 0
|
||||
#define IsOutputMember 1 << 1
|
||||
|
||||
#define DEFAULT_NODE_DIMENSION v2{125, 150}
|
||||
|
||||
struct color_buffer
|
||||
{
|
||||
v4* LedPositions;
|
||||
pixel* Colors;
|
||||
s32 LEDCount;
|
||||
};
|
||||
|
||||
// TODO(Peter): Use the Meta RTTI
|
||||
// :UseMetaInfo
|
||||
enum struct_member_type
|
||||
{
|
||||
MemberType_Invalid,
|
||||
MemberType_s32,
|
||||
MemberType_r32,
|
||||
MemberType_v4,
|
||||
MemberType_NODE_COLOR_BUFFER,
|
||||
MemberTypeCount,
|
||||
};
|
||||
|
||||
// TODO(Peter): Use the Meta RTTI
|
||||
// :UseMetaInfo
|
||||
struct node_struct_member
|
||||
{
|
||||
struct_member_type Type;
|
||||
char* Name;
|
||||
u64 Offset;
|
||||
b32 IsInput;
|
||||
};
|
||||
|
||||
// :UseMetaInfo
|
||||
struct node_specification
|
||||
{
|
||||
node_type Type;
|
||||
|
||||
char* Name;
|
||||
s32 NameLength;
|
||||
|
||||
node_struct_member* MemberList;
|
||||
u32 DataStructSize;
|
||||
u32 MemberListLength;
|
||||
|
||||
b32 IsPattern;
|
||||
};
|
||||
|
||||
struct node_specification_
|
||||
{
|
||||
node_type Type;
|
||||
gs_string Identifier;
|
||||
gsm_struct_type DataType;
|
||||
};
|
||||
|
||||
struct pattern_node
|
||||
{
|
||||
// TODO(Peter): Something to think about further down the line is the fact that
|
||||
// SpecificationIndex doesn't have to stay static throughout a single instance of
|
||||
// an application, let alone across separate runs. If you recompile (hot load or not)
|
||||
// with a new specification, the indecies all get thrown off. Should we hash the spec
|
||||
// names or something?
|
||||
|
||||
// TODO(Peter): A more immediate thing to handle is that u32 should really be node_type
|
||||
u32 SpecificationIndex;
|
||||
};
|
||||
|
||||
struct pattern_node_connection
|
||||
{
|
||||
gs_list_handle UpstreamNodeHandle;
|
||||
gs_list_handle DownstreamNodeHandle;
|
||||
|
||||
u32 UpstreamPortIndex;
|
||||
u32 DownstreamPortIndex;
|
||||
};
|
||||
|
||||
struct pattern_node_workspace
|
||||
{
|
||||
gs_list<pattern_node> Nodes;
|
||||
gs_bucket<pattern_node_connection> Connections;
|
||||
|
||||
// This is storage for all the structures which follow.
|
||||
// It is cleared when new nodes are added so that the
|
||||
// acceleration structures can be recalculated
|
||||
gs_memory_arena Storage;
|
||||
s32* SparseToSortedNodeMap;
|
||||
gs_list_handle* SortedNodeHandles;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// 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, r32 DeltaTime)
|
||||
|
||||
#define NODE_IN(type, name) type name
|
||||
#define NODE_OUT(type, name) type name
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// OUTPUT NODE
|
||||
///////////////////////////////////////////////
|
||||
|
||||
struct output_node_data
|
||||
{
|
||||
GSMetaTag(node_input);
|
||||
color_buffer Result;
|
||||
};
|
||||
|
||||
void OutputNode(output_node_data* Data, r32 DeltaTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#define FOLDHAUS_NODE_H
|
||||
#endif // FOLDHAUS_NODE_H
|
|
@ -1,24 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_node_gui.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_NODE_GUI_H
|
||||
|
||||
struct gui_node
|
||||
{
|
||||
s32 Handle;
|
||||
node_type Type;
|
||||
v2 Min, Dim;
|
||||
};
|
||||
|
||||
#define GUI_NODES_MAX 256
|
||||
struct gui_node_list
|
||||
{
|
||||
s32 NodesUsed;
|
||||
gui_node Nodes[GUI_NODES_MAX];
|
||||
};
|
||||
|
||||
|
||||
#define FOLDHAUS_NODE_GUI_H
|
||||
#endif // FOLDHAUS_NODE_GUI_H
|
|
@ -1,546 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_node_graph.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_NODE_GRAPH_H
|
||||
|
||||
struct visual_node
|
||||
{
|
||||
node_specification_ Spec;
|
||||
v2 Position;
|
||||
};
|
||||
|
||||
struct visual_port
|
||||
{
|
||||
gs_list_handle SparseNodeHandle;
|
||||
u32 PortIndex;
|
||||
rect2 PortBounds;
|
||||
};
|
||||
|
||||
struct visual_connection
|
||||
{
|
||||
u32 UpstreamVisualPortIndex;
|
||||
u32 DownstreamVisualPortIndex;
|
||||
v2 UpstreamPosition;
|
||||
v2 DownstreamPosition;
|
||||
};
|
||||
|
||||
struct node_layout
|
||||
{
|
||||
// NOTE(Peter): This Map is a sparse array.
|
||||
// index i corresponds to index i in some list of nodes
|
||||
// the value at index i is the index of that node in a compressed list
|
||||
// if the value at i is -1, that means the entry is free
|
||||
s32* SparseToContiguousNodeMap;
|
||||
u32 SparseToContiguousNodeMapCount;
|
||||
|
||||
visual_node* VisualNodes;
|
||||
u32* VisualNodeLayers;
|
||||
u32 VisualNodesCount;
|
||||
|
||||
visual_port* VisualPorts;
|
||||
u32 VisualPortsCount;
|
||||
|
||||
visual_connection* VisualConnections;
|
||||
u32 VisualConnectionsCount;
|
||||
|
||||
u32 LayerCount;
|
||||
v2* LayerPositions;
|
||||
|
||||
b32 ConnectionIsInProgress;
|
||||
v2 InProgressConnectionStart;
|
||||
v2 InProgressConnectionEnd;
|
||||
};
|
||||
|
||||
struct node_graph_state
|
||||
{
|
||||
v2 ViewOffset;
|
||||
|
||||
gs_memory_arena LayoutMemory;
|
||||
node_layout Layout;
|
||||
|
||||
b32 LayoutIsDirty;
|
||||
};
|
||||
|
||||
//
|
||||
// Pan Node Graph
|
||||
//
|
||||
|
||||
OPERATION_STATE_DEF(pan_node_graph_operation_state)
|
||||
{
|
||||
v2 InitialViewOffset;
|
||||
|
||||
// TODO(Peter): I DON"T LIKE THIS!!!!
|
||||
// We should have a way to access the panel that created an operation mode or something
|
||||
v2* ViewOffset;
|
||||
};
|
||||
|
||||
OPERATION_RENDER_PROC(UpdatePanNodeGraph)
|
||||
{
|
||||
pan_node_graph_operation_state* OpState = (pan_node_graph_operation_state*)Operation.OpStateMemory;
|
||||
v2 MouseDelta = Mouse.Pos - Mouse.DownPos;
|
||||
*OpState->ViewOffset = MouseDelta + OpState->InitialViewOffset;
|
||||
}
|
||||
|
||||
input_command PanNodeGraphCommands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndCurrentOperationMode },
|
||||
};
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(BeginPanNodeGraph)
|
||||
{
|
||||
operation_mode* PanNodeGraph = ActivateOperationModeWithCommands(&State->Modes, PanNodeGraphCommands, UpdatePanNodeGraph);
|
||||
pan_node_graph_operation_state* OpState = CreateOperationState(PanNodeGraph, &State->Modes, pan_node_graph_operation_state);
|
||||
|
||||
panel* NodeGraphPanel = State->HotPanel;
|
||||
node_graph_state* NodeGraphState = (node_graph_state*)NodeGraphPanel->PanelStateMemory;
|
||||
OpState->InitialViewOffset = NodeGraphState->ViewOffset;
|
||||
OpState->ViewOffset = &NodeGraphState->ViewOffset;
|
||||
}
|
||||
|
||||
//
|
||||
// Connect Nodes
|
||||
//
|
||||
|
||||
OPERATION_STATE_DEF(connect_nodes_operation_state)
|
||||
{
|
||||
visual_port VisualPort;
|
||||
u32 VisualPortIndex;
|
||||
b32 IsInput;
|
||||
};
|
||||
|
||||
OPERATION_RENDER_PROC(UpdateConnectNodeOperation)
|
||||
{
|
||||
panel_with_layout NodeGraphPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelSystem, State->WindowBounds);
|
||||
node_graph_state* GraphState = (node_graph_state*)NodeGraphPanel.Panel->PanelStateMemory;
|
||||
|
||||
GraphState->Layout.InProgressConnectionEnd = Mouse.Pos;
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndConnectNodesOperation)
|
||||
{
|
||||
connect_nodes_operation_state* OpState = GetCurrentOperationState(State->Modes, connect_nodes_operation_state);
|
||||
|
||||
panel_with_layout NodeGraphPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelSystem, State->WindowBounds);
|
||||
node_graph_state* GraphState = (node_graph_state*)NodeGraphPanel.Panel->PanelStateMemory;
|
||||
GraphState->Layout.ConnectionIsInProgress = false;
|
||||
|
||||
for (u32 p = 0; p < GraphState->Layout.VisualPortsCount; p++)
|
||||
{
|
||||
visual_port VisualPort = GraphState->Layout.VisualPorts[p];
|
||||
rect2 ViewAdjustedBounds = gs_RectOffsetByVector(VisualPort.PortBounds, GraphState->ViewOffset);
|
||||
if (gs_PointIsInRect(Mouse.Pos, ViewAdjustedBounds))
|
||||
{
|
||||
visual_port UpstreamPort = (OpState->IsInput & IsInputMember) ? VisualPort : OpState->VisualPort;
|
||||
visual_port DownstreamPort = (OpState->IsInput & IsInputMember) ? OpState->VisualPort : VisualPort;
|
||||
|
||||
PushNodeConnectionOnWorkspace(UpstreamPort.SparseNodeHandle, UpstreamPort.PortIndex,
|
||||
DownstreamPort.SparseNodeHandle, DownstreamPort.PortIndex,
|
||||
&State->NodeWorkspace, &State->Transient);
|
||||
|
||||
GraphState->LayoutIsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
EndCurrentOperationMode(State, Event, Mouse);
|
||||
}
|
||||
|
||||
input_command ConnectNodesOperationCommands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndConnectNodesOperation },
|
||||
};
|
||||
|
||||
internal void
|
||||
BeginConnectNodesOperation(visual_port VisualPort, u32 VisualPortIndex, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
operation_mode* ConnectNodesOperation = ActivateOperationModeWithCommands(&State->Modes, ConnectNodesOperationCommands, UpdateConnectNodeOperation);
|
||||
connect_nodes_operation_state* OpState = CreateOperationState(ConnectNodesOperation, &State->Modes, connect_nodes_operation_state);
|
||||
OpState->VisualPort = VisualPort;
|
||||
OpState->VisualPortIndex = VisualPortIndex;
|
||||
|
||||
panel_with_layout NodeGraphPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelSystem, State->WindowBounds);
|
||||
node_graph_state* GraphState = (node_graph_state*)NodeGraphPanel.Panel->PanelStateMemory;
|
||||
|
||||
GraphState->Layout.ConnectionIsInProgress = true;
|
||||
GraphState->Layout.InProgressConnectionStart = gs_CalculateRectCenter(VisualPort.PortBounds);
|
||||
}
|
||||
|
||||
//
|
||||
// Node Graph Panel
|
||||
//
|
||||
|
||||
GSMetaTag(panel_commands);
|
||||
input_command NodeGraph_Commands[] = {{}};
|
||||
s32 NodeGraph_CommandsCount = 0;
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_node_graph);
|
||||
internal void
|
||||
NodeGraph_Init(panel* Panel, app_state* State)
|
||||
{
|
||||
// TODO(Peter): We aren't able to free this memory. We need a system for
|
||||
// taking fixed size chunks off the Memory stack and then reusing them. THis
|
||||
// should probably live outside the paneling system.
|
||||
// TODO: :FreePanelMemory
|
||||
Panel->PanelStateMemory = (u8*)PushStruct(&State->Permanent, node_graph_state);
|
||||
node_graph_state* GraphState = (node_graph_state*)Panel->PanelStateMemory;
|
||||
GraphState->LayoutIsDirty = true;
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_node_graph);
|
||||
internal void
|
||||
NodeGraph_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
node_graph_state* GraphState = (node_graph_state*)Panel->PanelStateMemory;
|
||||
FreeMemoryArena(&GraphState->LayoutMemory);
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawGrid (v2 Offset, v2 GridSquareDim, rect2 PanelBounds, render_command_buffer* RenderBuffer)
|
||||
{
|
||||
r32 LineValue = .16f;
|
||||
v4 LineColor = v4{LineValue, LineValue, LineValue, 1.f};
|
||||
|
||||
v2 GridSquareOffset = v2{
|
||||
GSModF(Offset.x, GridSquareDim.x),
|
||||
GSModF(Offset.y, GridSquareDim.y),
|
||||
};
|
||||
v2 GridOrigin = PanelBounds.Min + GridSquareOffset;
|
||||
|
||||
// Draw Vertical Lines
|
||||
r32 XOffset = 0;
|
||||
while (GridOrigin.x + XOffset < PanelBounds.Max.x)
|
||||
{
|
||||
v2 LineMin = v2{ GridOrigin.x + XOffset, PanelBounds.Min.y };
|
||||
v2 LineMax = v2{ LineMin.x + 1, PanelBounds.Max.y };
|
||||
PushRenderQuad2D(RenderBuffer, LineMin, LineMax, LineColor);
|
||||
|
||||
XOffset += GridSquareDim.x;
|
||||
}
|
||||
|
||||
// Draw Horizontal Lines
|
||||
r32 YOffset = 0;
|
||||
while (GridOrigin.y + YOffset < PanelBounds.Max.y)
|
||||
{
|
||||
v2 LineMin = v2{ PanelBounds.Min.x, GridOrigin.y + YOffset };
|
||||
v2 LineMax = v2{ PanelBounds.Max.x, LineMin.y + 1, };
|
||||
PushRenderQuad2D(RenderBuffer, LineMin, LineMax, LineColor);
|
||||
|
||||
YOffset += GridSquareDim.y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawNodePorts(gsm_struct_type_info NodeDataTypeInfo, b32 InputMask, v2 Position, r32 LineHeight, gs_string_alignment TextAlign, v2 TextOffset, interface_config Interface, render_command_buffer* RenderBuffer, mouse_state Mouse)
|
||||
{
|
||||
rect2 PortBounds = rect2{v2{0, 0}, v2{6, 6}};
|
||||
|
||||
v2 LinePosition = Position;
|
||||
for (u32 i = 0; i < NodeDataTypeInfo.MembersCount; i++)
|
||||
{
|
||||
gsm_struct_member_type_info Member = NodeDataTypeInfo.Members[i];
|
||||
if (MemberIsInput(Member))
|
||||
{
|
||||
// TODO(Peter): Can we make this rely on the same data that we use to
|
||||
// render the actual connection points?
|
||||
gs_string MemberName = MakeString(Member.Identifier, Member.IdentifierLength);
|
||||
DrawString(RenderBuffer, MemberName, Interface.Font, LinePosition + TextOffset, WhiteV4, TextAlign);
|
||||
LinePosition.y -= LineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawNode (v2 Position, node_specification_ NodeSpecification, gs_list_handle NodeHandle, r32 NodeWidth, r32 LineHeight, interface_config Interface, render_command_buffer* RenderBuffer, mouse_state Mouse, gs_memory_arena* Scratch)
|
||||
{
|
||||
gsm_struct_type_info NodeDataTypeInfo = StructTypes[NodeSpecification.DataType];
|
||||
|
||||
u32 InputMembers = 0;
|
||||
u32 OutputMembers = 0;
|
||||
for (u32 i = 0; i < NodeDataTypeInfo.MembersCount; i++)
|
||||
{
|
||||
gsm_struct_member_type_info Member = NodeDataTypeInfo.Members[i];
|
||||
if (MemberIsInput(Member)) { InputMembers++; }
|
||||
if (MemberIsOutput(Member)) { OutputMembers++; }
|
||||
}
|
||||
u32 LineCount = 1 + Max(InputMembers, OutputMembers);
|
||||
|
||||
v2 NodeDim = v2{
|
||||
NodeWidth,
|
||||
(LineHeight * LineCount) + Interface.Margin.y,
|
||||
};
|
||||
rect2 NodeBounds = rect2{
|
||||
v2{ Position.x, Position.y - NodeDim.y },
|
||||
v2{ Position.x + NodeDim.x, Position.y },
|
||||
};
|
||||
|
||||
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.16f, .16f, .16f, 1.f});
|
||||
|
||||
v2 LinePosition = v2{ NodeBounds.Min.x, NodeBounds.Max.y - LineHeight };
|
||||
v2 TextOffset = v2{Interface.Margin.x, 0};
|
||||
|
||||
PushRenderQuad2D(RenderBuffer, LinePosition, LinePosition + v2{NodeWidth, LineHeight}, v4{1.f, .24f, .39f, 1.f});
|
||||
|
||||
gs_string NodePrintName = MakeString(PushArray(Scratch, char, 256), 0, 256);
|
||||
PrintF(&NodePrintName, "%S [%d]", NodeSpecification.Identifier, NodeHandle.Index);
|
||||
DrawString(RenderBuffer, NodePrintName, Interface.Font, LinePosition + TextOffset, WhiteV4);
|
||||
LinePosition.y -= LineHeight;
|
||||
|
||||
v2 InputLinePosition = LinePosition;
|
||||
v2 OutputLinePosition = v2{LinePosition.x + NodeDim.x, LinePosition.y };
|
||||
v2 OutputTextOffset = v2{-TextOffset.x, TextOffset.y};
|
||||
for (u32 i = 0; i < NodeDataTypeInfo.MembersCount; i++)
|
||||
{
|
||||
gsm_struct_member_type_info Member = NodeDataTypeInfo.Members[i];
|
||||
gs_string MemberName = MakeString(Member.Identifier, Member.IdentifierLength);
|
||||
|
||||
// TODO(Peter): Can we make this rely on the same data that we use to
|
||||
// render the actual connection points?
|
||||
if (MemberIsInput(Member))
|
||||
{
|
||||
DrawString(RenderBuffer, MemberName, Interface.Font, LinePosition + TextOffset, WhiteV4, Align_Left);
|
||||
InputLinePosition.y -= LineHeight;
|
||||
}
|
||||
else if (MemberIsOutput(Member))
|
||||
{
|
||||
DrawString(RenderBuffer, MemberName, Interface.Font, LinePosition + TextOffset, WhiteV4, Align_Right);
|
||||
OutputLinePosition.y -= LineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal s32
|
||||
GetVisualPortIndexForNode(gs_list_handle SparseNodeHandle, u32 PortIndex, node_layout Layout)
|
||||
{
|
||||
s32 Result = -1;
|
||||
|
||||
for (u32 i = 0; i < Layout.VisualPortsCount; i++)
|
||||
{
|
||||
visual_port Port = Layout.VisualPorts[i];
|
||||
if (GSListHandlesAreEqual(Port.SparseNodeHandle, SparseNodeHandle) && Port.PortIndex == PortIndex)
|
||||
{
|
||||
Result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal node_layout
|
||||
ArrangeNodes(pattern_node_workspace Workspace, r32 NodeWidth, r32 LayerDistance, r32 LineHeight, gs_memory_arena* Storage, app_state* State)
|
||||
{
|
||||
node_layout Result = {};
|
||||
|
||||
for (u32 n = 0; n < Workspace.Nodes.Used; n++)
|
||||
{
|
||||
gs_list_handle NodeHandle = Workspace.SortedNodeHandles[n];
|
||||
pattern_node Node = *Workspace.Nodes.GetElementWithHandle(NodeHandle);
|
||||
|
||||
u32 SpecIndex = Node.SpecificationIndex;
|
||||
|
||||
node_specification_ Spec = NodeSpecifications[SpecIndex];
|
||||
gsm_struct_type_info NodeDataTypeInfo = StructTypes[Spec.DataType];
|
||||
Result.VisualPortsCount += NodeDataTypeInfo.MembersCount;;
|
||||
}
|
||||
|
||||
// Place nodes and connections
|
||||
Result.VisualNodesCount = Workspace.Nodes.Used;
|
||||
Result.VisualNodes = PushArray(Storage, visual_node, Result.VisualNodesCount);
|
||||
|
||||
u32 VisualPortsUsed = 0;
|
||||
Result.VisualPorts = PushArray(Storage, visual_port, Result.VisualPortsCount);
|
||||
|
||||
for (u32 n = 0; n < Workspace.Nodes.Used; n++)
|
||||
{
|
||||
gs_list_handle NodeHandle = Workspace.SortedNodeHandles[n];
|
||||
pattern_node Node = *Workspace.Nodes.GetElementWithHandle(NodeHandle);
|
||||
|
||||
u32 SpecIndex = Node.SpecificationIndex;
|
||||
|
||||
node_specification_ Spec = NodeSpecifications[SpecIndex];
|
||||
gsm_struct_type_info NodeDataTypeInfo = StructTypes[Spec.DataType];
|
||||
|
||||
visual_node* VisualNode = Result.VisualNodes + n;
|
||||
VisualNode->Spec = Spec;
|
||||
VisualNode->Position = v2{(1.5f * NodeWidth) * n, 0};
|
||||
|
||||
// NOTE(Peter): These start at 2 to account for the offset past the node title
|
||||
s32 InputsCount = 2;
|
||||
s32 OutputsCount = 2;
|
||||
for (u32 p = 0; p < NodeDataTypeInfo.MembersCount; p++)
|
||||
{
|
||||
gsm_struct_member_type_info Member = NodeDataTypeInfo.Members[p];
|
||||
|
||||
rect2 PortBounds = {0};
|
||||
v2 PortDim = v2{8, 8};
|
||||
PortBounds.Min = VisualNode->Position + v2{0, PortDim.y / 2};
|
||||
if (MemberIsInput(Member))
|
||||
{
|
||||
PortBounds.Min.y -= LineHeight * InputsCount++;
|
||||
PortBounds.Min.x -= PortDim.x;
|
||||
}
|
||||
else if (MemberIsOutput(Member))
|
||||
{
|
||||
PortBounds.Min.y -= LineHeight * OutputsCount++;
|
||||
PortBounds.Min.x += NodeWidth;
|
||||
}
|
||||
PortBounds.Max = PortBounds.Min + v2{8, 8};
|
||||
|
||||
visual_port* VisualPort = Result.VisualPorts + VisualPortsUsed++;
|
||||
VisualPort->SparseNodeHandle = NodeHandle;
|
||||
VisualPort->PortIndex = p;
|
||||
VisualPort->PortBounds = PortBounds;
|
||||
}
|
||||
}
|
||||
|
||||
Result.VisualConnectionsCount = 0;
|
||||
|
||||
Result.VisualConnectionsCount = Workspace.Connections.Used;
|
||||
Result.VisualConnections = PushArray(Storage, visual_connection, Result.VisualConnectionsCount);
|
||||
for (u32 c = 0; c < Workspace.Connections.Used; c++)
|
||||
{
|
||||
pattern_node_connection* Connection = Workspace.Connections.GetElementAtIndex(c);
|
||||
|
||||
visual_connection* VisualConnection = Result.VisualConnections + c;
|
||||
VisualConnection->UpstreamVisualPortIndex = GetVisualPortIndexForNode(Connection->UpstreamNodeHandle, Connection->UpstreamPortIndex, Result);
|
||||
VisualConnection->DownstreamVisualPortIndex = GetVisualPortIndexForNode(Connection->DownstreamNodeHandle, Connection->DownstreamPortIndex, Result);
|
||||
|
||||
visual_port UpstreamPort = Result.VisualPorts[VisualConnection->UpstreamVisualPortIndex];
|
||||
visual_port DownstreamPort = Result.VisualPorts[VisualConnection->DownstreamVisualPortIndex];
|
||||
|
||||
VisualConnection->UpstreamPosition = gs_CalculateRectCenter(UpstreamPort.PortBounds);
|
||||
VisualConnection->DownstreamPosition = gs_CalculateRectCenter(DownstreamPort.PortBounds);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_node_graph);
|
||||
internal void
|
||||
NodeGraph_Render(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
node_graph_state* GraphState = (node_graph_state*)Panel.PanelStateMemory;
|
||||
b32 MouseHandled = false;
|
||||
|
||||
rect2 NodeSelectionWindowBounds = rect2{
|
||||
PanelBounds.Min,
|
||||
v2{PanelBounds.Min.x + 300, PanelBounds.Max.y},
|
||||
};
|
||||
rect2 GraphBounds = rect2{
|
||||
v2{NodeSelectionWindowBounds.Max.x, PanelBounds.Min.y},
|
||||
PanelBounds.Max,
|
||||
};
|
||||
|
||||
r32 NodeWidth = 150;
|
||||
r32 LayerDistance = 100;
|
||||
r32 LineHeight = ui_GetTextLineHeight(State->Interface);
|
||||
|
||||
if (GraphState->LayoutIsDirty)
|
||||
{
|
||||
// NOTE(Peter): Resset the LayoutMemory arena so we can use it again.
|
||||
// If LayoutIsDirty, then we need to recalculate all the members of GraphState->Layout
|
||||
// so we might as well just clear the whole thing (we aren't freeing, just reusing)
|
||||
ClearArena(&GraphState->LayoutMemory);
|
||||
GraphState->Layout = {};
|
||||
|
||||
GraphState->Layout = ArrangeNodes(State->NodeWorkspace, NodeWidth, LayerDistance, LineHeight, &GraphState->LayoutMemory, State);
|
||||
GraphState->LayoutIsDirty = false;
|
||||
}
|
||||
|
||||
DrawGrid(GraphState->ViewOffset, v2{100, 100}, GraphBounds, RenderBuffer);
|
||||
|
||||
for (u32 i = 0; i < GraphState->Layout.VisualConnectionsCount; i++)
|
||||
{
|
||||
visual_connection Connection = GraphState->Layout.VisualConnections[i];
|
||||
|
||||
v2 Start = GraphState->ViewOffset + Connection.UpstreamPosition;
|
||||
v2 End = GraphState->ViewOffset + Connection.DownstreamPosition;
|
||||
PushRenderLine2D(RenderBuffer, Start, End, 1.5f, WhiteV4);
|
||||
|
||||
v2 TempDim = v2{6, 6};
|
||||
PushRenderQuad2D(RenderBuffer, Start - TempDim, Start + TempDim, PinkV4);
|
||||
PushRenderQuad2D(RenderBuffer, End - TempDim, End + TempDim, YellowV4);
|
||||
}
|
||||
|
||||
if (GraphState->Layout.ConnectionIsInProgress)
|
||||
{
|
||||
PushRenderLine2D(RenderBuffer,
|
||||
GraphState->Layout.InProgressConnectionStart,
|
||||
GraphState->Layout.InProgressConnectionEnd,
|
||||
1.5f, WhiteV4);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < GraphState->Layout.VisualNodesCount; i++)
|
||||
{
|
||||
visual_node VisualNode = GraphState->Layout.VisualNodes[i];
|
||||
gs_list_handle NodeHandle = State->NodeWorkspace.SortedNodeHandles[i];
|
||||
DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeHandle, NodeWidth, LineHeight, State->Interface.Style, RenderBuffer, Context.Mouse, &State->Transient);
|
||||
}
|
||||
|
||||
for (u32 p = 0; p < GraphState->Layout.VisualPortsCount; p++)
|
||||
{
|
||||
visual_port VisualPort = GraphState->Layout.VisualPorts[p];
|
||||
VisualPort.PortBounds.Min += GraphState->ViewOffset;
|
||||
VisualPort.PortBounds.Max += GraphState->ViewOffset;
|
||||
|
||||
v4 PortColor = WhiteV4;
|
||||
if (PointIsInRange(Context.Mouse.Pos, VisualPort.PortBounds.Min, VisualPort.PortBounds.Max))
|
||||
{
|
||||
PortColor = PinkV4;
|
||||
if (MouseButtonTransitionedDown(Context.Mouse.LeftButtonState))
|
||||
{
|
||||
BeginConnectNodesOperation(VisualPort, p, Context.Mouse, State);
|
||||
MouseHandled = true;
|
||||
}
|
||||
}
|
||||
|
||||
PushRenderQuad2D(RenderBuffer, VisualPort.PortBounds.Min, VisualPort.PortBounds.Max, PortColor);
|
||||
}
|
||||
|
||||
// Node Selection Panel
|
||||
v4 LineBGColors[] = {
|
||||
{ .16f, .16f, .16f, 1.f },
|
||||
{ .18f, .18f, .18f, 1.f },
|
||||
};
|
||||
|
||||
interface_list List = {};
|
||||
List.LineBGColors = LineBGColors;
|
||||
List.LineBGColorsCount = sizeof(LineBGColors) / sizeof(LineBGColors[0]);
|
||||
List.LineBGHoverColor = v4{ .22f, .22f, .22f, 1.f };
|
||||
List.TextColor = WhiteV4;
|
||||
List.ListBounds = NodeSelectionWindowBounds;
|
||||
List.ListElementDimensions = v2{
|
||||
Rect2Width(NodeSelectionWindowBounds),
|
||||
ui_GetTextLineHeight(State->Interface)
|
||||
};
|
||||
List.ElementLabelIndent = v2{10, 4};
|
||||
|
||||
gs_string Titlegs_string = MakeStringLiteral("Available Nodes");
|
||||
DrawListElement(Titlegs_string, &List, Context.Mouse, RenderBuffer, State->Interface.Style);
|
||||
|
||||
for (u32 i = 0; i < NodeType_Count; i++)
|
||||
{
|
||||
node_specification_ Spec = NodeSpecifications[i];
|
||||
rect2 ElementBounds = DrawListElement(Spec.Identifier, &List, Context.Mouse, RenderBuffer, State->Interface.Style);
|
||||
|
||||
if (MouseButtonTransitionedDown(Context.Mouse.LeftButtonState)
|
||||
&& gs_PointIsInRect(Context.Mouse.DownPos, ElementBounds))
|
||||
{
|
||||
PushNodeOnWorkspace(i, &State->NodeWorkspace, &State->Transient);
|
||||
GraphState->LayoutIsDirty = true;
|
||||
MouseHandled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!MouseHandled && MouseButtonTransitionedDown(Context.Mouse.LeftButtonState))
|
||||
{
|
||||
BeginPanNodeGraph(State, {}, Context.Mouse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_PANEL_NODE_GRAPH_H
|
||||
#endif // FOLDHAUS_PANEL_NODE_GRAPH_H
|
|
@ -1,55 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_search_lister.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_SEARCH_LISTER_CPP
|
||||
|
||||
internal b32
|
||||
NamePassesFilter (gs_string Target, gs_string Filter)
|
||||
{
|
||||
return (Filter.Length == 0 || StringContainsStringCaseInsensitive(Target, Filter));
|
||||
}
|
||||
|
||||
internal void
|
||||
FilterSearchLister (search_lister* SearchLister)
|
||||
{
|
||||
Assert(SearchLister->FilteredIndexLUT != 0);
|
||||
Assert(SearchLister->FilteredListMax == SearchLister->SourceListCount);
|
||||
|
||||
SearchLister->FilteredListCount = 0;
|
||||
|
||||
for (s32 i = 0; i < SearchLister->SourceListCount; i++)
|
||||
{
|
||||
gs_string* Namegs_string = SearchLister->SourceList + i;
|
||||
if (NamePassesFilter(*Namegs_string, SearchLister->Filter))
|
||||
{
|
||||
SearchLister->FilteredIndexLUT[SearchLister->FilteredListCount++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
SearchLister->HotItem = Clamp(0, SearchLister->HotItem, SearchLister->FilteredListCount - 1);
|
||||
|
||||
if (SearchLister->FilteredListCount == 0)
|
||||
{
|
||||
SearchLister->HotItem = -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal s32
|
||||
GetNextFilteredItem (search_lister SearchLister)
|
||||
{
|
||||
s32 Result = Min(SearchLister.HotItem + 1, SearchLister.FilteredListCount - 1);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
GetPrevFilteredItem (search_lister SearchLister)
|
||||
{
|
||||
s32 Result = Max(SearchLister.HotItem - 1, 0);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_SEARCH_LISTER_CPP
|
||||
#endif // FOLDHAUS_SEARCH_LISTER_CPP
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_search_lister.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_SEARCH_LISTER_H
|
||||
|
||||
struct search_lister
|
||||
{
|
||||
// TODO(Peter): Giving up trying to just use the source list for now. At the moment
|
||||
// we are copying the gs_strings you want to filter from and storing them here. Come back
|
||||
// once its working and make the memory efficient version (ie store the existing memory
|
||||
// location, the element stride and the offset to the char*)
|
||||
s32 SourceListCount;
|
||||
gs_string* SourceList;
|
||||
|
||||
// NOTE(Peter): stores the source indecies of each filtered item
|
||||
// For example:
|
||||
// Index 0 in this array contains 3. This means the first item that passes the filter
|
||||
// is index 3 in ListMemory
|
||||
s32 FilteredListMax;
|
||||
s32 FilteredListCount;
|
||||
s32* FilteredIndexLUT;
|
||||
s32 HotItem;
|
||||
|
||||
gs_string Filter;
|
||||
};
|
||||
|
||||
|
||||
#define FOLDHAUS_SEARCH_LISTER_H
|
||||
#endif // FOLDHAUS_SEARCH_LISTER_H
|
|
@ -1,249 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_text_entry.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_TEXT_ENTRY_CPP
|
||||
|
||||
internal void
|
||||
ResetTextInput (text_entry* Input)
|
||||
{
|
||||
Input->CursorPosition = 0;
|
||||
Input->Buffer.Length = 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
PipeSearchStringToDestination (text_entry* Input)
|
||||
{
|
||||
switch (Input->Destination.Type)
|
||||
{
|
||||
case TextTranslateTo_gs_string:
|
||||
{
|
||||
PrintF(Input->Destination.StringDest, "%S", Input->Buffer);
|
||||
}break;
|
||||
|
||||
case TextTranslateTo_R32:
|
||||
{
|
||||
*Input->Destination.FloatDest = (r32)ParseFloat(Input->Buffer.ConstString);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
RemoveCharacterAtCursor (text_entry* TextEntry)
|
||||
{
|
||||
if (TextEntry->CursorPosition > 0)
|
||||
{
|
||||
for (u32 i = TextEntry->CursorPosition - 1; i < TextEntry->Buffer.Length; i++)
|
||||
{
|
||||
Assert(i + 1 < TextEntry->Buffer.Size);
|
||||
TextEntry->Buffer.Str[i] = TextEntry->Buffer.Str[i + 1];
|
||||
}
|
||||
TextEntry->CursorPosition--;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
SetTextInputDestinationToString (text_entry* TextInput, gs_string* DestinationString)
|
||||
{
|
||||
ResetTextInput(TextInput);
|
||||
TextInput->Destination.Type = TextTranslateTo_gs_string;
|
||||
TextInput->Destination.StringDest = DestinationString;
|
||||
PrintF(&TextInput->Buffer, "%S", *DestinationString);
|
||||
}
|
||||
|
||||
internal void
|
||||
SetTextInputDestinationToFloat (text_entry* TextInput, r32* DestinationFloat)
|
||||
{
|
||||
ResetTextInput(TextInput);
|
||||
TextInput->Destination.Type = TextTranslateTo_R32;
|
||||
TextInput->Destination.FloatDest = DestinationFloat;
|
||||
PrintF(&TextInput->Buffer, "%f", *DestinationFloat);
|
||||
|
||||
if (*DestinationFloat == 0.0f)
|
||||
{
|
||||
TextInput->CursorPosition = 1;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
MoveCursorRight (text_entry* TextEntry)
|
||||
{
|
||||
TextEntry->CursorPosition = Min(TextEntry->Buffer.Length, TextEntry->CursorPosition + 1);
|
||||
}
|
||||
|
||||
internal void
|
||||
MoveCursorLeft (text_entry* TextEntry)
|
||||
{
|
||||
TextEntry->CursorPosition = Max(0, TextEntry->CursorPosition - 1);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(TextEntryInsertChar)
|
||||
{
|
||||
|
||||
char Char = CharacterFromKeyCode(Event.Key);
|
||||
if (Event.Key >= KeyCode_A && Event.Key <= KeyCode_Z && (Event.Modifiers & Modifier_Shift) == 0)
|
||||
{
|
||||
Char += ('a' - 'A');
|
||||
}
|
||||
|
||||
// Shift string forward
|
||||
Assert(State->ActiveTextEntry.Buffer.Length < State->ActiveTextEntry.Buffer.Size);
|
||||
for (u32 i = State->ActiveTextEntry.Buffer.Length;
|
||||
i > (u32)State->ActiveTextEntry.CursorPosition;
|
||||
i--)
|
||||
{
|
||||
State->ActiveTextEntry.Buffer.Str[i] = State->ActiveTextEntry.Buffer.Str[i - 1];
|
||||
}
|
||||
// Insert new Character
|
||||
State->ActiveTextEntry.Buffer.Str[State->ActiveTextEntry.CursorPosition] = Char;
|
||||
State->ActiveTextEntry.CursorPosition++;
|
||||
PipeSearchStringToDestination(&State->ActiveTextEntry);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(RemoveCharacterFromEntrygs_string)
|
||||
{
|
||||
RemoveCharacterAtCursor(&State->ActiveTextEntry);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(TextEntryMoveCursorRight)
|
||||
{
|
||||
MoveCursorRight(&State->ActiveTextEntry);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(TextEntryMoveCursorLeft)
|
||||
{
|
||||
MoveCursorLeft(&State->ActiveTextEntry);
|
||||
}
|
||||
|
||||
internal void
|
||||
InitializeTextInputCommands (input_command_registry* Commands, gs_memory_arena* PermanentStorage)
|
||||
{
|
||||
if (Commands->Size > 0)
|
||||
{
|
||||
RegisterKeyPressCommand(Commands, KeyCode_Backspace, Command_Began | Command_Held, KeyCode_Invalid, RemoveCharacterFromEntrygs_string);
|
||||
RegisterKeyPressCommand(Commands, KeyCode_LeftArrow, Command_Began | Command_Held, KeyCode_Invalid, TextEntryMoveCursorLeft);
|
||||
RegisterKeyPressCommand(Commands, KeyCode_RightArrow, Command_Began | Command_Held, KeyCode_Invalid, TextEntryMoveCursorRight);
|
||||
|
||||
for (s32 i = KeyCode_a; i < KeyCode_UpArrow; i++)
|
||||
{
|
||||
RegisterKeyPressCommand(Commands, (key_code)i, Command_Began | Command_Held, KeyCode_Invalid, TextEntryInsertChar);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY \
|
||||
{ KeyCode_Backspace, KeyCode_Invalid, Command_Began | Command_Held, RemoveCharacterFromEntrygs_string }, \
|
||||
{ KeyCode_LeftArrow, KeyCode_Invalid, Command_Began | Command_Held, TextEntryMoveCursorLeft }, \
|
||||
{ KeyCode_RightArrow, KeyCode_Invalid, Command_Began | Command_Held, TextEntryMoveCursorRight }, \
|
||||
{ KeyCode_a, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_b, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_c, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_d, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_e, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_f, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_g, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_h, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_i, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_j, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_k, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_l, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_m, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_n, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_o, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_p, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_q, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_r, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_s, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_t, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_u, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_v, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_w, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_x, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_y, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_z, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_A, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_B, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_C, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_D, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_E, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_F, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_G, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_H, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_I, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_J, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_K, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_L, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_M, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_N, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_O, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_P, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Q, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_R, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_S, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_T, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_U, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_V, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_W, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_X, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Y, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Z, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_0, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_1, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_2, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_3, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_4, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_5, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_6, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_7, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_8, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_9, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num0, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num1, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num2, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num3, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num4, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num5, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num6, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num7, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num8, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Num9, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Bang, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_At, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Pound, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Dollar, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Percent, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Carrot, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Ampersand, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Star, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_LeftParen, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_RightParen, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Minus, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Plus, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Equals, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Underscore, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_LeftBrace, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_RightBrace, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_LeftBracket, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_RightBracket, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Colon, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_SemiColon, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_SingleQuote, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_DoubleQuote, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_ForwardSlash, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Backslash, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Pipe, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Comma, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Period, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_QuestionMark, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_LessThan, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_GreaterThan, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_Tilde, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }, \
|
||||
{ KeyCode_BackQuote, KeyCode_Invalid, Command_Began | Command_Held, TextEntryInsertChar }
|
||||
|
||||
|
||||
#define FOLDHAUS_TEXT_ENTRY_CPP
|
||||
#endif // FOLDHAUS_TEXT_ENTRY_CPP
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_text_entry.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_TEXT_ENTRY_H
|
||||
|
||||
enum text_translation_type
|
||||
{
|
||||
TextTranslateTo_gs_string,
|
||||
TextTranslateTo_R32,
|
||||
TextTranslateTo_S32,
|
||||
TextTranslateTo_U32,
|
||||
};
|
||||
|
||||
struct text_entry_destination
|
||||
{
|
||||
text_translation_type Type;
|
||||
union {
|
||||
gs_string* StringDest;
|
||||
r32* FloatDest;
|
||||
s32* SignedIntDest;
|
||||
u32* UnsignedIntDest;
|
||||
};
|
||||
};
|
||||
|
||||
struct text_entry
|
||||
{
|
||||
gs_string Buffer;
|
||||
s32 CursorPosition;
|
||||
|
||||
text_entry_destination Destination;
|
||||
};
|
||||
|
||||
#define FOLDHAUS_TEXT_ENTRY_H
|
||||
#endif // FOLDHAUS_TEXT_ENTRY_H
|
|
@ -1,377 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_util_radialumia_file_converter.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_UTIL_RADIALUMIA_FILE_CONVERTER_CPP
|
||||
|
||||
#define DEBUG
|
||||
#define DEBUG_TRACK_SCOPE(name)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <gs_string.h>
|
||||
|
||||
#include "gs/gs_language.h"
|
||||
#include "gs/gs_string.h"
|
||||
#include "../meta/gs_meta_lexer.h"
|
||||
#include "gs/gs_vector.h"
|
||||
|
||||
#define gs_string_BUFFER_SIZE 512
|
||||
struct gs_string_buffer
|
||||
{
|
||||
char* Memory;
|
||||
s32 Size;
|
||||
gs_string_buffer* Next;
|
||||
};
|
||||
|
||||
struct gs_string_writer
|
||||
{
|
||||
char* Cursor;
|
||||
s32 UsedIngs_string;
|
||||
gs_string_buffer* Buffer;
|
||||
};
|
||||
|
||||
internal gs_string_buffer*
|
||||
Growgs_stringBuffer (gs_string_buffer* Buffer)
|
||||
{
|
||||
gs_string_buffer* Result;
|
||||
if (Buffer->Next)
|
||||
{
|
||||
Result = Growgs_stringBuffer(Buffer->Next);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = (gs_string_buffer*)malloc(sizeof(gs_string_buffer));
|
||||
Result->Memory = (char*)malloc(sizeof(char) * gs_string_BUFFER_SIZE);
|
||||
memset(Result->Memory, 0, gs_string_BUFFER_SIZE);
|
||||
Result->Size = gs_string_BUFFER_SIZE;
|
||||
Result->Next = 0;
|
||||
|
||||
Buffer->Next = Result;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Writegs_string(gs_string_writer* Writer, char* gs_string, s32 Length)
|
||||
{
|
||||
char* Src = gs_string;
|
||||
char* Dst = Writer->Cursor;
|
||||
s32 LengthWritten = 0;
|
||||
|
||||
while (*Src && Writer->UsedIngs_string < Writer->Buffer->Size &&LengthWritten < Length)
|
||||
{
|
||||
LengthWritten++;
|
||||
*Dst++ = *Src++;
|
||||
Writer->UsedIngs_string++;
|
||||
}
|
||||
|
||||
Writer->Cursor = Dst;
|
||||
|
||||
if (*Src && Writer->UsedIngs_string == Writer->Buffer->Size)
|
||||
{
|
||||
*(Dst - 1) = 0; // Null terminate the buffer
|
||||
Writer->Buffer = Growgs_stringBuffer(Writer->Buffer);
|
||||
Writer->Cursor = Writer->Buffer->Memory;
|
||||
Writer->UsedIngs_string = 0;
|
||||
Writegs_string(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 (gs_stringsEqual(Tokenizer.At, "EOF"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
EatToCharacterInclusive(&Tokenizer, '{');
|
||||
EatWhitespace(&Tokenizer);
|
||||
Assert(gs_stringsEqual(Tokenizer.At, "neighbors: ["));
|
||||
Tokenizer.At += gs_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(gs_stringsEqual(Tokenizer.At, "ip: "));
|
||||
Tokenizer.At += gs_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(gs_stringsEqual(Tokenizer.At, "x: "));
|
||||
Tokenizer.At += gs_stringLength("x: ");
|
||||
NextControlBox->X = ParseFloat(Tokenizer.At);
|
||||
EatToCharacterInclusive(&Tokenizer, ';');
|
||||
// Parse Y
|
||||
EatWhitespace(&Tokenizer);
|
||||
Assert(gs_stringsEqual(Tokenizer.At, "y: "));
|
||||
Tokenizer.At += gs_stringLength("y: ");
|
||||
NextControlBox->Y = ParseFloat(Tokenizer.At);
|
||||
EatToCharacterInclusive(&Tokenizer, ';');
|
||||
// Parse Z
|
||||
EatWhitespace(&Tokenizer);
|
||||
Assert(gs_stringsEqual(Tokenizer.At, "z: "));
|
||||
Tokenizer.At += gs_stringLength("z: ");
|
||||
NextControlBox->Z = ParseFloat(Tokenizer.At);
|
||||
EatToCharacterInclusive(&Tokenizer, ';');
|
||||
|
||||
// Parse ID
|
||||
EatWhitespace(&Tokenizer);
|
||||
Assert(gs_stringsEqual(Tokenizer.At, "id: "));
|
||||
Tokenizer.At += gs_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++;
|
||||
}
|
||||
|
||||
|
||||
gs_string_buffer OutputFileBuffer = {};
|
||||
OutputFileBuffer.Memory = (char*)malloc(sizeof(char) * gs_string_BUFFER_SIZE);
|
||||
OutputFileBuffer.Size = gs_string_BUFFER_SIZE;
|
||||
OutputFileBuffer.Next = 0;
|
||||
|
||||
gs_string_writer RefWriter = {};
|
||||
RefWriter.Cursor = OutputFileBuffer.Memory;
|
||||
RefWriter.UsedIngs_string = 0;
|
||||
RefWriter.Buffer = &OutputFileBuffer;
|
||||
gs_string_writer* Writer = &RefWriter;
|
||||
|
||||
char gs_stringBuffer[512];
|
||||
s32 Len = 0;
|
||||
|
||||
Len = sprintf_s(gs_stringBuffer, 512, "control_box_count %d\n", ControlBoxesUsed);
|
||||
Writegs_string(Writer, gs_stringBuffer, Len);
|
||||
Len = sprintf_s(gs_stringBuffer, 512, "led_strip_count %d\n\n", ControlBoxPairsUsed);
|
||||
Writegs_string(Writer, gs_stringBuffer, Len);
|
||||
|
||||
for (s32 c = 0; c < ControlBoxesUsed; c++)
|
||||
{
|
||||
control_box* Box = ControlBoxes + c;
|
||||
Len = sprintf_s(gs_stringBuffer, 512,
|
||||
"control_box { %d, \"%s\", (%f, %f, %f) }\n",
|
||||
Box->ID, Box->Address,
|
||||
Box->X, Box->Y, Box->Z);
|
||||
Writegs_string(Writer, gs_stringBuffer, Len);
|
||||
}
|
||||
|
||||
Writegs_string(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 LEDStripFormatgs_string[] = "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(gs_stringBuffer, 512,
|
||||
LEDStripFormatgs_string,
|
||||
Pair->Start, Universe, 0,
|
||||
StartX, StartY, StartZ,
|
||||
EndX, EndY, EndZ);
|
||||
Writegs_string(Writer, gs_stringBuffer, Len);
|
||||
}
|
||||
|
||||
Writegs_string(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(gs_stringBuffer, 512,
|
||||
LEDStripFormatgs_string,
|
||||
Strip->BoxID, Universe, 0,
|
||||
Strip->Start.x, Strip->Start.y, Strip->Start.z,
|
||||
Strip->End.x, Strip->End.y, Strip->End.z);
|
||||
Writegs_string(Writer, gs_stringBuffer, Len);
|
||||
}
|
||||
|
||||
Writegs_string(Writer, "END_OF_ASSEMBLY_FILE", gs_stringLength("END_OF_ASSEMBLY_FILE"));
|
||||
|
||||
*Writer->Cursor = 0;
|
||||
|
||||
FILE* OutputFile = fopen("F:/data/radialumia.fold", "w");
|
||||
gs_string_buffer* BufferCursor = &OutputFileBuffer;
|
||||
while(BufferCursor)
|
||||
{
|
||||
fprintf(OutputFile, BufferCursor->Memory);
|
||||
BufferCursor = BufferCursor->Next;
|
||||
}
|
||||
fclose(OutputFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_UTIL_RADIALUMIA_FILE_CONVERTER_CPP
|
||||
#endif // FOLDHAUS_UTIL_RADIALUMIA_FILE_CONVERTER_CPP
|
|
@ -1,513 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_node_interface.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLHAUS_NODE_INTERFACE_CPP
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node Lister
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
struct node_lister_operation_state
|
||||
{
|
||||
search_lister SearchLister;
|
||||
v2 ListPosition;
|
||||
};
|
||||
|
||||
internal void
|
||||
RenderNodeLister(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
node_lister_operation_state* OpState = (node_lister_operation_state*)Operation.OpStateMemory;
|
||||
|
||||
v2 TopLeft = OpState->ListPosition;
|
||||
v2 Dimension = v2{300, 30};
|
||||
|
||||
// Filter the lister
|
||||
OpState->SearchLister.Filter = State->ActiveTextEntry.Buffer;
|
||||
FilterSearchLister(&OpState->SearchLister);
|
||||
|
||||
// Display Search Lister
|
||||
search_lister_result NodeListerResult = EvaluateSearchLister (&State->Interface_, TopLeft, Dimension,
|
||||
MakeStringLiteral("Nodes List"),
|
||||
OpState->SearchLister.SourceList,
|
||||
OpState->SearchLister.FilteredIndexLUT,
|
||||
OpState->SearchLister.FilteredListCount,
|
||||
OpState->SearchLister.HotItem,
|
||||
&State->ActiveTextEntry.Buffer,
|
||||
State->ActiveTextEntry.CursorPosition);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerNextItem)
|
||||
{
|
||||
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
|
||||
OpState->SearchLister.HotItem = GetNextFilteredItem(OpState->SearchLister);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerPrevItem)
|
||||
{
|
||||
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
|
||||
OpState->SearchLister.HotItem = GetPrevFilteredItem(OpState->SearchLister);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeLister)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(SelectAndCloseNodeLister)
|
||||
{
|
||||
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
|
||||
s32 FilteredNodeIndex = OpState->SearchLister.HotItem;
|
||||
if (FilteredNodeIndex >= 0)
|
||||
{
|
||||
s32 NodeIndex = OpState->SearchLister.FilteredIndexLUT[FilteredNodeIndex];
|
||||
PushNodeOnListFromSpecification(State->NodeList, (node_type)NodeIndex,
|
||||
Mouse.Pos, State->Permanent);
|
||||
}
|
||||
CloseNodeLister(State, Event, Mouse);
|
||||
}
|
||||
|
||||
input_command UniverseViewCommads [] = {
|
||||
{ KeyCode_DownArrow, KeyCode_Invalid, Command_Began, NodeListerNextItem },
|
||||
{ KeyCode_UpArrow, KeyCode_Invalid, Command_Began, NodeListerPrevItem },
|
||||
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, SelectAndCloseNodeLister },
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, CloseNodeLister },
|
||||
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseNodeLister },
|
||||
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
|
||||
};
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeLister)
|
||||
{
|
||||
operation_mode* AddNodeOperation = ActivateOperationModeWithCommands(&State->Modes, UniverseViewCommads);
|
||||
|
||||
AddNodeOperation->Render = RenderNodeLister;
|
||||
|
||||
node_lister_operation_state* OpState = CreateOperationState(AddNodeOperation,
|
||||
&State->Modes,
|
||||
node_lister_operation_state);
|
||||
{
|
||||
OpState->SearchLister.SourceListCount = NodeSpecificationsCount;
|
||||
OpState->SearchLister.SourceList = PushArray(&State->Modes.Arena, gs_string, OpState->SearchLister.SourceListCount);
|
||||
{
|
||||
for (s32 i = 0; i < OpState->SearchLister.SourceListCount; i++)
|
||||
{
|
||||
OpState->SearchLister.SourceList[i] = MakeString(
|
||||
NodeSpecifications[i].Name,
|
||||
NodeSpecifications[i].NameLength);
|
||||
}
|
||||
}
|
||||
OpState->SearchLister.Filter = MakeString(PushArray(&State->Modes.Arena, char, 64), 0, 64);
|
||||
|
||||
OpState->SearchLister.FilteredListMax = OpState->SearchLister.SourceListCount;
|
||||
OpState->SearchLister.FilteredListCount = 0;
|
||||
OpState->SearchLister.FilteredIndexLUT = PushArray(&State->Modes.Arena, s32, OpState->SearchLister.SourceListCount);
|
||||
}
|
||||
|
||||
OpState->ListPosition = Mouse.Pos;
|
||||
SetTextInputDestinationTogs_string(&State->ActiveTextEntry, &OpState->SearchLister.Filter);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node Color Picker
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
struct color_picker_operation_state
|
||||
{
|
||||
v4* ValueAddr;
|
||||
};
|
||||
|
||||
internal void
|
||||
CloseColorPicker(app_state* State)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(CloseColorPickerCommand)
|
||||
{
|
||||
CloseColorPicker(State);
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderColorPicker(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
color_picker_operation_state* OpState = (color_picker_operation_state*)Operation.OpStateMemory;
|
||||
|
||||
|
||||
b32 ShouldClose = EvaluateColorPicker(RenderBuffer, OpState->ValueAddr,
|
||||
v2{200, 200}, State->Interface, Mouse);
|
||||
|
||||
if (ShouldClose)
|
||||
{
|
||||
CloseColorPicker(State);
|
||||
}
|
||||
}
|
||||
|
||||
input_command ColorPickerCommands [] = {
|
||||
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseColorPickerCommand },
|
||||
};
|
||||
|
||||
internal void
|
||||
OpenColorPicker(app_state* State, node_connection* Connection)
|
||||
{
|
||||
operation_mode* ColorPickerMode = ActivateOperationModeWithCommands(&State->Modes, ColorPickerCommands);
|
||||
ColorPickerMode->Render = RenderColorPicker;
|
||||
|
||||
color_picker_operation_state* OpState = CreateOperationState(ColorPickerMode,
|
||||
&State->Modes,
|
||||
color_picker_operation_state);
|
||||
OpState->ValueAddr = Connection->V4ValuePtr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node Field Text Edit
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndNodeFieldTextEdit)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command NodeFieldTextEditCommands [] = {
|
||||
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
|
||||
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
|
||||
};
|
||||
|
||||
internal void
|
||||
BeginNodeFieldTextEdit(app_state* State, node_connection* Connection)
|
||||
{
|
||||
operation_mode* NodeFieldTextEditMode = ActivateOperationModeWithCommands(&State->Modes,
|
||||
NodeFieldTextEditCommands);
|
||||
|
||||
SetTextInputDestinationToFloat(&State->ActiveTextEntry, Connection->R32ValuePtr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node Port Mouse Drag
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
struct drag_node_port_operation_state
|
||||
{
|
||||
node_interaction Interaction;
|
||||
};
|
||||
|
||||
internal void
|
||||
RenderDraggingNodePort(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
drag_node_port_operation_state* OpState = (drag_node_port_operation_state*)Operation.OpStateMemory;
|
||||
UpdateDraggingNodePort(Mouse.Pos, OpState->Interaction, State->NodeList,
|
||||
State->NodeRenderSettings, RenderBuffer);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNodePort)
|
||||
{
|
||||
drag_node_port_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_port_operation_state);
|
||||
|
||||
TryConnectNodes(OpState->Interaction, Mouse.Pos, State->NodeList, State->NodeRenderSettings);
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command DragNodePortInputCommands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNodePort },
|
||||
};
|
||||
|
||||
internal void
|
||||
BeginDraggingNodePort(app_state* State, node_interaction Interaction)
|
||||
{
|
||||
operation_mode* DragNodePortMode = ActivateOperationModeWithCommands(
|
||||
&State->Modes,
|
||||
DragNodePortInputCommands);
|
||||
DragNodePortMode->Render = RenderDraggingNodePort;
|
||||
|
||||
drag_node_port_operation_state* OpState = CreateOperationState(DragNodePortMode,
|
||||
&State->Modes,
|
||||
drag_node_port_operation_state);
|
||||
OpState->Interaction = Interaction;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node Field Mouse Drag
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
internal void
|
||||
RenderDragNodeField(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
// TODO(Peter):
|
||||
//UpdateDraggingNodeValue(Mouse.Pos, Mouse.OldPos, OpState->Interaction, State->NodeList, State->NodeRenderSettings, State);
|
||||
}
|
||||
|
||||
internal void
|
||||
BeginInteractWithNodeField(app_state* State, node_interaction Interaction)
|
||||
{
|
||||
// TODO(Peter):
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node Mouse Drag
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
struct drag_node_operation_state
|
||||
{
|
||||
node_interaction Interaction;
|
||||
};
|
||||
|
||||
internal void
|
||||
RenderDraggingNode(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
drag_node_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_operation_state);
|
||||
UpdateDraggingNode(Mouse.Pos, OpState->Interaction, State->NodeList,
|
||||
State->NodeRenderSettings);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNode)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command DragNodeInputCommands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNode },
|
||||
};
|
||||
|
||||
internal void
|
||||
BeginDraggingNode(app_state* State, node_interaction Interaction)
|
||||
{
|
||||
operation_mode* DragNodeMode = ActivateOperationModeWithCommands(
|
||||
&State->Modes,
|
||||
DragNodeInputCommands);
|
||||
DragNodeMode->Render = RenderDraggingNode;
|
||||
|
||||
drag_node_operation_state* OpState = CreateOperationState(DragNodeMode,
|
||||
&State->Modes,
|
||||
drag_node_operation_state);
|
||||
OpState->Interaction = Interaction;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Node View
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
struct node_view_operation_state
|
||||
{
|
||||
s32 SelectedNodeHandle;
|
||||
};
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseDragInteraction)
|
||||
{
|
||||
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
|
||||
|
||||
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.DownPos, State->NodeRenderSettings);
|
||||
if (Node)
|
||||
{
|
||||
node_interaction NewInteraction = GetNodeInteractionType(Node,
|
||||
Mouse.Pos,
|
||||
State->NodeRenderSettings);
|
||||
if (IsDraggingNodePort(NewInteraction))
|
||||
{
|
||||
BeginDraggingNodePort(State, NewInteraction);
|
||||
}
|
||||
else if(IsDraggingNodeValue(NewInteraction))
|
||||
{
|
||||
// TODO(Peter): This probably wants to live in a mouse held action
|
||||
// the first frame we realize we're held over a field, just transition to
|
||||
// drag node field
|
||||
//BeginInteractWithNodeField(State, NewInteraction, State->NodeRenderSettings);
|
||||
}
|
||||
else // IsDraggingNode
|
||||
{
|
||||
OpState->SelectedNodeHandle = Node->Handle;
|
||||
BeginDraggingNode(State, NewInteraction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OpState->SelectedNodeHandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseSelectInteraction)
|
||||
{
|
||||
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
|
||||
|
||||
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.Pos, State->NodeRenderSettings);
|
||||
if (Node)
|
||||
{
|
||||
node_interaction NewInteraction = GetNodeInteractionType(Node,
|
||||
Mouse.Pos,
|
||||
State->NodeRenderSettings);
|
||||
if(IsDraggingNodeValue(NewInteraction))
|
||||
{
|
||||
node_connection* Connection = Node->Connections + NewInteraction.InputValue;
|
||||
struct_member_type InputType = Connection->Type;
|
||||
|
||||
if (InputType == MemberType_r32)
|
||||
{
|
||||
BeginNodeFieldTextEdit(State, Connection);
|
||||
}
|
||||
else if (InputType == MemberType_v4)
|
||||
{
|
||||
OpenColorPicker(State, Connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderNodeView(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
node_view_operation_state* OpState = (node_view_operation_state*)Operation.OpStateMemory;
|
||||
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
MakeStringBuffer(NodeHeaderBuffer, 128);
|
||||
|
||||
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
|
||||
|
||||
node_list_iterator NodeIter = GetNodeListIterator(*State->NodeList);
|
||||
while (NodeIteratorIsValid(NodeIter))
|
||||
{
|
||||
node_header* Node = NodeIter.At;
|
||||
|
||||
rect2 NodeBounds = CalculateNodeBounds(Node, State->NodeRenderSettings);
|
||||
b32 DrawFields = PointIsInRect(Mouse.Pos, NodeBounds);
|
||||
|
||||
if (Node == SelectedNode)
|
||||
{
|
||||
PushRenderQuad2D(RenderBuffer, NodeBounds.Min - v2{2, 2}, NodeBounds.Max + v2{2, 2}, WhiteV4);
|
||||
}
|
||||
|
||||
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.5f, .5f, .5f, 1.f});
|
||||
|
||||
// TODO(Peter): This is just for debug purposes. We can remove and go back to just having
|
||||
// Node->Name in Drawgs_string
|
||||
gs_string NodeName = GetNodeName(*Node);
|
||||
PrintF(&NodeHeaderBuffer, "%.*s: %d", NodeName.Length, NodeName.Memory, Node->Handle);
|
||||
DrawString(RenderBuffer, NodeHeaderBuffer, State->NodeRenderSettings.Font,
|
||||
v2{NodeBounds.Min.x + 5, NodeBounds.Max.y - (State->NodeRenderSettings.Font->PixelHeight + NODE_HEADER_HEIGHT + 5)},
|
||||
WhiteV4);
|
||||
|
||||
for (s32 Connection = 0; Connection < Node->ConnectionsCount; Connection++)
|
||||
{
|
||||
v4 PortColor = State->NodeRenderSettings.PortColors[Node->Connections[Connection].Type];
|
||||
|
||||
// Inputs
|
||||
if (ConnectionIsInput(Node, Connection))
|
||||
{
|
||||
rect2 PortBounds = CalculateNodeInputPortBounds(Node, Connection, State->NodeRenderSettings);
|
||||
DrawPort(RenderBuffer, PortBounds, PortColor);
|
||||
|
||||
//
|
||||
// TODO(Peter): I don't like excluding OutputNode, feels too much like a special case
|
||||
// but I don't want to get in to the meta programming right now.
|
||||
// We should just generate a spec and struct member types for NodeType_OutputNode
|
||||
//
|
||||
// :ExcludingOutputNodeSpecialCase
|
||||
//
|
||||
if (Node->Type != NodeType_OutputNode && DrawFields)
|
||||
{
|
||||
node_specification Spec = NodeSpecifications[Node->Type];
|
||||
node_struct_member Member = Spec.MemberList[Connection];
|
||||
DrawString(RenderBuffer, MakeString(Member.Name),
|
||||
State->NodeRenderSettings.Font,
|
||||
v2{PortBounds.Min.x - 8, PortBounds.Min.y}, WhiteV4, Align_Right);
|
||||
}
|
||||
|
||||
rect2 ValueBounds = CalculateNodeInputValueBounds(Node, Connection, State->NodeRenderSettings);
|
||||
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
|
||||
|
||||
// NOTE(Peter): its way easier to draw the connection on the input port b/c its a 1:1 relationship,
|
||||
// whereas output ports might have many connections, they really only know about the most recent one
|
||||
// Not sure if this is a problem. We mostly do everything backwards here, starting at the
|
||||
// most downstream node and working back up to find dependencies.
|
||||
if (ConnectionHasUpstreamConnection(Node, Connection))
|
||||
{
|
||||
rect2 ConnectedPortBounds = GetBoundsOfPortConnectedToInput(Node, Connection, State->NodeList, State->NodeRenderSettings);
|
||||
v2 InputCenter = CalculateRectCenter(PortBounds);
|
||||
v2 OutputCenter = CalculateRectCenter(ConnectedPortBounds);
|
||||
PushRenderLine2D(RenderBuffer, OutputCenter, InputCenter, 1, WhiteV4);
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs
|
||||
if (ConnectionIsOutput(Node, Connection))
|
||||
{
|
||||
rect2 PortBounds = CalculateNodeOutputPortBounds(Node, Connection, State->NodeRenderSettings);
|
||||
DrawPort(RenderBuffer, PortBounds, PortColor);
|
||||
|
||||
if (DrawFields)
|
||||
{
|
||||
node_specification Spec = NodeSpecifications[Node->Type];
|
||||
node_struct_member Member = Spec.MemberList[Connection];
|
||||
DrawString(RenderBuffer, MakeString(Member.Name),
|
||||
State->NodeRenderSettings.Font,
|
||||
v2{PortBounds.Max.x + 8, PortBounds.Min.y}, WhiteV4);
|
||||
}
|
||||
|
||||
rect2 ValueBounds = CalculateNodeOutputValueBounds(Node, Connection, State->NodeRenderSettings);
|
||||
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
|
||||
}
|
||||
|
||||
for (s32 Button = 0; Button < 3; Button++)
|
||||
{
|
||||
rect2 ButtonRect = CalculateNodeDragHandleBounds(NodeBounds, Button, State->NodeRenderSettings);
|
||||
PushRenderQuad2D(RenderBuffer, ButtonRect.Min, ButtonRect.Max, DragButtonColors[Button]);
|
||||
}
|
||||
}
|
||||
|
||||
Next(&NodeIter);
|
||||
}
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewDeleteNode)
|
||||
{
|
||||
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
|
||||
if (OpState->SelectedNodeHandle > 0)
|
||||
{
|
||||
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
|
||||
FreeNodeOnList(State->NodeList, SelectedNode);
|
||||
}
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeView)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command NodeViewCommands [] = {
|
||||
{ KeyCode_Tab, KeyCode_Invalid, Command_Began, CloseNodeView},
|
||||
{ KeyCode_A, KeyCode_Invalid, Command_Began, OpenNodeLister},
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, NodeViewBeginMouseDragInteraction},
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, NodeViewBeginMouseSelectInteraction},
|
||||
{ KeyCode_X, KeyCode_Invalid, Command_Began, NodeViewDeleteNode},
|
||||
};
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeView)
|
||||
{
|
||||
operation_mode* NodeViewMode = ActivateOperationModeWithCommands(&State->Modes, NodeViewCommands);
|
||||
NodeViewMode->Render = RenderNodeView;
|
||||
|
||||
node_view_operation_state* OpState = CreateOperationState(NodeViewMode,
|
||||
&State->Modes,
|
||||
node_view_operation_state);
|
||||
|
||||
OpState->SelectedNodeHandle = 0;
|
||||
}
|
||||
|
||||
|
||||
#define FOLHAUS_NODE_INTERFACE_CPP
|
||||
#endif // FOLHAUS_NODE_INTERFACE_CPP
|
|
@ -1,206 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_command_dispatch.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_COMMAND_DISPATCH_H
|
||||
|
||||
#define FOLDHAUS_INPUT_COMMAND_PROC(name) void name(app_state* State, input_entry Event, mouse_state Mouse, context Context, panel* Panel)
|
||||
typedef FOLDHAUS_INPUT_COMMAND_PROC(input_command_proc);
|
||||
|
||||
// NOTE(Peter): Helper function so I don't have to remember the parameters to this define
|
||||
#define ExecFoldhausCommand(cmd) cmd(State, Event, Mouse)
|
||||
|
||||
enum input_command_flags
|
||||
{
|
||||
Command_Began = 1 << 0,
|
||||
Command_Held = 1 << 1,
|
||||
Command_Ended = 1 << 2,
|
||||
};
|
||||
|
||||
#define Command_Any Command_Began | Command_Held | Command_Ended
|
||||
|
||||
// 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;
|
||||
key_code Mdfr;
|
||||
b32 Flags;
|
||||
input_command_proc* Proc;
|
||||
};
|
||||
|
||||
struct input_command_registry
|
||||
{
|
||||
input_command* Commands;
|
||||
s32 Size;
|
||||
s32 Used;
|
||||
|
||||
input_command_proc* MouseWheelCommand;
|
||||
};
|
||||
|
||||
struct command_queue_entry
|
||||
{
|
||||
input_command Command;
|
||||
input_entry Event;
|
||||
};
|
||||
|
||||
struct input_command_queue
|
||||
{
|
||||
s32 Size;
|
||||
s32 Used;
|
||||
command_queue_entry* Commands;
|
||||
};
|
||||
|
||||
internal void
|
||||
InitializeInputCommandRegistry (input_command_registry* CommandRegistry,
|
||||
s32 Size,
|
||||
gs_memory_arena* Storage)
|
||||
{
|
||||
CommandRegistry->Commands = PushArray(Storage, input_command, Size);
|
||||
CommandRegistry->Size = Size;
|
||||
CommandRegistry->Used = 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
RegisterMouseWheelCommand (input_command_registry* CommandRegistry,
|
||||
input_command_proc* Proc)
|
||||
{
|
||||
CommandRegistry->MouseWheelCommand = Proc;
|
||||
}
|
||||
|
||||
internal s32
|
||||
GetCommandIndexInQueue(input_command_queue* Queue, input_command Command, input_entry Event)
|
||||
{
|
||||
s32 Result = -1;
|
||||
for (s32 CommandIndex = 0; CommandIndex < Queue->Used; CommandIndex++)
|
||||
{
|
||||
command_queue_entry* Entry = Queue->Commands + CommandIndex;
|
||||
if(Entry->Event.Key == Event.Key)
|
||||
{
|
||||
Result = CommandIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal input_command_queue
|
||||
CommandQueue_Create(gs_memory_arena* Storage, u64 CommandMaxCount)
|
||||
{
|
||||
input_command_queue Result = {};
|
||||
Result.Size = CommandMaxCount;
|
||||
Result.Used = 0;
|
||||
Result.Commands = PushArray(Storage, command_queue_entry, CommandMaxCount);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
ClearCommandQueue(input_command_queue* Queue)
|
||||
{
|
||||
Queue->Used = 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
PushCommandOnQueue(input_command_queue* Queue, input_command Command, input_entry Event)
|
||||
{
|
||||
Assert(Queue->Used < Queue->Size);
|
||||
command_queue_entry Entry = {};
|
||||
Entry.Command = Command;
|
||||
Entry.Event = Event;
|
||||
Queue->Commands[Queue->Used++] = Entry;
|
||||
}
|
||||
|
||||
internal void
|
||||
RemoveCommandFromQueue(input_command_queue* Queue, s32 Index)
|
||||
{
|
||||
s32 CommandIndex = Index;
|
||||
if (CommandIndex < Queue->Used)
|
||||
{
|
||||
Queue->Used -= 1;
|
||||
|
||||
for (; CommandIndex < Queue->Used; CommandIndex++)
|
||||
{
|
||||
Queue->Commands[CommandIndex] = Queue->Commands[CommandIndex + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
RemoveCommandFromQueue(input_command_queue* Queue, input_command Command, input_entry Event)
|
||||
{
|
||||
s32 CommandIndex = GetCommandIndexInQueue(Queue, Command, Event);
|
||||
|
||||
// NOTE(Peter): If we made it through the queue without finding an event, there wasn't one
|
||||
// to remove. This happens when we've changed command registries as a result of an input command,
|
||||
// and the command exists in the new registry.
|
||||
// For example:
|
||||
// clicking a mouse button triggers a command to switch registries
|
||||
// the new registry tracks mouse drag (persist until release)
|
||||
// when the mouse is released, the event fires, but there is no mouse down event to remove
|
||||
// For this reason, I'm allowing the case where we try and remove a command where non exists
|
||||
// I don't think this is a great solution but Im not super familiar with the codebase right now
|
||||
// so leaving it as is. revisit if it becomes a problem.
|
||||
RemoveCommandFromQueue(Queue, CommandIndex);
|
||||
}
|
||||
|
||||
internal input_command*
|
||||
FindExistingCommand (input_command_registry CommandRegistry, key_code Key, key_code Mdfr, b32 Flags)
|
||||
{
|
||||
input_command* Result = 0;
|
||||
|
||||
for (s32 Cmd = 0; Cmd < CommandRegistry.Used; Cmd++)
|
||||
{
|
||||
input_command* Command = CommandRegistry.Commands + Cmd;
|
||||
if (Command->Key == Key && Command->Mdfr == Mdfr)
|
||||
{
|
||||
b32 FlagsOverlap = Flags & Command->Flags;
|
||||
if (FlagsOverlap)
|
||||
{
|
||||
Result = Command;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal b32
|
||||
FindAndPushExistingCommand(input_command_registry CommandRegistry, input_entry Event, b32 Flags, input_command_queue* CommandQueue)
|
||||
{
|
||||
b32 CommandFound = false;
|
||||
input_command* Command = FindExistingCommand(CommandRegistry, Event.Key, (key_code)0, Flags);
|
||||
if (Command)
|
||||
{
|
||||
PushCommandOnQueue(CommandQueue, *Command, Event);
|
||||
CommandFound = true;
|
||||
}
|
||||
return CommandFound;
|
||||
}
|
||||
|
||||
internal void
|
||||
RegisterKeyPressCommand (input_command_registry* CommandRegistry,
|
||||
key_code Key,
|
||||
b32 Flags,
|
||||
key_code Mdfr,
|
||||
input_command_proc* Proc)
|
||||
{
|
||||
input_command* Command = FindExistingCommand(*CommandRegistry, Key, Mdfr, Flags);
|
||||
|
||||
if (!Command)
|
||||
{
|
||||
Assert(CommandRegistry->Size > CommandRegistry->Used);
|
||||
Assert(Mdfr == KeyCode_Invalid || Mdfr == KeyCode_LeftShift || Mdfr == KeyCode_RightShift ||
|
||||
Mdfr == KeyCode_LeftCtrl || Mdfr == KeyCode_RightCtrl || Mdfr == KeyCode_Alt);
|
||||
Command = CommandRegistry->Commands + CommandRegistry->Used++;
|
||||
}
|
||||
|
||||
Command->Key = Key;
|
||||
Command->Flags = Flags;
|
||||
Command->Mdfr = Mdfr;
|
||||
Command->Proc = Proc;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_COMMAND_DISPATCH_H
|
||||
#endif // FOLDHAUS_COMMAND_DISPATCH_H
|
|
@ -1,156 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_editor.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-24
|
||||
//
|
||||
#ifndef FOLDHAUS_EDITOR_CPP
|
||||
|
||||
internal void
|
||||
Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue, mouse_state Mouse, context Context)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
|
||||
|
||||
gs_string TextInputString = PushString(State->Transient, 32);
|
||||
|
||||
panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos);
|
||||
if (ActivePanel)
|
||||
{
|
||||
panel_definition ActiveDef = State->PanelSystem.PanelDefs[ActivePanel->TypeIndex];
|
||||
|
||||
input_command_registry ActiveCommands = {};
|
||||
if (State->Modes.ActiveModesCount > 0)
|
||||
{
|
||||
ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
|
||||
}
|
||||
else if (ActiveDef.InputCommands)
|
||||
{
|
||||
ActiveCommands.Commands = ActiveDef.InputCommands;
|
||||
ActiveCommands.Size = sizeof(*ActiveDef.InputCommands) / sizeof(ActiveDef.InputCommands[0]);
|
||||
ActiveCommands.Used = ActiveCommands.Size;
|
||||
}
|
||||
|
||||
// Fill up the command queue
|
||||
for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
|
||||
{
|
||||
input_entry Event = InputQueue.Entries[EventIdx];
|
||||
|
||||
bool IsMouseEvent = (Event.Key == KeyCode_MouseLeftButton ||
|
||||
Event.Key == KeyCode_MouseMiddleButton ||
|
||||
Event.Key == KeyCode_MouseRightButton);
|
||||
if (IsMouseEvent && MouseInputHandled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
|
||||
// Down and Up over Held. In other words, we don't want to call a Held command on the
|
||||
// frame when the button was released, even if the command is registered to both events
|
||||
if (KeyTransitionedDown(Event))
|
||||
{
|
||||
if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue))
|
||||
{
|
||||
char KeyASCII = KeyCodeToChar(Event.Key);
|
||||
if (KeyASCII)
|
||||
{
|
||||
OutChar(&TextInputString, KeyASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (KeyTransitionedUp(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
|
||||
}
|
||||
else if (KeyHeldDown(Event))
|
||||
{
|
||||
if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue))
|
||||
{
|
||||
char KeyASCII = KeyCodeToChar(Event.Key);
|
||||
if (KeyASCII)
|
||||
{
|
||||
OutChar(&TextInputString, KeyASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute all commands in CommandQueue
|
||||
for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--)
|
||||
{
|
||||
command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx];
|
||||
if (Entry->Command.Proc)
|
||||
{
|
||||
Entry->Command.Proc(State, Entry->Event, Mouse, Context, ActivePanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
EndCurrentOperationMode(State);
|
||||
}
|
||||
}
|
||||
|
||||
State->Interface.TempInputString = TextInputString.ConstString;
|
||||
|
||||
ClearCommandQueue(&State->CommandQueue);
|
||||
}
|
||||
|
||||
internal void
|
||||
Editor_Update(app_state* State, context* Context, input_queue InputQueue)
|
||||
{
|
||||
Context->Mouse.CursorType = CursorType_Arrow;
|
||||
State->WindowBounds = Context->WindowBounds;
|
||||
State->Interface.Mouse = Context->Mouse;
|
||||
|
||||
State->Interface.HotWidgetFramesSinceUpdate += 1;
|
||||
if (State->Interface.HotWidgetFramesSinceUpdate > 1)
|
||||
{
|
||||
State->Interface.HotWidget = {};
|
||||
}
|
||||
|
||||
Assert(State->Interface.PerFrameMemory &&
|
||||
(u64)State->Interface.PerFrameMemory != 0x5);
|
||||
|
||||
PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds);
|
||||
Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
|
||||
}
|
||||
|
||||
internal void
|
||||
Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer)
|
||||
{
|
||||
State->Interface.WindowBounds = Context->WindowBounds;
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
PushRenderClearScreen(RenderBuffer);
|
||||
|
||||
|
||||
ui_InterfaceReset(&State->Interface);
|
||||
State->Interface.RenderBuffer = RenderBuffer;
|
||||
ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("Editor Layout"));
|
||||
{
|
||||
DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context);
|
||||
|
||||
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
|
||||
{
|
||||
operation_mode OperationMode = State->Modes.ActiveModes[m];
|
||||
if (OperationMode.Render != 0)
|
||||
{
|
||||
OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
ui_PopLayout(&State->Interface, MakeString("Editor Layout"));
|
||||
|
||||
|
||||
// Draw the Interface
|
||||
if (State->Interface.DrawOrderRoot != 0)
|
||||
{
|
||||
ui_widget* Widget = State->Interface.DrawOrderRoot;
|
||||
Editor_DrawWidgetList(State, Context, RenderBuffer, Widget, Context->WindowBounds);
|
||||
}
|
||||
|
||||
Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_EDITOR_CPP
|
||||
#endif // FOLDHAUS_EDITOR_CPP
|
|
@ -1,170 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_editor_draw.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-16
|
||||
//
|
||||
#ifndef FOLDHAUS_EDITOR_DRAW_H
|
||||
|
||||
internal void
|
||||
Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color, s32 CursorPosition)
|
||||
{
|
||||
gs_string Temp = PushString(State->Transient, 256);
|
||||
PrintF(&Temp, "%d", Widget.Id.Id);
|
||||
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
|
||||
Widget.String.Length + 1,
|
||||
State->Interface.Style.Font->BitmapMemory,
|
||||
State->Interface.Style.Font->BitmapTextureHandle,
|
||||
State->Interface.Style.Font->BitmapWidth,
|
||||
State->Interface.Style.Font->BitmapHeight,
|
||||
State->Interface.Style.Font->BitmapBytesPerPixel,
|
||||
State->Interface.Style.Font->BitmapStride);
|
||||
|
||||
v2 RegisterPosition = Widget.Bounds.Min + State->Interface.Style.Margin;
|
||||
|
||||
switch (Widget.Alignment)
|
||||
{
|
||||
case Align_Left:
|
||||
{
|
||||
RegisterPosition = DrawStringLeftAligned(RenderBuffer,
|
||||
&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color, CursorPosition, GreenV4);
|
||||
}break;
|
||||
|
||||
case Align_Right:
|
||||
{
|
||||
RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
enum widget_fill_dir
|
||||
{
|
||||
WidgetFill_Horizontal = 0,
|
||||
WidgetFill_Vertical = 1,
|
||||
};
|
||||
|
||||
internal rect2
|
||||
Editor_GetWidgetFillBounds(ui_widget Widget)
|
||||
{
|
||||
Assert(ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) || ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawVerticalFill));
|
||||
|
||||
rect2 Result = {};
|
||||
|
||||
widget_fill_dir Dir = WidgetFill_Horizontal;
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill)) { Dir = WidgetFill_Vertical; }
|
||||
widget_fill_dir OtherDir = (widget_fill_dir)(WidgetFill_Vertical - Dir);
|
||||
|
||||
Result.Min.E[Dir] = Widget.Bounds.Min.E[Dir];
|
||||
Result.Max.E[Dir] = Widget.Bounds.Max.E[Dir];
|
||||
r32 FillToPoint = LerpR32(Widget.FillPercent, Widget.Bounds.Min.E[OtherDir], Widget.Bounds.Max.E[OtherDir]);
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillReversed))
|
||||
{
|
||||
Result.Min.E[OtherDir] = FillToPoint;
|
||||
Result.Max.E[OtherDir] = Widget.Bounds.Max.E[OtherDir];
|
||||
}
|
||||
else if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillAsHandle))
|
||||
{
|
||||
Result.Min.E[OtherDir] = FillToPoint - 5;
|
||||
Result.Max.E[OtherDir] = FillToPoint + 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Min.E[OtherDir] = Widget.Bounds.Min.E[OtherDir];
|
||||
Result.Max.E[OtherDir] = FillToPoint;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void Editor_DrawWidgetList(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds);
|
||||
|
||||
internal void
|
||||
Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 WidgetParentUnion)
|
||||
{
|
||||
bool IsActiveWidget = ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget);
|
||||
;
|
||||
if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0))
|
||||
{
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground))
|
||||
{
|
||||
v4 Color = State->Interface.Style.ButtonColor_Inactive;
|
||||
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget))
|
||||
{
|
||||
Color = State->Interface.Style.ButtonColor_Active;
|
||||
}
|
||||
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget))
|
||||
{
|
||||
Color = State->Interface.Style.ButtonColor_Selected;
|
||||
}
|
||||
PushRenderQuad2DClipped(RenderBuffer, Widget.Bounds, WidgetParentUnion, Color);
|
||||
}
|
||||
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0)
|
||||
{
|
||||
v4 Color = State->Interface.Style.TextColor;
|
||||
s32 CursorPosition = -1;
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget)
|
||||
{
|
||||
CursorPosition = State->Interface.CursorPosition;
|
||||
}
|
||||
|
||||
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color, CursorPosition);
|
||||
}
|
||||
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) ||
|
||||
ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawVerticalFill))
|
||||
{
|
||||
v4 Color = State->Interface.Style.ButtonColor_Selected;
|
||||
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget) ||
|
||||
ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget))
|
||||
{
|
||||
Color = WhiteV4;
|
||||
}
|
||||
|
||||
rect2 FillBounds = Editor_GetWidgetFillBounds(Widget);
|
||||
rect2 ClippedFillBounds = Rect2Union(FillBounds, WidgetParentUnion);
|
||||
PushRenderQuad2D(RenderBuffer, ClippedFillBounds, Color);
|
||||
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0)
|
||||
{
|
||||
v4 TextColor = BlackV4;
|
||||
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor, -1);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawOutline = ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline);
|
||||
DrawOutline |= ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget;
|
||||
|
||||
if (DrawOutline)
|
||||
{
|
||||
// TODO(pjs): replace these with values from the style
|
||||
r32 Thickness = 1.0f;
|
||||
v4 Color = WhiteV4;
|
||||
PushRenderBoundingBox2D(RenderBuffer, WidgetParentUnion.Min, WidgetParentUnion.Max, Thickness, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void Editor_DrawWidgetList(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget* Widget, rect2 ParentClipBounds)
|
||||
{
|
||||
ui_widget* WidgetAt = Widget;
|
||||
while (WidgetAt)
|
||||
{
|
||||
rect2 WidgetParentUnion = WidgetAt->Bounds;
|
||||
WidgetParentUnion = Rect2Union(WidgetAt->Bounds, ParentClipBounds);
|
||||
|
||||
Editor_DrawWidget(State, Context, RenderBuffer, *WidgetAt, WidgetParentUnion);
|
||||
|
||||
if (WidgetAt->ChildrenRoot)
|
||||
{
|
||||
Editor_DrawWidgetList(State, Context, RenderBuffer, WidgetAt->ChildrenRoot, WidgetParentUnion);
|
||||
}
|
||||
|
||||
WidgetAt = WidgetAt->Next;
|
||||
}
|
||||
}
|
||||
|
||||
#define FOLDHAUS_EDITOR_DRAW_H
|
||||
#endif // FOLDHAUS_EDITOR_DRAW_H
|
|
@ -1,463 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_interface.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_INTERFACE_CPP
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Panels
|
||||
//
|
||||
///////////////////////////////////////
|
||||
|
||||
enum panel_edit_mode
|
||||
{
|
||||
PanelEdit_Invalid,
|
||||
|
||||
PanelEdit_Modify,
|
||||
PanelEdit_Destroy,
|
||||
|
||||
PanelEdit_Count,
|
||||
};
|
||||
|
||||
//
|
||||
// Drag Panel Border Operation Mode
|
||||
|
||||
OPERATION_STATE_DEF(drag_panel_border_operation_state)
|
||||
{
|
||||
panel* Panel;
|
||||
|
||||
// NOTE(Peter): InitialPanelBounds is the bounds of the panel we are modifying,
|
||||
// it stores the value calculated when the operation mode is kicked off.
|
||||
rect2 InitialPanelBounds;
|
||||
panel_split_direction PanelEdgeDirection;
|
||||
panel_edit_mode PanelEditMode;
|
||||
};
|
||||
|
||||
OPERATION_RENDER_PROC(UpdateAndRenderDragPanelBorder)
|
||||
{
|
||||
drag_panel_border_operation_state* OpState = (drag_panel_border_operation_state*)Operation.OpStateMemory;
|
||||
rect2 PanelBounds = OpState->InitialPanelBounds;
|
||||
|
||||
if (OpState->PanelEditMode == PanelEdit_Modify)
|
||||
{
|
||||
v4 EdgePreviewColor = v4{.3f, .3f, .3f, 1.f};
|
||||
|
||||
v2 EdgePreviewMin = {};
|
||||
v2 EdgePreviewMax = {};
|
||||
if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
|
||||
{
|
||||
EdgePreviewMin = v2{PanelBounds.Min.x, Mouse.Pos.y};
|
||||
EdgePreviewMax = v2{PanelBounds.Max.x, Mouse.Pos.y + 1};
|
||||
}
|
||||
else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
|
||||
{
|
||||
EdgePreviewMin = v2{Mouse.Pos.x, PanelBounds.Min.y};
|
||||
EdgePreviewMax = v2{Mouse.Pos.x + 1, PanelBounds.Max.y};
|
||||
}
|
||||
|
||||
PushRenderQuad2D(RenderBuffer, EdgePreviewMin, EdgePreviewMax, EdgePreviewColor);
|
||||
}
|
||||
else if (OpState->PanelEditMode == PanelEdit_Destroy)
|
||||
{
|
||||
rect2 PanelToDeleteBounds = {};
|
||||
if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
|
||||
{
|
||||
r32 SplitY = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y);
|
||||
if (Mouse.Pos.y > SplitY)
|
||||
{
|
||||
PanelToDeleteBounds = GetTopPanelBounds(OpState->Panel, PanelBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanelToDeleteBounds = GetBottomPanelBounds(OpState->Panel, PanelBounds);
|
||||
}
|
||||
}
|
||||
else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
|
||||
{
|
||||
r32 SplitX = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x);
|
||||
if (Mouse.Pos.x > SplitX)
|
||||
{
|
||||
PanelToDeleteBounds = GetRightPanelBounds(OpState->Panel, PanelBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanelToDeleteBounds = GetLeftPanelBounds(OpState->Panel, PanelBounds);
|
||||
}
|
||||
}
|
||||
v4 OverlayColor = v4{0, 0, 0, .3f};
|
||||
PushRenderQuad2D(RenderBuffer, PanelToDeleteBounds.Min, PanelToDeleteBounds.Max, OverlayColor);
|
||||
}
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation)
|
||||
{
|
||||
drag_panel_border_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_panel_border_operation_state);
|
||||
rect2 PanelBounds = OpState->InitialPanelBounds;
|
||||
|
||||
if (OpState->PanelEditMode == PanelEdit_Modify)
|
||||
{
|
||||
if (OpState->Panel->SplitDirection == PanelSplit_Horizontal)
|
||||
{
|
||||
r32 NewSplitY = Mouse.Pos.y;
|
||||
if (NewSplitY <= PanelBounds.Min.y)
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Top, &State->PanelSystem);
|
||||
}
|
||||
else if (NewSplitY >= PanelBounds.Max.y)
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Bottom, &State->PanelSystem);
|
||||
}
|
||||
else
|
||||
{
|
||||
OpState->Panel->SplitPercent = (NewSplitY - PanelBounds.Min.y) / Rect2Height(PanelBounds);
|
||||
Panel_UpdateLayout(OpState->Panel, PanelBounds);
|
||||
}
|
||||
}
|
||||
else if (OpState->Panel->SplitDirection == PanelSplit_Vertical)
|
||||
{
|
||||
r32 NewSplitX = Mouse.Pos.x;
|
||||
if (NewSplitX <= PanelBounds.Min.x)
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Right, &State->PanelSystem);
|
||||
}
|
||||
else if (NewSplitX >= PanelBounds.Max.x)
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Left, &State->PanelSystem);
|
||||
}
|
||||
else
|
||||
{
|
||||
OpState->Panel->SplitPercent = (NewSplitX - PanelBounds.Min.x) / Rect2Width(PanelBounds);
|
||||
Panel_UpdateLayout(OpState->Panel, PanelBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // PanelEdit_Destroy
|
||||
{
|
||||
if (OpState->PanelEdgeDirection == PanelSplit_Horizontal)
|
||||
{
|
||||
r32 SplitY = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y);
|
||||
if (Mouse.Pos.y > SplitY)
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Bottom, &State->PanelSystem);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Top, &State->PanelSystem);
|
||||
}
|
||||
}
|
||||
else if (OpState->PanelEdgeDirection == PanelSplit_Vertical)
|
||||
{
|
||||
r32 SplitX = LerpR32(OpState->Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x);
|
||||
if (Mouse.Pos.x > SplitX)
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Left, &State->PanelSystem);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsolidatePanelsKeepOne(OpState->Panel, OpState->Panel->Right, &State->PanelSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command DragPanelBorderCommands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragPanelBorderOperation },
|
||||
{ KeyCode_MouseRightButton, KeyCode_Invalid, Command_Ended, EndDragPanelBorderOperation },
|
||||
};
|
||||
|
||||
internal void
|
||||
BeginDragPanelBorder(panel* Panel, panel_edit_mode PanelEditMode, rect2 PanelBounds, panel_split_direction PanelEdgeDirection, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
operation_mode* DragPanelBorder = ActivateOperationModeWithCommands(&State->Modes, DragPanelBorderCommands, UpdateAndRenderDragPanelBorder);
|
||||
drag_panel_border_operation_state* OpState = CreateOperationState(DragPanelBorder, &State->Modes, drag_panel_border_operation_state);
|
||||
OpState->Panel = Panel;
|
||||
OpState->InitialPanelBounds = PanelBounds;
|
||||
OpState->PanelEdgeDirection = PanelEdgeDirection;
|
||||
OpState->PanelEditMode = PanelEditMode;
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
||||
//
|
||||
// Drag To Split Panel Operation
|
||||
|
||||
|
||||
OPERATION_STATE_DEF(split_panel_operation_state)
|
||||
{
|
||||
panel* Panel;
|
||||
|
||||
// NOTE(Peter): InitialPanelBounds is the bounds of the panel we are modifying,
|
||||
// it stores the value calculated when the operation mode is kicked off.
|
||||
rect2 InitialPanelBounds;
|
||||
};
|
||||
|
||||
OPERATION_RENDER_PROC(UpdateAndRenderSplitPanel)
|
||||
{
|
||||
split_panel_operation_state* OpState = (split_panel_operation_state*)Operation.OpStateMemory;
|
||||
rect2 PanelBounds = OpState->InitialPanelBounds;
|
||||
v4 EdgePreviewColor = v4{.3f, .3f, .3f, 1.f};
|
||||
|
||||
r32 MouseDeltaX = Abs(Mouse.Pos.x - Mouse.DownPos.x);
|
||||
r32 MouseDeltaY = Abs(Mouse.Pos.y - Mouse.DownPos.y);
|
||||
|
||||
v2 EdgePreviewMin = {};
|
||||
v2 EdgePreviewMax = {};
|
||||
if (MouseDeltaY > MouseDeltaX) // Horizontal Split
|
||||
{
|
||||
EdgePreviewMin = v2{PanelBounds.Min.x, Mouse.Pos.y};
|
||||
EdgePreviewMax = v2{PanelBounds.Max.x, Mouse.Pos.y + 1};
|
||||
}
|
||||
else // Vertical Split
|
||||
{
|
||||
EdgePreviewMin = v2{Mouse.Pos.x, PanelBounds.Min.y};
|
||||
EdgePreviewMax = v2{Mouse.Pos.x + 1, PanelBounds.Max.y};
|
||||
}
|
||||
|
||||
PushRenderQuad2D(RenderBuffer, EdgePreviewMin, EdgePreviewMax, EdgePreviewColor);
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndSplitPanelOperation)
|
||||
{
|
||||
split_panel_operation_state* OpState = GetCurrentOperationState(State->Modes, split_panel_operation_state);
|
||||
rect2 PanelBounds = OpState->InitialPanelBounds;
|
||||
|
||||
r32 XDistance = Abs(Mouse.Pos.x - Mouse.DownPos.x);
|
||||
r32 YDistance = Abs(Mouse.Pos.y - Mouse.DownPos.y);
|
||||
|
||||
if (XDistance > YDistance)
|
||||
{
|
||||
r32 XPercent = (Mouse.Pos.x - PanelBounds.Min.x) / Rect2Width(PanelBounds);
|
||||
SplitPanelVertically(Panel, XPercent, &State->PanelSystem, State, Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
r32 YPercent = (Mouse.Pos.y - PanelBounds.Min.y) / Rect2Height(PanelBounds);
|
||||
SplitPanelHorizontally(Panel, YPercent, &State->PanelSystem, State, Context);
|
||||
}
|
||||
|
||||
s32 PanelTypeIndex = Panel->TypeIndex;
|
||||
gs_data PanelStateMemory = Panel->StateMemory;
|
||||
Panel_SetCurrentType(Panel->Left, &State->PanelSystem, PanelTypeIndex, PanelStateMemory, State, Context);
|
||||
|
||||
Panel_SetType(Panel->Right, &State->PanelSystem, PanelTypeIndex, State, Context);
|
||||
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command SplitPanelCommands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndSplitPanelOperation },
|
||||
};
|
||||
|
||||
internal void
|
||||
BeginSplitPanelOperation(panel* Panel, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
operation_mode* SplitPanel = ActivateOperationModeWithCommands(&State->Modes, SplitPanelCommands, UpdateAndRenderSplitPanel);
|
||||
split_panel_operation_state* OpState = CreateOperationState(SplitPanel, &State->Modes, split_panel_operation_state);
|
||||
OpState->Panel = Panel;
|
||||
OpState->InitialPanelBounds = Panel->Bounds;
|
||||
}
|
||||
|
||||
|
||||
// ----------------
|
||||
|
||||
#define PANEL_EDGE_CLICK_MAX_DISTANCE 6
|
||||
|
||||
internal b32
|
||||
HandleMouseDownPanelInteractionOrRecurse(panel* Panel, panel_edit_mode PanelEditMode, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
b32 HandledMouseInput = false;
|
||||
|
||||
// TODO(pjs): this can probably live in panel_with_layout
|
||||
rect2 PanelSplitButtonBounds = rect2{ Panel->Bounds.Min, Panel->Bounds.Min + v2{25, 25} };
|
||||
|
||||
if (Panel->SplitDirection == PanelSplit_NoSplit
|
||||
&& PointIsInRect(PanelSplitButtonBounds, Mouse.DownPos))
|
||||
{
|
||||
BeginSplitPanelOperation(Panel, Mouse, State);
|
||||
HandledMouseInput = true;
|
||||
}
|
||||
else if (Panel->SplitDirection != PanelSplit_NoSplit)
|
||||
{
|
||||
u32 ElementIndex = 0;
|
||||
switch(Panel->SplitDirection)
|
||||
{
|
||||
case PanelSplit_Vertical: { ElementIndex = 0; } break;
|
||||
case PanelSplit_Horizontal: { ElementIndex = 1; } break;
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
r32 SplitPosition = LerpR32(Panel->SplitPercent, Panel->Bounds.Min.E[ElementIndex], Panel->Bounds.Max.E[ElementIndex]);
|
||||
r32 ClickDistanceFromSplit = Abs(Mouse.DownPos.E[ElementIndex] - SplitPosition);
|
||||
if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
BeginDragPanelBorder(Panel, PanelEditMode, Panel->Bounds, Panel->SplitDirection, Mouse, State);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PointIsInRect(Panel->Bottom->Bounds, Mouse.DownPos))
|
||||
{
|
||||
HandleMouseDownPanelInteractionOrRecurse(Panel->Bottom, PanelEditMode, Mouse, State);
|
||||
}
|
||||
else if (PointIsInRect(Panel->Top->Bounds, Mouse.DownPos))
|
||||
{
|
||||
HandleMouseDownPanelInteractionOrRecurse(Panel->Top, PanelEditMode, Mouse, State);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HandledMouseInput;
|
||||
}
|
||||
|
||||
internal b32
|
||||
HandleMousePanelInteraction(panel_system* PanelSystem, rect2 WindowBounds, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
b32 HandledMouseInput = false;
|
||||
|
||||
panel* FirstPanel = PanelSystem->Panels + 0;
|
||||
panel_edit_mode EditMode = PanelEdit_Invalid;
|
||||
|
||||
if (MouseButtonTransitionedDown(Mouse.LeftButtonState))
|
||||
{
|
||||
EditMode = PanelEdit_Modify;
|
||||
}
|
||||
else if (MouseButtonTransitionedDown(Mouse.RightButtonState))
|
||||
{
|
||||
EditMode = PanelEdit_Destroy;
|
||||
}
|
||||
|
||||
if (EditMode != PanelEdit_Invalid)
|
||||
{
|
||||
HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, EditMode, Mouse, State);
|
||||
}
|
||||
|
||||
return HandledMouseInput;
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, mouse_state* Mouse, render_command_buffer* RenderBuffer)
|
||||
{
|
||||
r32 MouseLeftEdgeDistance = Abs(Mouse->Pos.x - PanelMin.x);
|
||||
r32 MouseRightEdgeDistance = Abs(Mouse->Pos.x - PanelMax.x);
|
||||
r32 MouseTopEdgeDistance = Abs(Mouse->Pos.y - PanelMax.y);
|
||||
r32 MouseBottomEdgeDistance = Abs(Mouse->Pos.y - PanelMin.y);
|
||||
|
||||
v4 Color = BlackV4;
|
||||
PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color);
|
||||
|
||||
v4 HighlightColor = v4{.3f, .3f, .3f, 1.f};
|
||||
r32 HighlightThickness = 1;
|
||||
if (MouseLeftEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
v2 LeftEdgeMin = PanelMin;
|
||||
v2 LeftEdgeMax = v2{PanelMin.x + HighlightThickness, PanelMax.y};
|
||||
PushRenderQuad2D(RenderBuffer, LeftEdgeMin, LeftEdgeMax, HighlightColor);
|
||||
Mouse->CursorType = CursorType_HArrows;
|
||||
}
|
||||
else if (MouseRightEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
v2 RightEdgeMin = v2{PanelMax.x - HighlightThickness, PanelMin.y};
|
||||
v2 RightEdgeMax = PanelMax;
|
||||
PushRenderQuad2D(RenderBuffer, RightEdgeMin, RightEdgeMax, HighlightColor);
|
||||
Mouse->CursorType = CursorType_HArrows;
|
||||
}
|
||||
else if (MouseTopEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
v2 TopEdgeMin = v2{PanelMin.x, PanelMax.y - HighlightThickness};
|
||||
v2 TopEdgeMax = PanelMax;
|
||||
PushRenderQuad2D(RenderBuffer, TopEdgeMin, TopEdgeMax, HighlightColor);
|
||||
Mouse->CursorType = CursorType_VArrows;
|
||||
}
|
||||
else if (MouseBottomEdgeDistance < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
v2 BottomEdgeMin = PanelMin;
|
||||
v2 BottomEdgeMax = v2{PanelMax.x, PanelMin.y + HighlightThickness};
|
||||
PushRenderQuad2D(RenderBuffer, BottomEdgeMin, BottomEdgeMax, HighlightColor);
|
||||
Mouse->CursorType = CursorType_VArrows;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, rect2 FooterBounds, mouse_state Mouse, app_state* State, context Context)
|
||||
{
|
||||
PushRenderQuad2D(RenderBuffer, FooterBounds.Min, v2{FooterBounds.Max.x, FooterBounds.Min.y + 25}, v4{.5f, .5f, .5f, 1.f});
|
||||
PushRenderQuad2D(RenderBuffer, FooterBounds.Min, FooterBounds.Min + v2{25, 25}, WhiteV4);
|
||||
|
||||
rect2 PanelSelectBtnBounds = MakeRect2MinDim(FooterBounds.Min + v2{30, 1}, v2{100, 23});
|
||||
|
||||
panel_definition CurrentDef = State->PanelSystem.PanelDefs[Panel->TypeIndex];
|
||||
if (ui_BeginDropdown(&State->Interface, MakeString(CurrentDef.PanelName, CurrentDef.PanelNameLength), PanelSelectBtnBounds))
|
||||
{
|
||||
for (s32 i = 0; i < GlobalPanelDefsCount; i++)
|
||||
{
|
||||
panel_definition Def = State->PanelSystem.PanelDefs[i];
|
||||
gs_string DefName = MakeString(Def.PanelName, Def.PanelNameLength);
|
||||
if (ui_Button(&State->Interface, DefName))
|
||||
{
|
||||
Panel_SetType(Panel, &State->PanelSystem, i, State, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
ui_EndDropdown(&State->Interface);
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderPanel(panel* Panel, rect2 PanelBounds, rect2 WindowBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
u32 PanelType = Panel->TypeIndex;
|
||||
Assert(PanelType >= 0);
|
||||
Assert(PanelType < State->PanelSystem.PanelDefsCount);
|
||||
|
||||
rect2 FooterBounds = rect2{
|
||||
PanelBounds.Min,
|
||||
v2{PanelBounds.Max.x, PanelBounds.Min.y + 25},
|
||||
};
|
||||
rect2 PanelViewBounds = rect2{
|
||||
v2{PanelBounds.Min.x, FooterBounds.Max.y},
|
||||
PanelBounds.Max,
|
||||
};
|
||||
|
||||
panel_definition Definition = State->PanelSystem.PanelDefs[PanelType];
|
||||
Definition.Render(Panel, PanelViewBounds, RenderBuffer, State, Context);
|
||||
|
||||
PushRenderOrthographic(RenderBuffer, WindowBounds);
|
||||
DrawPanelFooter(Panel, RenderBuffer, FooterBounds, Mouse, State, Context);
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawPanelRecursive(panel* Panel, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context)
|
||||
{
|
||||
rect2 Bounds = Panel->Bounds;
|
||||
switch (Panel->SplitDirection)
|
||||
{
|
||||
case PanelSplit_Horizontal:
|
||||
case PanelSplit_Vertical:
|
||||
{
|
||||
DrawPanelRecursive(Panel->Left, RenderBuffer, Mouse, State, Context);
|
||||
DrawPanelRecursive(Panel->Right, RenderBuffer, Mouse, State, Context);
|
||||
}break;
|
||||
|
||||
case PanelSplit_NoSplit:
|
||||
{
|
||||
panel* OverridePanel = Panel_GetModalOverride(Panel);
|
||||
RenderPanel(OverridePanel, Bounds, State->WindowBounds, RenderBuffer, State, Context, *Mouse);
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
DrawPanelBorder(*OverridePanel, Bounds.Min, Bounds.Max, Mouse, RenderBuffer);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawAllPanels(panel_system System, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context)
|
||||
{
|
||||
panel* PanelAt = System.Panels + 0;
|
||||
DrawPanelRecursive(PanelAt, RenderBuffer, Mouse, State, Context);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_INTERFACE_CPP
|
||||
#endif // FOLDHAUS_INTERFACE_CPP
|
|
@ -1,142 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_operation_mode.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_OPERATION_MODE_H
|
||||
|
||||
typedef struct operation_mode operation_mode;
|
||||
|
||||
#define OPERATION_STATE_DEF(name) struct name
|
||||
|
||||
#define OPERATION_RENDER_PROC(name) void name(app_state* State, render_command_buffer* RenderBuffer, operation_mode Operation, mouse_state Mouse, context Context)
|
||||
typedef OPERATION_RENDER_PROC(operation_render_proc);
|
||||
|
||||
struct operation_mode
|
||||
{
|
||||
input_command_registry Commands;
|
||||
operation_render_proc* Render;
|
||||
gs_memory_cursor Memory;
|
||||
u8* OpStateMemory;
|
||||
};
|
||||
|
||||
#define OPERATION_MODES_MAX 32
|
||||
struct operation_mode_system
|
||||
{
|
||||
s32 ActiveModesCount;
|
||||
operation_mode ActiveModes[OPERATION_MODES_MAX];
|
||||
//arena_snapshot ModeMemorySnapshots[OPERATION_MODES_MAX];
|
||||
gs_data_array ModeMemoryPagesFreeList;
|
||||
|
||||
// NOTE(Peter): This acts as mode scoped memory. When a mode gets activated, it can allocate
|
||||
// temporary memory which then gets freed when the mode is deactivated
|
||||
gs_memory_arena Arena;
|
||||
};
|
||||
|
||||
internal operation_mode_system
|
||||
OperationModeSystemInit(gs_memory_arena* Storage, gs_thread_context ThreadContext)
|
||||
{
|
||||
operation_mode_system Result = {0};
|
||||
// TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint?
|
||||
Result.Arena.Allocator = ThreadContext.Allocator;
|
||||
|
||||
Result.ModeMemoryPagesFreeList.CountMax = 8;
|
||||
Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax);
|
||||
for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++)
|
||||
{
|
||||
Result.ModeMemoryPagesFreeList.Data[Page] = PushSize(Storage, KB(4));
|
||||
}
|
||||
Result.ModeMemoryPagesFreeList.Count = Result.ModeMemoryPagesFreeList.CountMax;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_data
|
||||
OperationModeTakeMemoryPage(operation_mode_system* System)
|
||||
{
|
||||
Assert(System->ModeMemoryPagesFreeList.Count > 0);
|
||||
gs_data Result = {0};
|
||||
System->ModeMemoryPagesFreeList.Count -= 1;
|
||||
u64 LastIndex = System->ModeMemoryPagesFreeList.Count;
|
||||
Result = System->ModeMemoryPagesFreeList.Data[LastIndex];
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
OperationModeFreeMemoryPage(operation_mode_system* System, gs_data Data)
|
||||
{
|
||||
Assert(System->ModeMemoryPagesFreeList.Count < System->ModeMemoryPagesFreeList.CountMax);
|
||||
u64 LastIndex = System->ModeMemoryPagesFreeList.Count;
|
||||
System->ModeMemoryPagesFreeList.Count += 1;
|
||||
System->ModeMemoryPagesFreeList.Data[LastIndex] = Data;
|
||||
}
|
||||
|
||||
internal operation_mode*
|
||||
ActivateOperationMode (operation_mode_system* System, operation_render_proc* RenderProc)
|
||||
{
|
||||
Assert(System->ActiveModesCount < OPERATION_MODES_MAX);
|
||||
operation_mode* Result = 0;
|
||||
s32 ModeIndex = System->ActiveModesCount++;
|
||||
|
||||
//System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena);
|
||||
|
||||
Result = &System->ActiveModes[ModeIndex];
|
||||
Result->Memory = MemoryCursorCreateFromData(OperationModeTakeMemoryPage(System));
|
||||
Result->Render = RenderProc;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define ActivateOperationModeWithCommands(sys, cmds, render) \
|
||||
ActivateOperationModeWithCommands_(sys, cmds, (s32)(sizeof(cmds) / sizeof(cmds[0])), render);
|
||||
|
||||
internal operation_mode*
|
||||
ActivateOperationModeWithCommands_(operation_mode_system* System, input_command* Commands, s32 CommandsCount, operation_render_proc* RenderProc)
|
||||
{
|
||||
operation_mode* NewMode = ActivateOperationMode(System, RenderProc);
|
||||
|
||||
#if 0
|
||||
InitializeInputCommandRegistry(&NewMode->Commands, CommandsCount, &System->Arena);
|
||||
for (s32 i = 0; i < CommandsCount; i++)
|
||||
{
|
||||
input_command Command = Commands[i];
|
||||
RegisterKeyPressCommand(&NewMode->Commands, Command.Key, Command.Flags, Command.Mdfr, Command.Proc);
|
||||
}
|
||||
#else
|
||||
NewMode->Commands.Commands = Commands;
|
||||
NewMode->Commands.Size = CommandsCount;
|
||||
NewMode->Commands.Used = CommandsCount;
|
||||
#endif
|
||||
return NewMode;
|
||||
}
|
||||
|
||||
internal void
|
||||
DeactivateCurrentOperationMode (operation_mode_system* System)
|
||||
{
|
||||
Assert(System->ActiveModesCount > 0);
|
||||
s32 ModeIndex = --System->ActiveModesCount;
|
||||
OperationModeFreeMemoryPage(System, System->ActiveModes[ModeIndex].Memory.Data);
|
||||
//ClearArenaToSnapshot(&System->Arena, System->ModeMemorySnapshots[ModeIndex]);
|
||||
}
|
||||
|
||||
#define CreateOperationState(mode, modeSystem, stateType) \
|
||||
(stateType*)CreateOperationState_(mode, modeSystem, sizeof(stateType))
|
||||
|
||||
#define GetCurrentOperationState(modeSystem, stateType) \
|
||||
(stateType*)(modeSystem).ActiveModes[(modeSystem).ActiveModesCount - 1].OpStateMemory;
|
||||
|
||||
|
||||
internal u8*
|
||||
CreateOperationState_ (operation_mode* Mode, operation_mode_system* System, s32 StateSize)
|
||||
{
|
||||
// NOTE(Peter): This isn't a problem if this fires, it just means our page size is too small,
|
||||
// and its time to make the pages dynamically sized
|
||||
Assert(Mode->Memory.Data.Size >= StateSize);
|
||||
u8* Result = MemoryCursorPushSize(&Mode->Memory, StateSize).Memory;
|
||||
Mode->OpStateMemory = Result;
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_OPERATION_MODE_H
|
||||
#endif // FOLDHAUS_OPERATION_MODE_H
|
|
@ -1,442 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2019-12-26
|
||||
//
|
||||
// Usage:
|
||||
// Include this file in ONE file in your project.
|
||||
// Define SetPanelDefinitionExternal
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_H
|
||||
|
||||
enum panel_split_direction
|
||||
{
|
||||
PanelSplit_NoSplit,
|
||||
PanelSplit_Horizontal,
|
||||
PanelSplit_Vertical,
|
||||
|
||||
PanelSplit_Count,
|
||||
};
|
||||
|
||||
typedef struct panel panel;
|
||||
|
||||
#define PANEL_MODAL_OVERRIDE_CALLBACK(name) void name(panel* ReturningFrom, app_state* State, context Context)
|
||||
typedef PANEL_MODAL_OVERRIDE_CALLBACK(panel_modal_override_callback);
|
||||
|
||||
struct panel
|
||||
{
|
||||
s32 TypeIndex;
|
||||
gs_data StateMemory;
|
||||
|
||||
panel* ModalOverride;
|
||||
panel* IsModalOverrideFor;
|
||||
panel_modal_override_callback* ModalOverrideCB;
|
||||
|
||||
rect2 Bounds;
|
||||
panel_split_direction SplitDirection;
|
||||
r32 SplitPercent;
|
||||
|
||||
panel* Parent;
|
||||
|
||||
union{
|
||||
panel* Left;
|
||||
panel* Top;
|
||||
};
|
||||
union{
|
||||
panel* Right;
|
||||
panel* Bottom;
|
||||
};
|
||||
};
|
||||
|
||||
struct free_panel
|
||||
{
|
||||
free_panel* Next;
|
||||
};
|
||||
|
||||
#define PANEL_INIT_PROC(name) void name(panel* Panel, app_state* State, context Context)
|
||||
typedef PANEL_INIT_PROC(panel_init_proc);
|
||||
|
||||
#define PANEL_CLEANUP_PROC(name) void name(panel* Panel, app_state* State)
|
||||
typedef PANEL_CLEANUP_PROC(panel_cleanup_proc);
|
||||
|
||||
#define PANEL_RENDER_PROC(name) void name(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
typedef PANEL_RENDER_PROC(panel_render_proc);
|
||||
|
||||
// NOTE(Peter): This is used by the meta system to generate panel type info
|
||||
struct panel_definition
|
||||
{
|
||||
char* PanelName;
|
||||
s32 PanelNameLength;
|
||||
panel_init_proc* Init;
|
||||
panel_cleanup_proc* Cleanup;
|
||||
panel_render_proc* Render;
|
||||
input_command* InputCommands;
|
||||
s32 InputCommandsCount;
|
||||
};
|
||||
|
||||
#define PANELS_MAX 16
|
||||
struct panel_system
|
||||
{
|
||||
panel_definition* PanelDefs;
|
||||
u32 PanelDefsCount;
|
||||
|
||||
panel* Panels;
|
||||
u32 PanelsUsed;
|
||||
|
||||
free_panel* FreeList;
|
||||
};
|
||||
|
||||
/////////////////////////////////
|
||||
//
|
||||
// Book-Keeping
|
||||
//
|
||||
/////////////////////////////////
|
||||
|
||||
internal void
|
||||
PanelSystem_Init(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount, gs_memory_arena* Storage)
|
||||
{
|
||||
PanelSystem->FreeList = 0;
|
||||
PanelSystem->PanelDefs = PanelDefs;
|
||||
PanelSystem->PanelDefsCount = PanelDefsCount;
|
||||
|
||||
PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX);
|
||||
}
|
||||
|
||||
internal panel*
|
||||
PanelSystem_TakePanel(panel_system* PanelSystem)
|
||||
{
|
||||
panel* FreeEntry = 0;
|
||||
if (PanelSystem->FreeList != 0)
|
||||
{
|
||||
free_panel* FreePanel = PanelSystem->FreeList;
|
||||
PanelSystem->FreeList = FreePanel->Next;
|
||||
FreeEntry = (panel*)FreePanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(PanelSystem->PanelsUsed < PANELS_MAX);
|
||||
FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++;
|
||||
}
|
||||
return FreeEntry;
|
||||
}
|
||||
|
||||
internal void
|
||||
PanelSystem_FreePanel(panel* Panel, panel_system* PanelSystem)
|
||||
{
|
||||
Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX);
|
||||
|
||||
free_panel* FreeEntry = (free_panel*)Panel;
|
||||
FreeEntry->Next = PanelSystem->FreeList;
|
||||
PanelSystem->FreeList = FreeEntry;
|
||||
}
|
||||
|
||||
internal void
|
||||
PanelSystem_FreePanelRecursive(panel* Panel, panel_system* PanelSystem)
|
||||
{
|
||||
if (Panel->SplitDirection != PanelSplit_NoSplit)
|
||||
{
|
||||
PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem);
|
||||
PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem);
|
||||
}
|
||||
PanelSystem_FreePanel(Panel, PanelSystem);
|
||||
}
|
||||
|
||||
internal panel*
|
||||
Panel_GetModalOverride(panel* Panel)
|
||||
{
|
||||
panel* Result = Panel;
|
||||
if (Panel->ModalOverride != 0)
|
||||
{
|
||||
Result = Panel_GetModalOverride(Panel->ModalOverride);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_PushModalOverride(panel* Root, panel* Override, panel_modal_override_callback* Callback)
|
||||
{
|
||||
Root->ModalOverride = Override;
|
||||
Root->ModalOverrideCB = Callback;
|
||||
Override->IsModalOverrideFor = Root;
|
||||
Override->Bounds = Root->Bounds;
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_PopModalOverride(panel* Parent, panel_system* System)
|
||||
{
|
||||
// TODO(pjs): Free the overrided panel
|
||||
PanelSystem_FreePanel(Parent->ModalOverride, System);
|
||||
Parent->ModalOverride = 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_SetCurrentType(panel* Panel, panel_system* System, s32 NewPanelType, gs_data TypeStateMemory, app_state* State, context Context)
|
||||
{
|
||||
s32 OldTypeIndex = Panel->TypeIndex;
|
||||
|
||||
Panel->TypeIndex = NewPanelType;
|
||||
Panel->StateMemory = TypeStateMemory;
|
||||
|
||||
if(OldTypeIndex >= 0)
|
||||
{
|
||||
System->PanelDefs[OldTypeIndex].Cleanup(Panel, State);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_SetType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context)
|
||||
{
|
||||
gs_data EmptyStateData = {0};
|
||||
Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context);
|
||||
System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context);
|
||||
}
|
||||
|
||||
#define Panel_GetStateStruct(p, type) (type*)Panel_GetStateMemory((p), sizeof(type)).Memory
|
||||
internal gs_data
|
||||
Panel_GetStateMemory(panel* Panel, u64 Size)
|
||||
{
|
||||
Assert(Panel->StateMemory.Size == Size);
|
||||
gs_data Result = Panel->StateMemory;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal panel*
|
||||
PanelSystem_PushPanel(panel_system* PanelSystem, s32 PanelTypeIndex, app_state* State, context Context)
|
||||
{
|
||||
panel* Panel = PanelSystem_TakePanel(PanelSystem);
|
||||
Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context);
|
||||
return Panel;
|
||||
}
|
||||
|
||||
internal void
|
||||
SplitPanel(panel* Parent, r32 Percent, panel_split_direction SplitDirection, panel_system* PanelSystem, app_state* State, context Context)
|
||||
{
|
||||
if (Percent >= 0.0f && Percent <= 1.0f)
|
||||
{
|
||||
Parent->SplitDirection = SplitDirection;
|
||||
Parent->SplitPercent = Percent;
|
||||
|
||||
s32 ParentTypeIndex = Parent->TypeIndex;
|
||||
gs_data ParentStateMemory = Parent->StateMemory;
|
||||
|
||||
Parent->Left = PanelSystem_TakePanel(PanelSystem);
|
||||
Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
|
||||
Parent->Left->Parent = Parent;
|
||||
|
||||
Parent->Right = PanelSystem_TakePanel(PanelSystem);
|
||||
Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
|
||||
Parent->Right->Parent = Parent;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
SplitPanelVertically(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context)
|
||||
{
|
||||
SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context);
|
||||
}
|
||||
|
||||
internal void
|
||||
SplitPanelHorizontally(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context)
|
||||
{
|
||||
SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context);
|
||||
}
|
||||
|
||||
internal void
|
||||
ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelSystem)
|
||||
{
|
||||
panel* LeftChild = Parent->Left;
|
||||
panel* RightChild = Parent->Right;
|
||||
|
||||
panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild;
|
||||
|
||||
*Parent = *PanelToKeep;
|
||||
|
||||
PanelSystem_FreePanel(PanelToKeep, PanelSystem);
|
||||
PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem);
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
//
|
||||
// Rendering And Interaction
|
||||
//
|
||||
/////////////////////////////////
|
||||
|
||||
internal rect2
|
||||
GetTopPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = v2{
|
||||
Panel->Bounds.Min.x,
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
|
||||
};
|
||||
Result.Max = Panel->Bounds.Max;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetBottomPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = Panel->Bounds.Min;
|
||||
Result.Max = v2{
|
||||
Panel->Bounds.Max.x,
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetRightPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = v2{
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
|
||||
Panel->Bounds.Min.y
|
||||
};
|
||||
Result.Max = Panel->Bounds.Max;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetLeftPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = Panel->Bounds.Min;
|
||||
Result.Max = v2{
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
|
||||
Panel->Bounds.Max.y
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetTopPanelBounds(panel* Panel, rect2 PanelBounds)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = v2{
|
||||
PanelBounds.Min.x,
|
||||
LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
|
||||
};
|
||||
Result.Max = PanelBounds.Max;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetBottomPanelBounds(panel* Panel, rect2 PanelBounds)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = PanelBounds.Min;
|
||||
Result.Max = v2{
|
||||
PanelBounds.Max.x,
|
||||
LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetRightPanelBounds(panel* Panel, rect2 PanelBounds)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = v2{
|
||||
LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
|
||||
PanelBounds.Min.y
|
||||
};
|
||||
Result.Max = PanelBounds.Max;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetLeftPanelBounds(panel* Panel, rect2 PanelBounds)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = PanelBounds.Min;
|
||||
Result.Max = v2{
|
||||
LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
|
||||
PanelBounds.Max.y
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_UpdateLayout(panel* Panel, rect2 Bounds)
|
||||
{
|
||||
Panel->Bounds = Bounds;
|
||||
|
||||
if (Panel->SplitDirection != PanelSplit_NoSplit)
|
||||
{
|
||||
rect2 LeftOrTopBounds = {};
|
||||
rect2 RightOrBottomBounds = {};
|
||||
switch (Panel->SplitDirection)
|
||||
{
|
||||
case PanelSplit_Horizontal:
|
||||
{
|
||||
LeftOrTopBounds = GetTopPanelBounds(Panel);
|
||||
RightOrBottomBounds = GetBottomPanelBounds(Panel);
|
||||
} break;
|
||||
|
||||
case PanelSplit_Vertical:
|
||||
{
|
||||
LeftOrTopBounds = GetLeftPanelBounds(Panel);
|
||||
RightOrBottomBounds = GetRightPanelBounds(Panel);
|
||||
} break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
Panel_UpdateLayout(Panel->Left, LeftOrTopBounds);
|
||||
Panel_UpdateLayout(Panel->Right, RightOrBottomBounds);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
PanelSystem_UpdateLayout(panel_system* System, rect2 WindowBounds)
|
||||
{
|
||||
panel* Root = System->Panels;
|
||||
Panel_UpdateLayout(Root, WindowBounds);
|
||||
}
|
||||
|
||||
internal panel*
|
||||
GetPanelContainingPoint(panel* Panel, v2 Point)
|
||||
{
|
||||
panel* Result = 0;
|
||||
|
||||
if (PointIsInRect(Panel->Bounds, Point))
|
||||
{
|
||||
switch (Panel->SplitDirection)
|
||||
{
|
||||
case PanelSplit_NoSplit:
|
||||
{
|
||||
Result = Panel;
|
||||
}break;
|
||||
|
||||
case PanelSplit_Vertical:
|
||||
case PanelSplit_Horizontal:
|
||||
{asdfasdfasdfasdfasdf
|
||||
if (PointIsInRect(Panel->Left->Bounds, Point))
|
||||
{
|
||||
Result = GetPanelContainingPoint(Panel->Left, Point);
|
||||
}
|
||||
else if (PointIsInRect(Panel->Right->Bounds, Point))
|
||||
{
|
||||
Result = GetPanelContainingPoint(Panel->Right, Point);
|
||||
}
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal panel*
|
||||
PanelSystem_GetPanelContainingPoint(panel_system* System, v2 Point)
|
||||
{
|
||||
panel* Result = 0;
|
||||
panel* Root = System->Panels;
|
||||
Result = GetPanelContainingPoint(Root, Point);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_PANEL_H
|
||||
#endif // FOLDHAUS_PANEL_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,146 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_assembly_debug.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-15
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_ASSEMBLY_DEBUG_H
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
AssemblyDebug_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
AssemblyDebug_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// TODO(pjs): This is really blumen specific
|
||||
#define FSC(f,c) FlowerStripToChannel((f), (c))
|
||||
internal u8
|
||||
FlowerStripToChannel(u8 Flower, u8 Channel)
|
||||
{
|
||||
Assert(Flower < 3);
|
||||
Assert(Channel < 8);
|
||||
|
||||
u8 Result = 0;
|
||||
Result |= (Flower & 0x03) << 3;
|
||||
Result |= (Channel & 0x07);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_interface* Interface = &State->Interface;
|
||||
ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Assembly Debug Layout"));
|
||||
|
||||
ui_BeginRow(Interface, 2);
|
||||
{
|
||||
if (ui_Button(Interface, MakeString("Assembly")))
|
||||
{
|
||||
State->ShowingUserSpaceDebug = false;
|
||||
}
|
||||
|
||||
if (ui_Button(Interface, MakeString("User Space")))
|
||||
{
|
||||
State->ShowingUserSpaceDebug = true;
|
||||
}
|
||||
}
|
||||
ui_EndRow(Interface);
|
||||
|
||||
if (State->ShowingUserSpaceDebug && State->UserSpaceDesc.CustomDebugUI)
|
||||
{
|
||||
US_CustomDebugUI(&State->UserSpaceDesc, Panel, PanelBounds, RenderBuffer,
|
||||
State, Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterfaceAssert(Interface->PerFrameMemory);
|
||||
|
||||
State->AssemblyDebugState.AllAssemblies = ui_ToggleText(Interface, MakeString("All Assemblies"), State->AssemblyDebugState.AllAssemblies);
|
||||
|
||||
gs_string OverrideStr = MakeString(OverrideTypeStrings[State->AssemblyDebugState.Override]);
|
||||
if (ui_BeginLabeledDropdown(Interface, MakeString("Override"), OverrideStr))
|
||||
{
|
||||
for (u32 i = 0; i < ADS_Override_Count; i++)
|
||||
{
|
||||
if (ui_Button(Interface, MakeString(OverrideTypeStrings[i])))
|
||||
{
|
||||
State->AssemblyDebugState.Override = (override_type)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
ui_EndLabeledDropdown(Interface);
|
||||
InterfaceAssert(Interface->PerFrameMemory);
|
||||
|
||||
switch (State->AssemblyDebugState.Override)
|
||||
{
|
||||
case ADS_Override_TagWhite:
|
||||
case ADS_Override_TagStripWhite:
|
||||
{
|
||||
ui_LabeledTextEntry(Interface, MakeString("Tag Name"), &State->AssemblyDebugState.TagName);
|
||||
ui_LabeledTextEntry(Interface, MakeString("Tag Value"), &State->AssemblyDebugState.TagValue);
|
||||
|
||||
if (State->AssemblyDebugState.Override == ADS_Override_TagStripWhite)
|
||||
{
|
||||
State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
|
||||
|
||||
State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip);
|
||||
}
|
||||
}break;
|
||||
|
||||
case ADS_Override_ChannelWhite:
|
||||
{
|
||||
u64 Board = 0;
|
||||
u64 Strip = 0;
|
||||
Board = ui_LabeledTextEntryU64(Interface, MakeString("Board"), Board);
|
||||
Strip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), Strip);
|
||||
|
||||
State->AssemblyDebugState.TargetChannel = FSC(Board, Strip);
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllOff:
|
||||
case ADS_Override_AllRed:
|
||||
case ADS_Override_AllGreen:
|
||||
case ADS_Override_AllBlue:
|
||||
case ADS_Override_AllWhite:
|
||||
{
|
||||
State->AssemblyDebugState.Brightness = (u8)ui_LabeledRangeSlider(Interface, MakeString("Brightness"), (r32)State->AssemblyDebugState.Brightness, 0, 255);
|
||||
State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllHue:
|
||||
{
|
||||
State->AssemblyDebugState.TargetHue = (u32)ui_LabeledRangeSlider(Interface, MakeString("Hue"), (r32)State->AssemblyDebugState.TargetHue, 0, 360);
|
||||
}break;
|
||||
|
||||
default:
|
||||
{
|
||||
InterfaceAssert(Interface->PerFrameMemory);
|
||||
|
||||
State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
|
||||
|
||||
InterfaceAssert(Interface->PerFrameMemory);
|
||||
|
||||
State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip);
|
||||
|
||||
InterfaceAssert(Interface->PerFrameMemory);
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
State->SendEmptyPackets = ui_LabeledToggle(Interface, MakeString("Send Empty Packets"), State->SendEmptyPackets);
|
||||
ui_PopLayout(Interface, MakeString("Assembly Debug Layout"));
|
||||
}
|
||||
|
||||
#define FOLDHAUS_PANEL_ASSEMBLY_DEBUG_H
|
||||
#endif // FOLDHAUS_PANEL_ASSEMBLY_DEBUG_H
|
|
@ -1,148 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_dmx_view.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_DMX_VIEW_H
|
||||
|
||||
struct universe_view_operation_state
|
||||
{
|
||||
b32 MouseDown;
|
||||
v2 DisplayOffset;
|
||||
r32 Zoom;
|
||||
};
|
||||
|
||||
input_command DMXView_Commands[] = {{}};
|
||||
s32 DMXView_CommandsCount = 0;
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_dmx_view);
|
||||
internal void
|
||||
DMXView_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_dmx_view);
|
||||
internal void
|
||||
DMXView_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// NOTE(Peter): Here to illustrate what old SACN Universe drawing looked like
|
||||
// This won't actually function
|
||||
// :NoLongerFunctionalSACNCodeButThatsOk
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_dmx_view);
|
||||
internal void
|
||||
DMXView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
#if 0
|
||||
// :NoLongerFunctionalSACNCodeButThatsOk
|
||||
DEBUG_TRACK_SCOPE(DrawUniverseOutputDisplay);
|
||||
|
||||
universe_view_operation_state* OpState = (universe_view_operation_state*)Operation.OpStateMemory;
|
||||
|
||||
gs_string TitleBargs_string = InitializeEmptygs_string(PushArray(State->Transient, char, 64), 64);
|
||||
|
||||
v2 DisplayArea_Dimension = v2{600, 600};
|
||||
|
||||
v2 DisplayContents_Offset = OpState->DisplayOffset;
|
||||
|
||||
//
|
||||
// TODO(Peter): I don't like this. Dragging the Universe view should be an operation mode, just
|
||||
// like rotating the 3D view, but modes don't have access to the state of modes above them in the stack
|
||||
// (and attempting to cast those states to the appropriate type seems risky)
|
||||
//
|
||||
// :NeedToPassStateDownModeChain
|
||||
//
|
||||
if (OpState->MouseDown)
|
||||
{
|
||||
DisplayContents_Offset += (Mouse.Pos - Mouse.DownPos);
|
||||
}
|
||||
|
||||
v2 DisplayArea_TopLeft = v2{300, (r32)RenderBuffer->ViewHeight - 50} + DisplayContents_Offset;
|
||||
v2 UniverseDisplayDimension = v2{100, 100} * OpState->Zoom;
|
||||
v2 Padding = v2{25, 50} * OpState->Zoom;
|
||||
|
||||
v2 UniverseDisplayTopLeft = DisplayArea_TopLeft;
|
||||
|
||||
sacn_universe_buffer* UniverseList = State->SACN.UniverseBuffer;
|
||||
while(UniverseList)
|
||||
{
|
||||
for (s32 UniverseIdx = 0;
|
||||
UniverseIdx < UniverseList->Used;
|
||||
UniverseIdx++)
|
||||
{
|
||||
sacn_universe* Universe = UniverseList->Universes + UniverseIdx;
|
||||
DrawSACNUniversePixels(RenderBuffer, Universe, UniverseDisplayTopLeft, UniverseDisplayDimension);
|
||||
|
||||
|
||||
if (OpState->Zoom > .5f)
|
||||
{
|
||||
v2 TitleDisplayStart = UniverseDisplayTopLeft + v2{0, 12};
|
||||
PrintF(&TitleBargs_string, "Universe %d", Universe->Universe);
|
||||
DrawString(RenderBuffer, TitleBargs_string, State->Interface.Font,
|
||||
TitleDisplayStart, WhiteV4);
|
||||
}
|
||||
|
||||
UniverseDisplayTopLeft.x += UniverseDisplayDimension.x + Padding.x;
|
||||
if (UniverseDisplayTopLeft.x > DisplayArea_TopLeft.x + DisplayArea_Dimension.x)
|
||||
{
|
||||
UniverseDisplayTopLeft.x = DisplayArea_TopLeft.x;
|
||||
UniverseDisplayTopLeft.y -= UniverseDisplayDimension.y + Padding.y;
|
||||
}
|
||||
|
||||
if (UniverseDisplayTopLeft.y < DisplayArea_TopLeft.y - DisplayArea_Dimension.y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
UniverseList = UniverseList->Next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_PANEL_DMX_VIEW_H
|
||||
#endif // FOLDHAUS_PANEL_DMX_VIEW_H
|
|
@ -1,200 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_file_view.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-03-08
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_FILE_VIEW_H
|
||||
|
||||
enum file_view_mode
|
||||
{
|
||||
FileViewMode_Load,
|
||||
FileViewMode_Save,
|
||||
};
|
||||
|
||||
struct file_view_state
|
||||
{
|
||||
file_view_mode Mode;
|
||||
|
||||
gs_string WorkingDirectory;
|
||||
gs_string DisplayDirectory;
|
||||
|
||||
gs_memory_arena FileNamesArena;
|
||||
gs_file_info_array FileNames;
|
||||
|
||||
gs_file_info SelectedFile;
|
||||
};
|
||||
|
||||
internal void
|
||||
FileView_SetMode(panel* Panel, file_view_mode Mode)
|
||||
{
|
||||
file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
|
||||
FileViewState->Mode = Mode;
|
||||
}
|
||||
|
||||
internal void
|
||||
FileView_Exit_(panel* FileViewPanel, app_state* State, context Context)
|
||||
{
|
||||
// TODO(pjs): Free State->FileNamesArena
|
||||
|
||||
Assert(FileViewPanel->IsModalOverrideFor != 0);
|
||||
panel* ReturnTo = FileViewPanel->IsModalOverrideFor;
|
||||
if (ReturnTo->ModalOverrideCB)
|
||||
{
|
||||
ReturnTo->ModalOverrideCB(FileViewPanel, State, Context);
|
||||
}
|
||||
Panel_PopModalOverride(ReturnTo, &State->PanelSystem);
|
||||
}
|
||||
|
||||
global input_command* FileView_Commands = 0;
|
||||
s32 FileView_CommandsCount = 0;
|
||||
|
||||
internal void
|
||||
FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context)
|
||||
{
|
||||
// NOTE(pjs): make sure we're only passing valid directory paths to the
|
||||
// function
|
||||
char LastChar = WorkingDirectory.Str[WorkingDirectory.Length - 1];
|
||||
Assert(LastChar == '\\' || LastChar == '/');
|
||||
MemoryArenaClear(&State->FileNamesArena);
|
||||
|
||||
|
||||
gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2);
|
||||
SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient);
|
||||
if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\')
|
||||
{
|
||||
AppendPrintF(&SanitizedDir, "\\");
|
||||
}
|
||||
|
||||
gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString;
|
||||
|
||||
gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString);
|
||||
if (NewWorkingDir.IsDirectory)
|
||||
{
|
||||
AppendPrintF(&SanitizedDir, "*");
|
||||
NullTerminate(&SanitizedDir);
|
||||
|
||||
State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, SanitizedDir.ConstString, EnumerateDirectory_IncludeDirectories);
|
||||
|
||||
// NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory
|
||||
// in some cases. Shouldn't be a problem but it is unnecessary
|
||||
PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString);
|
||||
PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir);
|
||||
}
|
||||
}
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
FileView_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
// TODO: :FreePanelMemory
|
||||
file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state);
|
||||
Panel->StateMemory = StructToData(FileViewState, file_view_state);
|
||||
FileViewState->FileNamesArena = MemoryArenaCreate(MB(4), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "File View - File Names Arena");
|
||||
|
||||
// TODO(pjs): this shouldn't be stored in permanent
|
||||
FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024);
|
||||
FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024);
|
||||
|
||||
FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context);
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
FileView_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
|
||||
|
||||
ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout"));
|
||||
{
|
||||
ui_BeginRow(&State->Interface, 3);
|
||||
{
|
||||
if (FileViewState->Mode == FileViewMode_Save)
|
||||
{
|
||||
if (ui_Button(&State->Interface, MakeString("Save")))
|
||||
{
|
||||
if (!FileViewState->SelectedFile.Path.Str)
|
||||
{
|
||||
FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString;
|
||||
}
|
||||
|
||||
FileView_Exit_(Panel, State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_Button(&State->Interface, MakeString("Exit")))
|
||||
{
|
||||
FileView_Exit_(Panel, State, Context);
|
||||
}
|
||||
}
|
||||
ui_EndRow(&State->Interface);
|
||||
|
||||
// Header
|
||||
if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory))
|
||||
{
|
||||
// if last character is a slash, update pwd, and clear the filter string
|
||||
// otherwise update the filter string
|
||||
gs_string Pwd = FileViewState->DisplayDirectory;
|
||||
char LastChar = Pwd.Str[Pwd.Length - 1];
|
||||
if (LastChar == '\\' || LastChar == '/')
|
||||
{
|
||||
FileView_UpdateWorkingDirectory(Pwd.ConstString, FileViewState, Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// File Display
|
||||
ui_BeginList(&State->Interface, MakeString("Files"), 10, FileViewState->FileNames.Count);
|
||||
for (u32 i = 0; i < FileViewState->FileNames.Count; i++)
|
||||
{
|
||||
gs_file_info File = FileViewState->FileNames.Values[i];
|
||||
|
||||
u32 LastSlashIndex = FindLast(File.Path, File.Path.Length - 2, '\\');
|
||||
gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length);
|
||||
gs_string PathString = PushString(State->Transient, FileName.Length);
|
||||
PrintF(&PathString, "%S", FileName);
|
||||
|
||||
if (ui_LayoutListButton(&State->Interface, PathString, i))
|
||||
{
|
||||
if (File.IsDirectory)
|
||||
{
|
||||
FileView_UpdateWorkingDirectory(File.Path, FileViewState, Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileViewState->SelectedFile = File;
|
||||
switch (FileViewState->Mode)
|
||||
{
|
||||
case FileViewMode_Load:
|
||||
{
|
||||
FileView_Exit_(Panel, State, Context);
|
||||
} break;
|
||||
|
||||
case FileViewMode_Save:
|
||||
{
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ui_EndList(&State->Interface);
|
||||
}
|
||||
ui_PopLayout(&State->Interface, MakeString("FileView Layout"));
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_PANEL_FILE_VIEW_H
|
||||
#endif // FOLDHAUS_PANEL_FILE_VIEW_H
|
|
@ -1,83 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_hierarchy.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_HIERARCHY_H
|
||||
|
||||
input_command HierarchyView_Commands[] = {{}};
|
||||
s32 HierarchyView_CommandsCount = 0;
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_hierarchy);
|
||||
internal void
|
||||
HierarchyView_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_hierarchy);
|
||||
internal void
|
||||
HierarchyView_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PANEL_MODAL_OVERRIDE_CALLBACK(LoadAssemblyCallback)
|
||||
{
|
||||
Assert(ReturningFrom->TypeIndex == PanelType_FileView);
|
||||
file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state);
|
||||
gs_file_info FileInfo = FileViewState->SelectedFile;
|
||||
|
||||
LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, GlobalLogBuffer);
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_hierarchy);
|
||||
internal void
|
||||
HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
gs_string TempString = PushString(State->Transient, 256);
|
||||
|
||||
ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Hierarchy Layout"));
|
||||
ui_BeginList(&State->Interface, MakeString("Hierarchy List"), 10, State->Assemblies.Count + 1);
|
||||
{
|
||||
ui_column_spec Cols[2] = {
|
||||
ui_column_spec{ UIColumnSize_Fill, 0 },
|
||||
ui_column_spec{ UIColumnSize_MaxWidth, 128 }
|
||||
};
|
||||
for (u32 i = 0; i < State->Assemblies.Count; i++)
|
||||
{
|
||||
ui_BeginRow(&State->Interface, 2, &Cols[0]);
|
||||
|
||||
assembly Assembly = State->Assemblies.Values[i];
|
||||
PrintF(&TempString, "%S", Assembly.Name);
|
||||
|
||||
ui_Label(&State->Interface, TempString);
|
||||
if (ui_Button(&State->Interface, MakeString("X")))
|
||||
{
|
||||
UnloadAssembly(i, State, Context);
|
||||
}
|
||||
|
||||
ui_EndRow(&State->Interface);
|
||||
}
|
||||
|
||||
|
||||
ui_BeginRow(&State->Interface, 2, &Cols[0]);
|
||||
ui_Label(&State->Interface, MakeString(" "));
|
||||
if (ui_Button(&State->Interface, MakeString("+ Add Assembly")))
|
||||
{
|
||||
panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
|
||||
FileView_SetMode(FileBrowser, FileViewMode_Load);
|
||||
Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback);
|
||||
}
|
||||
ui_EndRow(&State->Interface);
|
||||
}
|
||||
ui_EndList(&State->Interface);
|
||||
ui_PopLayout(&State->Interface, MakeString("Hierarchy Layout"));
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_PANEL_HIERARCHY_H
|
||||
#endif // FOLDHAUS_PANEL_HIERARCHY_H
|
|
@ -1,46 +0,0 @@
|
|||
/* date = April 12th 2021 4:47 pm */
|
||||
|
||||
#ifndef FOLDHAUS_PANEL_MESSAGE_LOG_H
|
||||
#define FOLDHAUS_PANEL_MESSAGE_LOG_H
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
MessageLog_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
MessageLog_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_file_view);
|
||||
internal void
|
||||
MessageLog_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_interface* Interface = &State->Interface;
|
||||
ui_widget* Layout = ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Message Log Layout"));
|
||||
|
||||
ui_BeginList(Interface, MakeString("Message Log List"), 10, GlobalLogBuffer->EntriesCount);
|
||||
|
||||
log_buffer_iter Iter = Log_GetIter(GlobalLogBuffer);
|
||||
while (true)
|
||||
{
|
||||
log_entry* At = Iter.At;
|
||||
ui_Label(Interface, At->String);
|
||||
if (!LogIter_CanAdvance(Iter))
|
||||
{
|
||||
break;
|
||||
}
|
||||
LogIter_Advance(&Iter);
|
||||
}
|
||||
ui_EndList(Interface);
|
||||
|
||||
ui_PopLayout(Interface, MakeString("Message Log Layout"));
|
||||
}
|
||||
#endif //FOLDHAUS_PANEL_MESSAGE_LOG_H
|
|
@ -1,360 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_profiler.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_PROFILER_H
|
||||
|
||||
input_command ProfilerView_Commands[] = {{}};
|
||||
s32 ProfilerView_CommandsCount = 0;
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_profiler);
|
||||
internal void
|
||||
ProfilerView_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_profiler);
|
||||
internal void
|
||||
ProfilerView_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Transient)
|
||||
{
|
||||
rect2 Bounds = ui_LayoutRemaining(*Layout);
|
||||
r32 Width = Rect2Width(Bounds);
|
||||
r32 DepthHeight = 32;
|
||||
|
||||
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
|
||||
r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles);
|
||||
|
||||
r32 NextThreadTop = Bounds.Max.y;
|
||||
|
||||
for (s32 t = 0; t < VisibleFrame->ThreadCount; t++)
|
||||
{
|
||||
debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t];
|
||||
|
||||
gs_string String = PushString(Transient, 256);
|
||||
|
||||
r32 ThreadScopeMin = Bounds.Max.y;
|
||||
|
||||
//PrintF(&String, "Thread %d", ThreadCalls.ThreadId);
|
||||
//ui_Label(Interface, String, rect2{ThreadScopeMin);
|
||||
|
||||
r32 Hue = (r32)(t) / (r32)(VisibleFrame->ThreadCount);
|
||||
Hue += (.5f * (t % 2));
|
||||
v4 ThreadHSV = v4{ 360.0f * Hue, .5f, 1.0f, 1.0f };
|
||||
v4 ThreadRGB = HSVToRGB(ThreadHSV);
|
||||
|
||||
for (s32 i = 0; i < ThreadCalls.Count; i++)
|
||||
{
|
||||
scope_record* Record = ThreadCalls.Calls + i;
|
||||
scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash);
|
||||
s64 OffsetStart = Record->StartCycles - FrameStartCycles;
|
||||
s64 OffsetEnd = Record->EndCycles - FrameStartCycles;
|
||||
r32 PercentStart = (r32)(OffsetStart) / FrameTotalCycles;
|
||||
r32 PercentEnd = (r32)(OffsetEnd) / FrameTotalCycles;
|
||||
r32 PercentWidth = PercentEnd - PercentStart;
|
||||
|
||||
rect2 ScopeBounds = {
|
||||
v2{0, 0},
|
||||
v2{PercentWidth * Width, DepthHeight - 4},
|
||||
};
|
||||
v2 Offset = {
|
||||
Bounds.Min.x + (PercentStart * Width),
|
||||
NextThreadTop - ((Record->CallDepth + 1) * DepthHeight)
|
||||
};
|
||||
ScopeBounds = Rect2Translate(ScopeBounds, Offset);
|
||||
ThreadScopeMin = Min(ScopeBounds.Min.y, ThreadScopeMin);
|
||||
|
||||
if (Rect2Width(ScopeBounds) >= 1)
|
||||
{
|
||||
v4 Color = ThreadRGB;
|
||||
if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos))
|
||||
{
|
||||
Color = GreenV4;
|
||||
|
||||
ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover"));
|
||||
{
|
||||
s64 Cycles = (Record->EndCycles - Record->StartCycles);
|
||||
r32 PercentFrame = (r32)(Cycles) / FrameTotalCycles;
|
||||
PrintF(&String, "%S : %.2f%% frame | %dcy",
|
||||
Name->Name,
|
||||
PercentFrame,
|
||||
Cycles);
|
||||
ui_Label(Interface, String);
|
||||
}
|
||||
ui_EndMousePopup(Interface);
|
||||
}
|
||||
|
||||
ui_FillRect(Interface, ScopeBounds, Color);
|
||||
ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4);
|
||||
}
|
||||
}
|
||||
|
||||
NextThreadTop = ThreadScopeMin;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory)
|
||||
{
|
||||
char Backbuffer[256];
|
||||
gs_string String = MakeString(Backbuffer, 0, 256);
|
||||
|
||||
ui_column_spec ColumnWidths[] = {
|
||||
{ UIColumnSize_Fixed, 256 },
|
||||
{ UIColumnSize_Fixed, 128 },
|
||||
{ UIColumnSize_Fixed, 128 },
|
||||
{ UIColumnSize_Fixed, 128 },
|
||||
{ UIColumnSize_Fixed, 128 }};
|
||||
ui_BeginRow(Interface, 5, &ColumnWidths[0]);
|
||||
{
|
||||
ui_Label(Interface, MakeString("Procedure"));
|
||||
ui_Label(Interface, MakeString("% Frame"));
|
||||
ui_Label(Interface, MakeString("Seconds"));
|
||||
ui_Label(Interface, MakeString("Cycles"));
|
||||
ui_Label(Interface, MakeString("Calls"));
|
||||
}
|
||||
ui_EndRow(Interface);
|
||||
|
||||
s32 CountedScopes = 0;
|
||||
for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
|
||||
{
|
||||
scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
|
||||
if (NameEntry.Hash != 0)
|
||||
{
|
||||
CountedScopes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes);
|
||||
ui_BeginRow(Interface, 5, &ColumnWidths[0]);
|
||||
for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
|
||||
{
|
||||
scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
|
||||
if (NameEntry.Hash != 0)
|
||||
{
|
||||
collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
|
||||
|
||||
PrintF(&String, "%S", NameEntry.Name);
|
||||
ui_Label(Interface, String);
|
||||
|
||||
PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime);
|
||||
ui_Label(Interface, String);
|
||||
|
||||
PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
|
||||
ui_Label(Interface, String);
|
||||
|
||||
PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
|
||||
ui_Label(Interface, String);
|
||||
|
||||
PrintF(&String, "%d", CollatedRecord->CallCount);
|
||||
ui_Label(Interface, String);
|
||||
}
|
||||
}
|
||||
ui_EndRow(Interface);
|
||||
ui_EndList(Interface);
|
||||
}
|
||||
|
||||
struct mem_amt
|
||||
{
|
||||
u64 OrigSize;
|
||||
r64 Size;
|
||||
char* Units;
|
||||
};
|
||||
|
||||
internal mem_amt
|
||||
GetMemAmt (u64 BytesCount)
|
||||
{
|
||||
mem_amt Result = {};
|
||||
Result.OrigSize = BytesCount;
|
||||
Result.Size = (r64)BytesCount;
|
||||
Result.Units = "bytes";
|
||||
|
||||
u32 i = 0;
|
||||
char* UnitList[] = { "kb", "mb", "gb", "tb" };
|
||||
while (Result.Size > 1024) {
|
||||
Result.Size /= 1024.0;
|
||||
Result.Units = UnitList[i++];
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderProfiler_MemoryView(ui_interface* Interface, ui_widget* Layout, app_state* State, context Context, gs_memory_arena* Memory)
|
||||
{
|
||||
gs_debug_allocations_list* DA = Context.ThreadContext.Allocator.DEBUGAllocList;
|
||||
|
||||
gs_string TempString = PushString(State->Transient, 256);
|
||||
|
||||
mem_amt MemFootprint = GetMemAmt(DA->AllocationsSizeTotal);
|
||||
u64 AllocCount = DA->AllocationsCount;
|
||||
|
||||
PrintF(&TempString, "Total Memory Size: %.2f %s | Allocations: %lld", MemFootprint.Size, MemFootprint.Units, AllocCount);
|
||||
ui_Label(Interface, TempString);
|
||||
|
||||
ui_column_spec ColumnWidths[] = {
|
||||
{ UIColumnSize_Fill, 0 },
|
||||
{ UIColumnSize_Fixed,256 },
|
||||
};
|
||||
ui_BeginRow(Interface, 2, &ColumnWidths[0]);
|
||||
{
|
||||
ui_Label(Interface, MakeString("Location"));
|
||||
ui_Label(Interface, MakeString("Alloc Size"));
|
||||
}
|
||||
ui_EndRow(Interface);
|
||||
|
||||
ui_BeginList(Interface, MakeString("Alloc List"), 10, DA->AllocationsCount);
|
||||
ui_BeginRow(Interface, 2, &ColumnWidths[0]);
|
||||
|
||||
for (gs_debug_memory_allocation* A = DA->Root;
|
||||
A && A->Next != 0;
|
||||
A = A->Next)
|
||||
{
|
||||
gs_const_string Str = ConstString(A->Loc.File);
|
||||
u64 LastSlash = FindLastFromSet(Str, "\\/");
|
||||
gs_const_string JustFileName = Substring(Str, LastSlash + 1, Str.Length);
|
||||
PrintF(&TempString, "%s:%s(%d)", JustFileName.Str, A->Loc.Function, A->Loc.Line);
|
||||
ui_Label(Interface, TempString);
|
||||
|
||||
mem_amt Amt = GetMemAmt(A->Size);
|
||||
|
||||
PrintF(&TempString, "%.2f %s", Amt.Size, Amt.Units);
|
||||
ui_Label(Interface, TempString);
|
||||
}
|
||||
ui_EndRow(Interface);
|
||||
ui_EndList(Interface);
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_profiler);
|
||||
internal void
|
||||
ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
gs_memory_arena* Memory = State->Transient;
|
||||
gs_string String = PushString(Memory, 256);
|
||||
|
||||
v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 };
|
||||
|
||||
r32 FrameListHeight = 64;
|
||||
rect2 FrameListBounds, ProcListBounds;
|
||||
RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds);
|
||||
rect2 FrameListInner = RectInset(FrameListBounds, 4);
|
||||
|
||||
s32 FramesToDisplay = DEBUG_FRAME_COUNT;
|
||||
if (FramesToDisplay != 0)
|
||||
{
|
||||
r32 SingleFrameStep = Rect2Width(FrameListInner) / FramesToDisplay;
|
||||
r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2);
|
||||
|
||||
ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4);
|
||||
if (MouseButtonHeldDown(Context.Mouse.LeftButtonState))
|
||||
{
|
||||
if (PointIsInRect(FrameListBounds, Context.Mouse.Pos))
|
||||
{
|
||||
v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos);
|
||||
s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep);
|
||||
if (ClosestFrameIndex >= 0 && ClosestFrameIndex < FramesToDisplay)
|
||||
{
|
||||
GlobalDebugServices->RecordFrames = false;
|
||||
GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)});
|
||||
for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++)
|
||||
{
|
||||
rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep);
|
||||
s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F);
|
||||
if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; }
|
||||
v4 Color = FrameColors[Clamp(0, FramesAgo, 3)];
|
||||
ui_FillRect(&State->Interface, PositionedFrameBounds, Color);
|
||||
}
|
||||
}
|
||||
|
||||
ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout"));
|
||||
|
||||
debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
|
||||
if (VisibleFrame)
|
||||
{
|
||||
ui_BeginRow(&State->Interface, 4);
|
||||
{
|
||||
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
|
||||
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
|
||||
u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1;
|
||||
PrintF(&String, "Frame %d", CurrentDebugFrame);
|
||||
ui_Label(&State->Interface, String);
|
||||
|
||||
PrintF(&String, "Total Cycles: %lld", FrameTotalCycles);
|
||||
ui_Label(&State->Interface, String);
|
||||
|
||||
// NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could
|
||||
// be removed, or used for something else
|
||||
ui_ReserveBounds(&State->Interface, Layout, true);
|
||||
|
||||
if (ui_Button(&State->Interface, MakeString("Resume Recording")))
|
||||
{
|
||||
GlobalDebugServices->RecordFrames = true;
|
||||
}
|
||||
}
|
||||
ui_EndRow(&State->Interface);
|
||||
}
|
||||
|
||||
ui_BeginRow(&State->Interface, 8);
|
||||
{
|
||||
if (ui_Button(&State->Interface, MakeString("Profiler")))
|
||||
{
|
||||
GlobalDebugServices->Interface.FrameView = DebugUI_Profiler;
|
||||
}
|
||||
if (ui_Button(&State->Interface, MakeString("List View")))
|
||||
{
|
||||
GlobalDebugServices->Interface.FrameView = DebugUI_ScopeList;
|
||||
}
|
||||
if (ui_Button(&State->Interface, MakeString("Memory")))
|
||||
{
|
||||
GlobalDebugServices->Interface.FrameView = DebugUI_MemoryView;
|
||||
}
|
||||
}
|
||||
ui_EndRow(&State->Interface);
|
||||
|
||||
switch (GlobalDebugServices->Interface.FrameView)
|
||||
{
|
||||
case DebugUI_Profiler:
|
||||
{
|
||||
if (VisibleFrame)
|
||||
{
|
||||
RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory);
|
||||
}
|
||||
}break;
|
||||
|
||||
case DebugUI_ScopeList:
|
||||
{
|
||||
if (VisibleFrame)
|
||||
{
|
||||
RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory);
|
||||
}
|
||||
}break;
|
||||
|
||||
case DebugUI_MemoryView:
|
||||
{
|
||||
RenderProfiler_MemoryView(&State->Interface, Layout, State, Context, Memory);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
ui_PopLayout(&State->Interface, MakeString("Profiler Layout"));
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_PANEL_PROFILER_H
|
||||
#endif // FOLDHAUS_PANEL_PROFILER_H
|
|
@ -1,244 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_sculpture_view.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_SCULPTURE_VIEW_H
|
||||
|
||||
// Definitions
|
||||
|
||||
#define PIXEL_TO_WORLD_SCALE 0.01f
|
||||
|
||||
//
|
||||
|
||||
struct sculpture_view_panel_state
|
||||
{
|
||||
camera Camera;
|
||||
};
|
||||
|
||||
// 3D Mouse View
|
||||
|
||||
OPERATION_STATE_DEF(mouse_rotate_view_operation_state)
|
||||
{
|
||||
v4 CameraStartPos;
|
||||
camera* Camera;
|
||||
};
|
||||
|
||||
OPERATION_RENDER_PROC(Update3DViewMouseRotate)
|
||||
{
|
||||
mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory;
|
||||
|
||||
v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos;
|
||||
|
||||
m44 XRotation = M44RotationX(-TotalDeltaPos.y * PIXEL_TO_WORLD_SCALE);
|
||||
m44 YRotation = M44RotationY(TotalDeltaPos.x * PIXEL_TO_WORLD_SCALE);
|
||||
m44 Combined = XRotation * YRotation;
|
||||
|
||||
OpState->Camera->Position = (Combined * OpState->CameraStartPos).xyz;
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(End3DViewMouseRotate)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
input_command MouseRotateViewCommands [] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate},
|
||||
};
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate)
|
||||
{
|
||||
sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
|
||||
|
||||
operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate);
|
||||
mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode,
|
||||
&State->Modes,
|
||||
mouse_rotate_view_operation_state);
|
||||
OpState->CameraStartPos = ToV4Point(PanelState->Camera.Position);
|
||||
OpState->Camera = &PanelState->Camera;
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
||||
GSMetaTag(panel_commands);
|
||||
GSMetaTag(panel_type_sculpture_view);
|
||||
global input_command SculptureView_Commands[] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, Begin3DViewMouseRotate },
|
||||
};
|
||||
global s32 SculptureView_CommandsCount = 1;
|
||||
|
||||
GSMetaTag(panel_init);
|
||||
GSMetaTag(panel_type_sculpture_view);
|
||||
internal void
|
||||
SculptureView_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
sculpture_view_panel_state* PanelState = PushStruct(&State->Permanent, sculpture_view_panel_state);
|
||||
|
||||
PanelState->Camera.FieldOfView = 45.0f;
|
||||
PanelState->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
|
||||
PanelState->Camera.Near = .1f;
|
||||
PanelState->Camera.Far = 800.0f;
|
||||
PanelState->Camera.Position = v3{0, 0, 400};
|
||||
PanelState->Camera.LookAt = v3{0, 0, 0};
|
||||
|
||||
Panel->StateMemory = StructToData(PanelState, sculpture_view_panel_state);
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
GSMetaTag(panel_type_sculpture_view);
|
||||
internal void
|
||||
SculptureView_Cleanup(panel* Panel, app_state* State)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct draw_leds_job_data
|
||||
{
|
||||
v4 CameraPosition;
|
||||
led_buffer LedBuffer;
|
||||
s32 StartIndex;
|
||||
s32 OnePastLastIndex;
|
||||
render_quad_batch_constructor* Batch;
|
||||
quad_batch_constructor_reserved_range BatchReservedRange;
|
||||
r32 LEDHalfWidth;
|
||||
};
|
||||
|
||||
internal void
|
||||
DrawLedsInBuffer(led_buffer LedBuffer, s32 StartIndex, s32 OnePastLastIndex, render_quad_batch_constructor* Batch, quad_batch_constructor_reserved_range ReservedRange, r32 LedHalfWidth)
|
||||
{
|
||||
s32 TrisUsed = 0;
|
||||
|
||||
v4 P0_In = v4{-LedHalfWidth, -LedHalfWidth, 0, 1};
|
||||
v4 P1_In = v4{LedHalfWidth, -LedHalfWidth, 0, 1};
|
||||
v4 P2_In = v4{LedHalfWidth, LedHalfWidth, 0, 1};
|
||||
v4 P3_In = v4{-LedHalfWidth, LedHalfWidth, 0, 1};
|
||||
|
||||
v2 UV0 = v2{0, 0};
|
||||
v2 UV1 = v2{1, 0};
|
||||
v2 UV2 = v2{1, 1};
|
||||
v2 UV3 = v2{0, 1};
|
||||
|
||||
Assert(OnePastLastIndex <= (s32)LedBuffer.LedCount);
|
||||
for (s32 LedIndex = StartIndex; LedIndex < OnePastLastIndex; LedIndex++)
|
||||
{
|
||||
pixel PixelColor = LedBuffer.Colors[LedIndex];
|
||||
v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f};
|
||||
|
||||
v4 Position = LedBuffer.Positions[LedIndex];
|
||||
v4 PositionOffset = ToV4Vec(Position.xyz);
|
||||
v4 P0 = P0_In + PositionOffset;
|
||||
v4 P1 = P1_In + PositionOffset;
|
||||
v4 P2 = P2_In + PositionOffset;
|
||||
v4 P3 = P3_In + PositionOffset;
|
||||
|
||||
SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P1, P2, UV0, UV1, UV2, Color, Color, Color);
|
||||
SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P2, P3, UV0, UV2, UV3, Color, Color, Color);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawLEDsInBufferRangeJob (gs_thread_context Context, gs_data JobData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
draw_leds_job_data* Data = (draw_leds_job_data*)JobData.Memory;
|
||||
DrawLedsInBuffer(Data->LedBuffer, Data->StartIndex, Data->OnePastLastIndex, Data->Batch, Data->BatchReservedRange, Data->LEDHalfWidth);
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawQuad(render_command_buffer* RenderBuffer, v4 C, r32 Rad, v4 Color)
|
||||
{
|
||||
v4 P0 = C + v4{-Rad,-Rad,0,0};
|
||||
v4 P1 = C + v4{ Rad,-Rad,0,0};
|
||||
v4 P2 = C + v4{ Rad,Rad,0,0};
|
||||
v4 P3 = C + v4{ -Rad,Rad,0,0};
|
||||
PushRenderQuad3D(RenderBuffer, P0, P1, P2, P3, Color);
|
||||
}
|
||||
|
||||
internal v2
|
||||
SculptureView_WorldToScreenPosition(v4 WorldPosition, camera Camera, rect2 PanelBounds)
|
||||
{
|
||||
v2 Result = {0};
|
||||
|
||||
r32 PanelW = Rect2Width(PanelBounds);
|
||||
r32 PanelH = Rect2Height(PanelBounds);
|
||||
|
||||
m44 Matrix = GetCameraPerspectiveProjectionMatrix(Camera) * GetCameraModelViewMatrix(Camera);
|
||||
v4 WorldPos = Matrix * WorldPosition;
|
||||
|
||||
// this is the Perspective Divide
|
||||
v2 ProjectedPos = WorldPos.xy / WorldPos.w;
|
||||
|
||||
// Projection gets us in a range [-1, 1], and we want [0, width]
|
||||
ProjectedPos.x = ((ProjectedPos.x / 2) * PanelW) + (PanelW / 2);
|
||||
ProjectedPos.y = ((ProjectedPos.y / 2) * PanelH) + (PanelH / 2);
|
||||
|
||||
Result = ProjectedPos + PanelBounds.Min;
|
||||
return Result;
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
GSMetaTag(panel_type_sculpture_view);
|
||||
internal void
|
||||
SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
DEBUG_TRACK_SCOPE(RenderSculpture);
|
||||
sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
|
||||
PanelState->Camera.AspectRatio = RectAspectRatio(PanelBounds);
|
||||
|
||||
PushRenderPerspective(RenderBuffer, PanelBounds, PanelState->Camera);
|
||||
|
||||
u32 MaxLEDsPerJob = 2048;
|
||||
render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->LedSystem.LedsCountTotal);
|
||||
|
||||
u32 FocusPixel = 100;
|
||||
|
||||
for (u32 BufferIndex = 0; BufferIndex < State->LedSystem.BuffersCount; BufferIndex++)
|
||||
{
|
||||
led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, BufferIndex);
|
||||
u32 JobsNeeded = U32DivideRoundUp(LedBuffer->LedCount, MaxLEDsPerJob);
|
||||
u32 NextLEDIndex = 0;
|
||||
for (u32 Job = 0; Job < JobsNeeded; Job++)
|
||||
{
|
||||
gs_data Data = PushSize(State->Transient, sizeof(draw_leds_job_data));
|
||||
draw_leds_job_data* JobData = (draw_leds_job_data*)Data.Memory;
|
||||
JobData->LedBuffer = *LedBuffer;
|
||||
JobData->StartIndex = NextLEDIndex;
|
||||
JobData->OnePastLastIndex = Min(JobData->StartIndex + MaxLEDsPerJob, LedBuffer->LedCount);
|
||||
s32 JobLedCount = JobData->OnePastLastIndex - JobData->StartIndex;
|
||||
JobData->Batch = &RenderLEDsBatch;
|
||||
JobData->BatchReservedRange = ReserveRangeInQuadConstructor(JobData->Batch, JobLedCount * 2);
|
||||
JobData->LEDHalfWidth = .5f;
|
||||
JobData->CameraPosition = ToV4Point(PanelState->Camera.Position);
|
||||
#if 1
|
||||
Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, (thread_proc*)DrawLEDsInBufferRangeJob, Data, ConstString("Sculpture Draw LEDS"));
|
||||
#else
|
||||
DrawLedsInBuffer(JobData->LedBuffer, JobData->StartIndex, JobData->OnePastLastIndex, JobData->Batch, JobData->BatchReservedRange, JobData->LEDHalfWidth);
|
||||
#endif
|
||||
NextLEDIndex = JobData->OnePastLastIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Peter): I don't like the fact that setting an orthographic view inside a panel render function
|
||||
// needs to relyon the window bounds rather than the panel bounds. Ideally the panel only needs to know where
|
||||
// itself is, and nothing else.
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
if (State->Assemblies.Count > 0)
|
||||
{
|
||||
assembly Assembly = State->Assemblies.Values[0];
|
||||
led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly.LedBufferIndex);
|
||||
|
||||
v4 LedPosition = LedBuffer->Positions[FocusPixel];
|
||||
v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds);
|
||||
|
||||
gs_string Tempgs_string = PushString(State->Transient, 256);
|
||||
PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id,
|
||||
State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id);
|
||||
DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4, -1, GreenV4);
|
||||
|
||||
}
|
||||
Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_PANEL_SCULPTURE_VIEW_H
|
||||
#endif // FOLDHAUS_PANEL_SCULPTURE_VIEW_H
|
|
@ -1,19 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_types.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-17
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_TYPES_CPP
|
||||
global s32 GlobalPanelDefsCount = 8;
|
||||
global panel_definition GlobalPanelDefs[] = {
|
||||
{ "File View", 9, FileView_Init, FileView_Cleanup, FileView_Render, FileView_Commands, FileView_CommandsCount },
|
||||
{ "Sculpture View", 14, SculptureView_Init, SculptureView_Cleanup, SculptureView_Render, SculptureView_Commands, SculptureView_CommandsCount },
|
||||
{ "Animation Timeline", 18, AnimationTimeline_Init, AnimationTimeline_Cleanup, AnimationTimeline_Render, AnimationTimeline_Commands, AnimationTimeline_CommandsCount },
|
||||
{ "Dmx View", 8, DMXView_Init, DMXView_Cleanup, DMXView_Render, DMXView_Commands, DMXView_CommandsCount },
|
||||
{ "Hierarchy", 9, HierarchyView_Init, HierarchyView_Cleanup, HierarchyView_Render, HierarchyView_Commands, HierarchyView_CommandsCount },
|
||||
{ "Profiler", 8, ProfilerView_Init, ProfilerView_Cleanup, ProfilerView_Render, ProfilerView_Commands, ProfilerView_CommandsCount },
|
||||
{ "Assembly Debug", 14, AssemblyDebug_Init, AssemblyDebug_Cleanup, AssemblyDebug_Render, 0, 0 },
|
||||
{ "Message Log", 11, MessageLog_Init, MessageLog_Cleanup, MessageLog_Render, 0, 0 },
|
||||
};
|
||||
#define FOLDHAUS_PANEL_TYPES_CPP
|
||||
#endif // FOLDHAUS_PANEL_TYPES_CPP
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_panel_types.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-17
|
||||
//
|
||||
#ifndef FOLDHAUS_PANEL_TYPES_H
|
||||
enum panel_type {
|
||||
PanelType_FileView,
|
||||
PanelType_SculptureView,
|
||||
PanelType_AnimationTimeline,
|
||||
PanelType_DMXView,
|
||||
PanelType_HierarchyView,
|
||||
PanelType_ProfilerView,
|
||||
PanelType_AssemblyDebug,
|
||||
PanelType_MessageLog
|
||||
};
|
||||
#define FOLDHAUS_PANEL_TYPES_H
|
||||
#endif // FOLDHAUS_PANEL_TYPES_H
|
|
@ -1,843 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_animation.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_ANIMATION
|
||||
|
||||
#define ANIMATION_PROC(name) void name(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
typedef ANIMATION_PROC(animation_proc);
|
||||
|
||||
struct frame_range
|
||||
{
|
||||
s32 Min;
|
||||
s32 Max;
|
||||
};
|
||||
|
||||
struct animation_pattern_handle
|
||||
{
|
||||
s32 IndexPlusOne;
|
||||
};
|
||||
|
||||
// NOTE(pjs): An animation block is a time range paired with an
|
||||
// animation_pattern (see below). While a timeline's current time
|
||||
// is within the range of a block, that particular block's animation
|
||||
// will run
|
||||
struct animation_block
|
||||
{
|
||||
frame_range Range;
|
||||
animation_pattern_handle AnimationProcHandle;
|
||||
u32 Layer;
|
||||
};
|
||||
|
||||
struct animation_block_array
|
||||
{
|
||||
u32* Generations;
|
||||
animation_block* Values;
|
||||
u32 Count;
|
||||
u32 CountMax;
|
||||
};
|
||||
|
||||
enum blend_mode
|
||||
{
|
||||
BlendMode_Overwrite,
|
||||
BlendMode_Add,
|
||||
BlendMode_Multiply,
|
||||
BlendMode_Count,
|
||||
};
|
||||
|
||||
// TODO(pjs): This really doesn't belong here
|
||||
global gs_string BlendModeStrings[] = {
|
||||
MakeString("Overwrite"),
|
||||
MakeString("Add"),
|
||||
MakeString("Multiply"),
|
||||
MakeString("Count"),
|
||||
};
|
||||
|
||||
struct anim_layer
|
||||
{
|
||||
gs_string Name;
|
||||
blend_mode BlendMode;
|
||||
};
|
||||
|
||||
struct anim_layer_array
|
||||
{
|
||||
anim_layer* Values;
|
||||
u32 Count;
|
||||
u32 CountMax;
|
||||
};
|
||||
|
||||
// NOTE(pjs): An animation is a stack of layers, each of which
|
||||
// is a timeline of animation blocks.
|
||||
struct animation
|
||||
{
|
||||
gs_string Name;
|
||||
|
||||
anim_layer_array Layers;
|
||||
animation_block_array Blocks_;
|
||||
|
||||
frame_range PlayableRange;
|
||||
|
||||
// The information / path to the file where this animation is to be saved / where it is loaded from
|
||||
gs_file_info FileInfo;
|
||||
};
|
||||
|
||||
struct animation_handle
|
||||
{
|
||||
s32 Index;
|
||||
};
|
||||
|
||||
struct animation_handle_array
|
||||
{
|
||||
u32 Count;
|
||||
animation_handle* Handles;
|
||||
};
|
||||
|
||||
internal animation_handle InvalidAnimHandle () { return { -1 }; }
|
||||
internal bool IsValid (animation_handle H) { return H.Index >= 0; }
|
||||
internal void Clear (animation_handle* H) { H->Index = -1; }
|
||||
internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B)
|
||||
{
|
||||
return A.Index == B.Index;
|
||||
}
|
||||
|
||||
struct animation_array
|
||||
{
|
||||
animation* Values;
|
||||
u32 Count;
|
||||
u32 CountMax;
|
||||
};
|
||||
|
||||
struct animation_layer_frame
|
||||
{
|
||||
animation_block Hot;
|
||||
bool HasHot;
|
||||
|
||||
animation_block NextHot;
|
||||
bool HasNextHot;
|
||||
|
||||
r32 NextHotOpacity;
|
||||
|
||||
blend_mode BlendMode;
|
||||
};
|
||||
|
||||
// NOTE(pjs): This is an evaluated frame - across all layers in an
|
||||
// animation, these are the blocks that need to be run
|
||||
struct animation_frame
|
||||
{
|
||||
animation_layer_frame* Layers;
|
||||
u32 LayersCount;
|
||||
};
|
||||
|
||||
enum animation_repeat_mode
|
||||
{
|
||||
AnimationRepeat_Single,
|
||||
AnimationRepeat_Loop,
|
||||
AnimationRepeat_Invalid,
|
||||
};
|
||||
|
||||
global gs_const_string AnimationRepeatModeStrings[] = {
|
||||
ConstString("Repeat Single"),
|
||||
ConstString("Loop"),
|
||||
ConstString("Invalid"),
|
||||
};
|
||||
|
||||
struct animation_fade_group
|
||||
{
|
||||
animation_handle From;
|
||||
animation_handle To;
|
||||
r32 FadeElapsed;
|
||||
r32 FadeDuration;
|
||||
};
|
||||
|
||||
#define ANIMATION_SYSTEM_LAYERS_MAX 128
|
||||
#define ANIMATION_SYSTEM_BLOCKS_MAX 128
|
||||
struct animation_system
|
||||
{
|
||||
gs_memory_arena* Storage;
|
||||
animation_array Animations;
|
||||
|
||||
animation_repeat_mode RepeatMode;
|
||||
animation_handle_array Playlist;
|
||||
u32 PlaylistAt;
|
||||
r32 PlaylistFadeTime;
|
||||
|
||||
// NOTE(Peter): The frame currently being displayed/processed. you
|
||||
// can see which frame you're on by looking at the time slider on the timeline
|
||||
// panel
|
||||
animation_fade_group ActiveFadeGroup;
|
||||
|
||||
r32 SecondsOnCurrentFrame;
|
||||
s32 CurrentFrame;
|
||||
s32 LastUpdatedFrame;
|
||||
r32 SecondsPerFrame;
|
||||
b32 TimelineShouldAdvance;
|
||||
u32 UpdatesThisFrame;
|
||||
|
||||
// Settings
|
||||
bool Multithreaded;
|
||||
};
|
||||
|
||||
// NOTE(pjs): A Pattern is a named procedure which can be used as
|
||||
// an element of an animation. Patterns are sequenced on a timeline
|
||||
// and blended via layers to create an animation
|
||||
struct animation_pattern
|
||||
{
|
||||
char* Name;
|
||||
s32 NameLength;
|
||||
animation_proc* Proc;
|
||||
bool Multithreaded;
|
||||
};
|
||||
|
||||
struct animation_pattern_array
|
||||
{
|
||||
animation_pattern* Values;
|
||||
u32 Count;
|
||||
u32 CountMax;
|
||||
};
|
||||
|
||||
// Serialization
|
||||
|
||||
enum animation_field
|
||||
{
|
||||
AnimField_FileIdent,
|
||||
AnimField_AnimName,
|
||||
AnimField_LayersCount,
|
||||
AnimField_BlocksCount,
|
||||
|
||||
AnimField_PlayableRange,
|
||||
AnimField_PlayableRangeMin,
|
||||
AnimField_PlayableRangeMax,
|
||||
|
||||
AnimField_LayersArray,
|
||||
AnimField_Layer,
|
||||
AnimField_LayerName,
|
||||
AnimField_LayerBlendMode,
|
||||
|
||||
AnimField_BlocksArray,
|
||||
AnimField_Block,
|
||||
AnimField_BlockFrameRange,
|
||||
AnimField_BlockFrameRangeMin,
|
||||
AnimField_BlockFrameRangeMax,
|
||||
AnimField_BlockLayerIndex,
|
||||
AnimField_BlockAnimName,
|
||||
|
||||
AnimField_Count,
|
||||
};
|
||||
|
||||
global gs_const_string AnimationFieldStrings[] = {
|
||||
ConstString("lumenarium_animation_file"), // AnimField_FileIdent
|
||||
ConstString("animation_name"),// AnimField_AnimName
|
||||
ConstString("layers_count"),// AnimField_LayersCount
|
||||
ConstString("blocks_count"),// AnimField_BlocksCount
|
||||
|
||||
ConstString("playable_range"),// AnimField_PlayableRange
|
||||
ConstString("min"),// AnimField_PlayableRangeMin
|
||||
ConstString("max"),// AnimField_PlayableRangeMax
|
||||
|
||||
ConstString("layers"),// AnimField_LayersArray
|
||||
ConstString("layer"),// AnimField_Layer
|
||||
ConstString("name"),// AnimField_LayerName
|
||||
ConstString("blend"),// AnimField_LayerBlendMode
|
||||
|
||||
ConstString("blocks"),// AnimField_BlocksArray
|
||||
ConstString("block"),// AnimField_Block
|
||||
ConstString("frame_range"),// AnimField_BlockFrameRange
|
||||
ConstString("min"),// AnimField_BlockFrameRangeMin
|
||||
ConstString("max"),// AnimField_BlockFrameRangeMax
|
||||
ConstString("layer_index"),// AnimField_BlockLayerIndex
|
||||
ConstString("animation_name"),// AnimField_BlockAnimName
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////
|
||||
//
|
||||
// Patterns List
|
||||
|
||||
internal animation_pattern_array
|
||||
Patterns_Create(gs_memory_arena* Arena, s32 CountMax)
|
||||
{
|
||||
animation_pattern_array Result = {0};
|
||||
Result.CountMax = CountMax;
|
||||
Result.Values = PushArray(Arena, animation_pattern, Result.CountMax);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define PATTERN_MULTITHREADED true
|
||||
#define PATTERN_SINGLETHREADED false
|
||||
|
||||
#define Patterns_PushPattern(array, proc, multithread) \
|
||||
Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1, (multithread))
|
||||
|
||||
internal void
|
||||
Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength, bool Multithreaded)
|
||||
{
|
||||
Assert(Array->Count < Array->CountMax);
|
||||
|
||||
animation_pattern Pattern = {0};
|
||||
Pattern.Name = Name;
|
||||
Pattern.NameLength = NameLength;
|
||||
Pattern.Proc = Proc;
|
||||
Pattern.Multithreaded = Multithreaded;
|
||||
|
||||
Array->Values[Array->Count++] = Pattern;
|
||||
}
|
||||
|
||||
internal animation_pattern_handle
|
||||
Patterns_IndexToHandle(s32 Index)
|
||||
{
|
||||
animation_pattern_handle Result = {};
|
||||
Result.IndexPlusOne = Index + 1;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
IsValid(animation_pattern_handle Handle)
|
||||
{
|
||||
bool Result = Handle.IndexPlusOne > 0;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal animation_pattern
|
||||
Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle Handle)
|
||||
{
|
||||
animation_pattern Result = {0};
|
||||
if (Handle.IndexPlusOne > 0)
|
||||
{
|
||||
u32 Index = Handle.IndexPlusOne - 1;
|
||||
Assert(Index < Patterns.Count);
|
||||
Result = Patterns.Values[Index];
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//
|
||||
// Anim Block Array
|
||||
|
||||
internal animation_block_array
|
||||
AnimBlockArray_Create(gs_memory_arena* Storage, u32 CountMax)
|
||||
{
|
||||
animation_block_array Result = {0};
|
||||
Result.CountMax = Max(CountMax, 32);
|
||||
Result.Values = PushArray(Storage, animation_block, Result.CountMax);
|
||||
Result.Generations = PushArray(Storage, u32, Result.CountMax);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal handle
|
||||
AnimBlockArray_Push(animation_block_array* Array, animation_block Value)
|
||||
{
|
||||
Assert(Array->Count < Array->CountMax);
|
||||
handle Result = {0};
|
||||
Result.Index = Array->Count++;
|
||||
// NOTE(pjs): pre-increment so that generation 0 is always invalid
|
||||
Result.Generation = ++Array->Generations[Result.Index];
|
||||
|
||||
Array->Values[Result.Index] = Value;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimBlockArray_Remove(animation_block_array* Array, handle Handle)
|
||||
{
|
||||
Assert(Handle.Index < Array->Count);
|
||||
Assert(Handle_IsValid(Handle));
|
||||
Array->Generations[Handle.Index]++;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimBlockArray_RemoveAt(animation_block_array* Array, u32 Index)
|
||||
{
|
||||
Assert(Index < Array->Count);
|
||||
|
||||
handle Handle = {};
|
||||
Handle.Index = Index;
|
||||
Handle.Generation = Array->Generations[Index];
|
||||
AnimBlockArray_Remove(Array, Handle);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//
|
||||
// Anim Layers Array
|
||||
|
||||
internal anim_layer_array
|
||||
AnimLayerArray_Create(gs_memory_arena* Storage, u32 CountMax)
|
||||
{
|
||||
anim_layer_array Result = {0};
|
||||
Result.CountMax = Max(CountMax, 32);
|
||||
Result.Values = PushArray(Storage, anim_layer, Result.CountMax);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
AnimLayerArray_Push(anim_layer_array* Array, anim_layer Value)
|
||||
{
|
||||
Assert(Array->Count < Array->CountMax);
|
||||
u32 Index = Array->Count++;
|
||||
Array->Values[Index] = Value;
|
||||
return Index;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimLayerArray_Remove(anim_layer_array* Array, u32 Index)
|
||||
{
|
||||
Assert(Index < Array->Count);
|
||||
for (u32 i = Index; i < Array->Count - 1; i++)
|
||||
{
|
||||
Array->Values[i] = Array->Values[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//
|
||||
// Animation Array
|
||||
|
||||
internal animation_array
|
||||
AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax)
|
||||
{
|
||||
animation_array Result = {0};
|
||||
Result.CountMax = CountMax;
|
||||
Result.Values = PushArray(Storage, animation, Result.CountMax);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal animation_handle
|
||||
AnimationArray_Push(animation_array* Array, animation Value)
|
||||
{
|
||||
Assert(Array->Count < Array->CountMax);
|
||||
animation_handle Result = {0};
|
||||
Result.Index = Array->Count++;
|
||||
Array->Values[Result.Index] = Value;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal animation*
|
||||
AnimationArray_Get(animation_array Array, animation_handle Handle)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
animation* Result = 0;
|
||||
if (IsValid(Handle) && Handle.Index < (s32)Array.Count)
|
||||
{
|
||||
Result = Array.Values + Handle.Index;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal animation*
|
||||
AnimationArray_GetSafe(animation_array Array, animation_handle Handle)
|
||||
{
|
||||
Assert(IsValid(Handle));
|
||||
Assert(Handle.Index < (s32)Array.Count);
|
||||
return AnimationArray_Get(Array, Handle);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//
|
||||
// Animation
|
||||
|
||||
typedef struct animation_desc
|
||||
{
|
||||
u32 NameSize;
|
||||
char* Name;
|
||||
|
||||
u32 LayersCount;
|
||||
u32 BlocksCount;
|
||||
|
||||
u32 MinFrames;
|
||||
u32 MaxFrames;
|
||||
} animation_desc;
|
||||
|
||||
internal animation
|
||||
Animation_Create(animation_desc Desc, animation_system* System)
|
||||
{
|
||||
animation Result = {};
|
||||
u32 NameLen = Desc.NameSize;
|
||||
if (Desc.Name)
|
||||
{
|
||||
NameLen = Max(CStringLength(Desc.Name), NameLen);
|
||||
Result.Name = PushStringF(System->Storage, NameLen, "%s", Desc.Name);
|
||||
} else {
|
||||
Result.Name = PushStringF(System->Storage, NameLen, "[New Animation]");
|
||||
}
|
||||
|
||||
Result.Layers = AnimLayerArray_Create(System->Storage, Desc.LayersCount);
|
||||
Result.Blocks_ = AnimBlockArray_Create(System->Storage, Desc.BlocksCount);
|
||||
Result.PlayableRange.Min = Desc.MinFrames;
|
||||
Result.PlayableRange.Max = Desc.MaxFrames;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal handle
|
||||
Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, animation_pattern_handle AnimationProcHandle, u32 LayerIndex)
|
||||
{
|
||||
Assert(LayerIndex < Animation->Layers.Count);
|
||||
|
||||
animation_block NewBlock = {0};
|
||||
NewBlock.Range.Min = StartFrame;
|
||||
NewBlock.Range.Max = EndFrame;
|
||||
NewBlock.AnimationProcHandle = AnimationProcHandle;
|
||||
NewBlock.Layer = LayerIndex;
|
||||
|
||||
handle Handle = AnimBlockArray_Push(&Animation->Blocks_, NewBlock);
|
||||
return Handle;
|
||||
}
|
||||
|
||||
internal void
|
||||
Animation_RemoveBlock(animation* Animation, handle AnimHandle)
|
||||
{
|
||||
AnimBlockArray_Remove(&Animation->Blocks_, AnimHandle);
|
||||
}
|
||||
|
||||
internal animation_block*
|
||||
Animation_GetBlockFromHandle(animation* Animation, handle AnimHandle)
|
||||
{
|
||||
animation_block* Result = 0;
|
||||
|
||||
if (AnimHandle.Generation != 0 &&
|
||||
Animation->Blocks_.Generations[AnimHandle.Index] == AnimHandle.Generation)
|
||||
{
|
||||
Result = Animation->Blocks_.Values + AnimHandle.Index;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
Animation_AddLayer(animation* Animation, anim_layer Layer)
|
||||
{
|
||||
return AnimLayerArray_Push(&Animation->Layers, Layer);
|
||||
}
|
||||
|
||||
internal u32
|
||||
Animation_AddLayer (animation* Animation, gs_string Name, blend_mode BlendMode, animation_system* System)
|
||||
{
|
||||
anim_layer NewLayer = {0};
|
||||
NewLayer.Name = PushStringF(System->Storage, 256, "%S", Name);
|
||||
NewLayer.BlendMode = BlendMode;
|
||||
|
||||
return Animation_AddLayer(Animation, NewLayer);
|
||||
}
|
||||
|
||||
internal void
|
||||
Animation_RemoveLayer (animation* Animation, u32 LayerIndex)
|
||||
{
|
||||
AnimLayerArray_Remove(&Animation->Layers, LayerIndex);
|
||||
for (u32 i = Animation->Blocks_.Count - 1; i >= 0; i--)
|
||||
{
|
||||
animation_block* Block = Animation->Blocks_.Values + i;
|
||||
if (Block->Layer > LayerIndex)
|
||||
{
|
||||
Block->Layer -= 1;
|
||||
}
|
||||
else if (Block->Layer == LayerIndex)
|
||||
{
|
||||
AnimBlockArray_RemoveAt(&Animation->Blocks_, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
internal u32
|
||||
SecondsToFrames(r32 Seconds, animation_system System)
|
||||
{
|
||||
u32 Result = Seconds * (1.0f / System.SecondsPerFrame);
|
||||
return Result;
|
||||
}
|
||||
|
||||
inline frame_range
|
||||
FrameRange_Overlap(frame_range A, frame_range B)
|
||||
{
|
||||
frame_range Result = {};
|
||||
|
||||
}
|
||||
|
||||
inline bool
|
||||
FrameIsInRange(frame_range Range, s32 Frame)
|
||||
{
|
||||
bool Result = (Frame >= Range.Min) && (Frame <= Range.Max);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
GetFrameCount(frame_range Range)
|
||||
{
|
||||
u32 Result = (u32)Max(0, Range.Max - Range.Min);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal r32
|
||||
FrameToPercentRange(s32 Frame, frame_range Range)
|
||||
{
|
||||
r32 Result = (r32)(Frame - Range.Min);
|
||||
Result = Result / GetFrameCount(Range);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
PercentToFrameInRange(r32 Percent, frame_range Range)
|
||||
{
|
||||
s32 Result = Range.Min + (s32)(Percent * GetFrameCount(Range));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
ClampFrameToRange(s32 Frame, frame_range Range)
|
||||
{
|
||||
s32 Result = Frame;
|
||||
if (Result < Range.Min)
|
||||
{
|
||||
Result = Range.Min;
|
||||
}
|
||||
else if (Result > Range.Max)
|
||||
{
|
||||
Result = Range.Max;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Blocks
|
||||
|
||||
// Layers
|
||||
|
||||
// Fade Group
|
||||
|
||||
internal bool
|
||||
AnimationFadeGroup_ShouldRender (animation_fade_group FadeGroup)
|
||||
{
|
||||
return IsValid(FadeGroup.From);
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationFadeGroup_Advance(animation_fade_group* Group)
|
||||
{
|
||||
Group->From = Group->To;
|
||||
Clear(&Group->To);
|
||||
Group->FadeElapsed = 0;
|
||||
Group->FadeDuration = 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime)
|
||||
{
|
||||
if (IsValid(Group->To))
|
||||
{
|
||||
r32 FadeBefore = Group->FadeElapsed;
|
||||
Group->FadeElapsed += DeltaTime;
|
||||
|
||||
if (Group->FadeElapsed >= Group->FadeDuration)
|
||||
{
|
||||
AnimationFadeGroup_Advance(Group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration)
|
||||
{
|
||||
if (IsValid(Group->From))
|
||||
{
|
||||
// complete current fade if there is one in progress
|
||||
if (IsValid(Group->To))
|
||||
{
|
||||
AnimationFadeGroup_Advance(Group);
|
||||
}
|
||||
|
||||
Group->To = To;
|
||||
Group->FadeDuration = Duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
Group->From = To;
|
||||
}
|
||||
}
|
||||
|
||||
// System
|
||||
|
||||
struct animation_system_desc
|
||||
{
|
||||
gs_memory_arena* Storage;
|
||||
u32 AnimArrayCount;
|
||||
r32 SecondsPerFrame;
|
||||
};
|
||||
|
||||
internal animation_system
|
||||
AnimationSystem_Init(animation_system_desc Desc)
|
||||
{
|
||||
animation_system Result = {};
|
||||
Result.Storage = Desc.Storage;
|
||||
Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount);
|
||||
Result.SecondsPerFrame = Desc.SecondsPerFrame;
|
||||
|
||||
Clear(&Result.ActiveFadeGroup.From);
|
||||
Clear(&Result.ActiveFadeGroup.To);
|
||||
Result.ActiveFadeGroup.FadeElapsed = 0;
|
||||
|
||||
// Settings
|
||||
Result.Multithreaded = false;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal animation*
|
||||
AnimationSystem_GetActiveAnimation(animation_system* System)
|
||||
{
|
||||
return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From);
|
||||
}
|
||||
|
||||
internal animation_frame
|
||||
AnimationSystem_CalculateAnimationFrame(animation_system* System,
|
||||
animation* Animation,
|
||||
gs_memory_arena* Arena)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
animation_frame Result = {0};
|
||||
Result.LayersCount = Animation->Layers.Count;
|
||||
Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount);
|
||||
ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount);
|
||||
|
||||
for (u32 l = 0; l < Animation->Layers.Count; l++)
|
||||
{
|
||||
animation_layer_frame* Layer = Result.Layers + l;
|
||||
Layer->BlendMode = Animation->Layers.Values[l].BlendMode;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < Animation->Blocks_.Count; i++)
|
||||
{
|
||||
animation_block Block = Animation->Blocks_.Values[i];
|
||||
|
||||
if (FrameIsInRange(Block.Range, System->CurrentFrame))
|
||||
{
|
||||
animation_layer_frame* Layer = Result.Layers + Block.Layer;
|
||||
if (Layer->HasHot)
|
||||
{
|
||||
// NOTE(pjs): With current implementation, we don't allow
|
||||
// animations to hvae more than 2 concurrent blocks in the
|
||||
// timeline
|
||||
Assert(!Layer->HasNextHot);
|
||||
|
||||
// NOTE(pjs): Make sure that Hot comes before NextHot
|
||||
if (Layer->Hot.Range.Min < Block.Range.Min)
|
||||
{
|
||||
Layer->NextHot = Block;
|
||||
}
|
||||
else
|
||||
{
|
||||
Layer->NextHot = Layer->Hot;
|
||||
Layer->Hot = Block;
|
||||
}
|
||||
Layer->HasNextHot = true;
|
||||
|
||||
frame_range BlendRange = {};
|
||||
BlendRange.Min = Layer->NextHot.Range.Min;
|
||||
BlendRange.Max = Layer->Hot.Range.Max;
|
||||
Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
Layer->Hot = Block;
|
||||
Layer->NextHotOpacity = 0.0f;
|
||||
Layer->HasHot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_Update(animation_system* System, r32 DeltaTime)
|
||||
{
|
||||
if (!System->TimelineShouldAdvance) { return; }
|
||||
if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; }
|
||||
|
||||
System->UpdatesThisFrame = 0;
|
||||
|
||||
AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime);
|
||||
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
|
||||
if (ActiveAnim)
|
||||
{
|
||||
System->SecondsOnCurrentFrame += DeltaTime;
|
||||
while (System->SecondsOnCurrentFrame > System->SecondsPerFrame)
|
||||
{
|
||||
System->CurrentFrame += 1;
|
||||
System->SecondsOnCurrentFrame -= System->SecondsPerFrame;
|
||||
System->UpdatesThisFrame += 1;
|
||||
}
|
||||
|
||||
// Loop back to the beginning
|
||||
if (System->CurrentFrame > ActiveAnim->PlayableRange.Max)
|
||||
{
|
||||
// NOTE(PS): There's no long term reason why this needs to be true
|
||||
// but I don't want to implement dealing with PlayableRanges that
|
||||
// don't start at zero right now becuse there's literally no reason
|
||||
// I can think of where that's useful.
|
||||
Assert(ActiveAnim->PlayableRange.Min == 0);
|
||||
|
||||
s32 FramesPastEnd = System->CurrentFrame;
|
||||
while (FramesPastEnd > ActiveAnim->PlayableRange.Max)
|
||||
{
|
||||
FramesPastEnd -= ActiveAnim->PlayableRange.Max;
|
||||
}
|
||||
|
||||
switch (System->RepeatMode)
|
||||
{
|
||||
case AnimationRepeat_Single:
|
||||
{
|
||||
System->CurrentFrame = 0;
|
||||
}break;
|
||||
|
||||
case AnimationRepeat_Loop:
|
||||
{
|
||||
Assert(System->Playlist.Count > 0);
|
||||
u32 NextIndex = System->PlaylistAt;
|
||||
System->PlaylistAt = (System->PlaylistAt + 1) % System->Playlist.Count;
|
||||
animation_handle Next = System->Playlist.Handles[NextIndex];
|
||||
|
||||
AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup,
|
||||
Next,
|
||||
System->PlaylistFadeTime);
|
||||
System->CurrentFrame = 0;
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_FadeToPlaylist(animation_system* System, animation_handle_array Playlist)
|
||||
{
|
||||
System->Playlist = Playlist;
|
||||
System->PlaylistAt = 0;
|
||||
|
||||
if (System->Playlist.Count > 0)
|
||||
{
|
||||
AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
AnimationSystem_NeedsRender(animation_system System)
|
||||
{
|
||||
bool Result = (System.CurrentFrame != System.LastUpdatedFrame);
|
||||
return Result;
|
||||
}
|
||||
|
||||
inline r32
|
||||
AnimationSystem_GetCurrentTime(animation_system System)
|
||||
{
|
||||
r32 Result = System.CurrentFrame * System.SecondsPerFrame;
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ANIMATION
|
||||
#endif // FOLDHAUS_ANIMATION
|
|
@ -1,375 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_animation_renderer.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-11-14
|
||||
//
|
||||
#ifndef FOLDHAUS_ANIMATION_RENDERER_CPP
|
||||
|
||||
internal pixel
|
||||
LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData)
|
||||
{
|
||||
r32 MagB = (r32)(PixelB.R + PixelB.G + PixelB.B) / (255 * 3);
|
||||
|
||||
pixel Result = {};
|
||||
Result.R = (u8)LerpR32(MagB, PixelA.R, PixelB.R);
|
||||
Result.G = (u8)LerpR32(MagB, PixelA.G, PixelB.G);
|
||||
Result.B = (u8)LerpR32(MagB, PixelA.B, PixelB.B);
|
||||
|
||||
#if 0
|
||||
pixel Result = PixelB;
|
||||
if (PixelB.R == 0 &&
|
||||
PixelB.G == 0 &&
|
||||
PixelB.B == 0)
|
||||
{
|
||||
Result = PixelA;
|
||||
}
|
||||
#endif
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Lerp(pixel PixelA, pixel PixelB, u8* UserData)
|
||||
{
|
||||
r32 BOpacity = *(r32*)UserData;
|
||||
|
||||
pixel Result = {};
|
||||
|
||||
r32 AOpacity = 1.0f - BOpacity;
|
||||
Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity));
|
||||
Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity));
|
||||
Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Add(pixel PixelA, pixel PixelB, u8* UserData)
|
||||
{
|
||||
pixel Result = {};
|
||||
|
||||
u32 R = (u32)PixelA.R + (u32)PixelB.R;
|
||||
u32 G = (u32)PixelA.G + (u32)PixelB.G;
|
||||
u32 B = (u32)PixelA.B + (u32)PixelB.B;
|
||||
|
||||
Result.R = (u8)Min(R, (u32)255);
|
||||
Result.G = (u8)Min(G, (u32)255);
|
||||
Result.B = (u8)Min(B, (u32)255);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Multiply(pixel PixelA, pixel PixelB, u8* UserData)
|
||||
{
|
||||
pixel Result = {};
|
||||
|
||||
r32 DR = (r32)PixelA.R / 255.f;
|
||||
r32 DG = (r32)PixelA.G / 255.f;
|
||||
r32 DB = (r32)PixelA.B / 255.f;
|
||||
|
||||
r32 SR = (r32)PixelB.R / 255.f;
|
||||
r32 SG = (r32)PixelB.G / 255.f;
|
||||
r32 SB = (r32)PixelB.B / 255.f;
|
||||
|
||||
Result.R = (u8)((DR * SR) * 255.f);
|
||||
Result.G = (u8)((DG * SG) * 255.f);
|
||||
Result.B = (u8)((DB * SB) * 255.f);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Overlay(pixel PixelA, pixel PixelB, u8* UserData)
|
||||
{
|
||||
pixel Result = {};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal led_blend_proc*
|
||||
LedBlend_GetProc(blend_mode BlendMode)
|
||||
{
|
||||
led_blend_proc* Result = 0;
|
||||
switch (BlendMode)
|
||||
{
|
||||
case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break;
|
||||
case BlendMode_Add: { Result = LedBlend_Add; }break;
|
||||
case BlendMode_Multiply: { Result = LedBlend_Multiply; }break;
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
struct pattern_args
|
||||
{
|
||||
assembly Assembly;
|
||||
gs_memory_arena* Transient;
|
||||
u8* UserData;
|
||||
};
|
||||
|
||||
struct render_anim_to_led_buffer_job_data
|
||||
{
|
||||
animation_pattern Pattern;
|
||||
led_buffer Buffer;
|
||||
led_buffer_range BufferRange;
|
||||
pattern_args PatternArgs;
|
||||
r32 SecondsIntoBlock;
|
||||
};
|
||||
|
||||
internal void
|
||||
AnimationSystem_RenderAnimationToLedBufferJob(gs_thread_context Context, gs_data Data)
|
||||
{
|
||||
render_anim_to_led_buffer_job_data JobData = *(render_anim_to_led_buffer_job_data*)Data.Memory;
|
||||
JobData.Pattern.Proc(&JobData.Buffer,
|
||||
JobData.BufferRange,
|
||||
JobData.PatternArgs.Assembly,
|
||||
JobData.SecondsIntoBlock,
|
||||
JobData.PatternArgs.Transient,
|
||||
JobData.PatternArgs.UserData);
|
||||
}
|
||||
|
||||
#define MULTITHREAD_PATTERN_RENDERING 1
|
||||
|
||||
internal void
|
||||
AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs,
|
||||
context Context)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min;
|
||||
r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame;
|
||||
|
||||
animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle);
|
||||
Assert(Pattern.Proc);
|
||||
|
||||
if (System->Multithreaded && Pattern.Multithreaded)
|
||||
{
|
||||
u32 JobsCount = 4;
|
||||
u32 LedsPerJob = Buffer->LedCount / JobsCount;
|
||||
|
||||
for (u32 i = 0; i < JobsCount; i++)
|
||||
{
|
||||
gs_data Data = PushSize(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data));
|
||||
render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory;
|
||||
JobData->Pattern = Pattern;
|
||||
JobData->Buffer = *Buffer;
|
||||
JobData->BufferRange.First = LedsPerJob * i;
|
||||
JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1);
|
||||
JobData->PatternArgs = PatternArgs;
|
||||
JobData->SecondsIntoBlock = SecondsIntoBlock;
|
||||
|
||||
Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue,
|
||||
(thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob,
|
||||
Data,
|
||||
ConstString("Render Pattern To Buffer"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
led_buffer_range Range = {};
|
||||
Range.First = 0;
|
||||
Range.OnePastLast = Buffer->LedCount;
|
||||
|
||||
Pattern.Proc(Buffer, Range, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_EndRenderBlockToLedBuffer (animation_system* System, context Context)
|
||||
{
|
||||
if (System->Multithreaded)
|
||||
{
|
||||
Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(pjs): This mirrors animation_layer_frame to account
|
||||
// for overlapping
|
||||
struct layer_led_buffer
|
||||
{
|
||||
led_buffer HotBuffer;
|
||||
led_buffer NextHotBuffer;
|
||||
};
|
||||
|
||||
internal led_buffer
|
||||
RenderAnimationToLedBuffer (animation_system* System,
|
||||
pattern_args PatternArgs,
|
||||
animation_frame CurrFrame,
|
||||
layer_led_buffer* LayerBuffers,
|
||||
led_buffer* AssemblyLedBuffer,
|
||||
animation_pattern_array Patterns,
|
||||
gs_memory_arena* Transient,
|
||||
context Context)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
|
||||
|
||||
// Create the LayerLEDBuffers
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
layer_led_buffer TempBuffer = {};
|
||||
|
||||
if (CurrFrame.Layers[Layer].HasHot)
|
||||
{
|
||||
TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
|
||||
|
||||
if (CurrFrame.Layers[Layer].HasNextHot)
|
||||
{
|
||||
TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
|
||||
}
|
||||
}
|
||||
|
||||
LayerBuffers[Layer] = TempBuffer;
|
||||
}
|
||||
|
||||
// Render Each layer's block to the appropriate temp buffer
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
|
||||
if (LayerFrame.HasHot)
|
||||
{
|
||||
led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer;
|
||||
animation_block Block = LayerFrame.Hot;
|
||||
AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context);
|
||||
}
|
||||
|
||||
if (LayerFrame.HasNextHot)
|
||||
{
|
||||
led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer;
|
||||
animation_block Block = LayerFrame.NextHot;
|
||||
AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context);
|
||||
}
|
||||
|
||||
AnimationSystem_EndRenderBlockToLedBuffer(System, Context);
|
||||
}
|
||||
|
||||
// Blend together any layers that have a hot and next hot buffer
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
|
||||
layer_led_buffer LayerBuffer = LayerBuffers[Layer];
|
||||
if (LayerFrame.HasNextHot)
|
||||
{
|
||||
LedBuffer_Blend(LayerBuffer.HotBuffer,
|
||||
LayerBuffer.NextHotBuffer,
|
||||
&LayerBuffer.HotBuffer,
|
||||
LedBlend_Lerp,
|
||||
(u8*)&LayerFrame.NextHotOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate Temp Buffers back into AssemblyLedBuffer
|
||||
// We do this in reverse order so that they go from top to bottom
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
if (CurrFrame.Layers[Layer].HasHot)
|
||||
{
|
||||
led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode);
|
||||
LedBuffer_Blend(AccBuffer,
|
||||
LayerBuffers[Layer].HotBuffer,
|
||||
&AccBuffer,
|
||||
Blend,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
return AccBuffer;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies,
|
||||
led_system* LedSystem,
|
||||
animation_pattern_array Patterns,
|
||||
gs_memory_arena* Transient,
|
||||
context Context,
|
||||
u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
r32 FrameTime = AnimationSystem_GetCurrentTime(*System);
|
||||
|
||||
#if 1
|
||||
animation_array Animations = System->Animations;
|
||||
animation_fade_group FadeGroup = System->ActiveFadeGroup;
|
||||
|
||||
animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From);
|
||||
animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient);
|
||||
layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount);
|
||||
|
||||
animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To);
|
||||
animation_frame ToFrame = {0};
|
||||
layer_led_buffer* ToLayerBuffers = 0;
|
||||
if (ToAnim)
|
||||
{
|
||||
ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient);
|
||||
ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount);
|
||||
}
|
||||
|
||||
for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
|
||||
{
|
||||
assembly Assembly = Assemblies.Values[AssemblyIndex];
|
||||
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
|
||||
|
||||
pattern_args PatternArgs = {};
|
||||
PatternArgs.Assembly = Assembly;
|
||||
PatternArgs.Transient = Transient;
|
||||
PatternArgs.UserData = UserData;
|
||||
|
||||
led_buffer FromBuffer = RenderAnimationToLedBuffer(System,
|
||||
PatternArgs,
|
||||
FromFrame,
|
||||
FromLayerBuffers,
|
||||
AssemblyLedBuffer,
|
||||
Patterns,
|
||||
Transient,
|
||||
Context);
|
||||
led_buffer ConsolidatedBuffer = FromBuffer;
|
||||
|
||||
if (ToAnim) {
|
||||
led_buffer ToBuffer = RenderAnimationToLedBuffer(System,
|
||||
PatternArgs,
|
||||
ToFrame,
|
||||
ToLayerBuffers,
|
||||
AssemblyLedBuffer,
|
||||
Patterns,
|
||||
Transient,
|
||||
Context);
|
||||
|
||||
r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration;
|
||||
LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent);
|
||||
}
|
||||
|
||||
LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
|
||||
animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient);
|
||||
|
||||
for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
|
||||
{
|
||||
assembly Assembly = Assemblies.Values[AssemblyIndex];
|
||||
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
|
||||
|
||||
pattern_args PatternArgs = {};
|
||||
PatternArgs.Assembly = Assembly;
|
||||
PatternArgs.Transient = Transient;
|
||||
PatternArgs.UserData = UserData;
|
||||
|
||||
led_buffer AccBuffer = RenderAnimationToLedBuffer(System,
|
||||
PatternArgs,
|
||||
CurrFrame,
|
||||
LayerBuffers,
|
||||
AssemblyLedBuffer,
|
||||
Patterns,
|
||||
Transient);
|
||||
LedBuffer_Copy(AccBuffer, AssemblyLedBuffer);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
System->LastUpdatedFrame = System->CurrentFrame;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ANIMATION_RENDERER_CPP
|
||||
#endif // FOLDHAUS_ANIMATION_RENDERER_CPP
|
|
@ -1,207 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_animation_serializer.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-04
|
||||
//
|
||||
#ifndef FOLDHAUS_ANIMATION_SERIALIZER_CPP
|
||||
|
||||
internal gs_string
|
||||
AnimSerializer_Serialize(animation Anim, animation_pattern_array Patterns, gs_memory_arena* Arena)
|
||||
{
|
||||
serializer Serializer = {0};
|
||||
Serializer.String = PushString(Arena, 4096);
|
||||
Serializer.Identifiers = AnimationFieldStrings;
|
||||
Serializer.IdentifiersCount = AnimField_Count;
|
||||
|
||||
Serializer_WriteF(&Serializer, "%S;\n", AnimationFieldStrings[AnimField_FileIdent]);
|
||||
Serializer_WriteStringValue(&Serializer, AnimField_AnimName, Anim.Name.ConstString);
|
||||
Serializer_WriteValue(&Serializer, AnimField_LayersCount, Anim.Layers.Count);
|
||||
Serializer_WriteValue(&Serializer, AnimField_BlocksCount, Anim.Blocks_.Count);
|
||||
|
||||
Serializer_OpenStruct(&Serializer, AnimField_PlayableRange);
|
||||
{
|
||||
Serializer_WriteValue(&Serializer, AnimField_PlayableRangeMin, (u32)Anim.PlayableRange.Min);
|
||||
Serializer_WriteValue(&Serializer, AnimField_PlayableRangeMax, (u32)Anim.PlayableRange.Max);
|
||||
}
|
||||
Serializer_CloseStruct(&Serializer);
|
||||
|
||||
Serializer_OpenStruct(&Serializer, AnimField_LayersArray);
|
||||
for (u32 i = 0; i < Anim.Layers.Count; i++)
|
||||
{
|
||||
anim_layer LayerAt = Anim.Layers.Values[i];
|
||||
Serializer_OpenStruct(&Serializer, AnimField_Layer);
|
||||
{
|
||||
Serializer_WriteStringValue(&Serializer, AnimField_LayerName, LayerAt.Name.ConstString);
|
||||
Serializer_WriteStringValue(&Serializer, AnimField_LayerBlendMode, BlendModeStrings[LayerAt.BlendMode].ConstString);
|
||||
}
|
||||
Serializer_CloseStruct(&Serializer);
|
||||
}
|
||||
Serializer_CloseStruct(&Serializer);
|
||||
|
||||
|
||||
Serializer_OpenStruct(&Serializer, AnimField_BlocksArray);
|
||||
for (u32 i = 0; i < Anim.Blocks_.Count; i++)
|
||||
{
|
||||
// TODO(pjs): Handle free'd animation blocks
|
||||
animation_block AnimationBlockAt = Anim.Blocks_.Values[i];
|
||||
|
||||
animation_pattern Animation = Patterns_GetPattern(Patterns, AnimationBlockAt.AnimationProcHandle);
|
||||
|
||||
Serializer_OpenStruct(&Serializer, AnimField_Block);
|
||||
{
|
||||
Serializer_OpenStruct(&Serializer, AnimField_BlockFrameRange);
|
||||
{
|
||||
Serializer_WriteValue(&Serializer, AnimField_BlockFrameRangeMin, (u32)AnimationBlockAt.Range.Min);
|
||||
Serializer_WriteValue(&Serializer, AnimField_BlockFrameRangeMax, (u32)AnimationBlockAt.Range.Max);
|
||||
}
|
||||
Serializer_CloseStruct(&Serializer);
|
||||
|
||||
Serializer_WriteValue(&Serializer, AnimField_BlockLayerIndex, AnimationBlockAt.Layer);
|
||||
Serializer_WriteStringValue(&Serializer, AnimField_BlockAnimName, ConstString(Animation.Name));
|
||||
}
|
||||
Serializer_CloseStruct(&Serializer);
|
||||
}
|
||||
Serializer_CloseStruct(&Serializer);
|
||||
|
||||
return Serializer.String;
|
||||
}
|
||||
|
||||
internal animation
|
||||
AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns)
|
||||
{
|
||||
animation Result = {0};
|
||||
|
||||
parser Parser = {0};
|
||||
Parser.String = File;
|
||||
Parser.At = Parser.String.Str;
|
||||
Parser.Identifiers = AnimationFieldStrings;
|
||||
Parser.IdentifiersCount = AnimField_Count;
|
||||
Parser.Arena = Arena;
|
||||
|
||||
if (Parser_ReadString(&Parser, AnimationFieldStrings[AnimField_FileIdent]))
|
||||
{
|
||||
Result.Name = Parser_ReadStringValue(&Parser, AnimField_AnimName);
|
||||
|
||||
u32 LayersNeeded = Parser_ReadU32Value(&Parser, AnimField_LayersCount);
|
||||
u32 BlocksNeeded = Parser_ReadU32Value(&Parser, AnimField_BlocksCount);
|
||||
Result.Layers = AnimLayerArray_Create(Arena, LayersNeeded);
|
||||
Result.Blocks_ = AnimBlockArray_Create(Arena, BlocksNeeded);
|
||||
|
||||
if (Parser_ReadOpenStruct(&Parser, AnimField_PlayableRange))
|
||||
{
|
||||
Result.PlayableRange.Min = Parser_ReadU32Value(&Parser, AnimField_PlayableRangeMin);
|
||||
Result.PlayableRange.Max = Parser_ReadU32Value(&Parser, AnimField_PlayableRangeMax);
|
||||
|
||||
if (Parser_ReadCloseStruct(&Parser))
|
||||
{
|
||||
// TODO(pjs): Error
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs): Error
|
||||
}
|
||||
|
||||
if (Parser_ReadOpenStruct(&Parser, AnimField_LayersArray))
|
||||
{
|
||||
while (!Parser_ReadCloseStruct(&Parser))
|
||||
{
|
||||
anim_layer Layer = {0};
|
||||
if (Parser_ReadOpenStruct(&Parser, AnimField_Layer))
|
||||
{
|
||||
Layer.Name = Parser_ReadStringValue(&Parser, AnimField_LayerName);
|
||||
|
||||
gs_string BlendModeName = Parser_ReadStringValue(&Parser, AnimField_LayerBlendMode);
|
||||
for (u32 i = 0; i < BlendMode_Count; i++)
|
||||
{
|
||||
if (StringsEqual(BlendModeName, BlendModeStrings[i]))
|
||||
{
|
||||
Layer.BlendMode = (blend_mode)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Parser_ReadCloseStruct(&Parser))
|
||||
{
|
||||
Animation_AddLayer(&Result, Layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs): Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Parser_ReadOpenStruct(&Parser, AnimField_BlocksArray))
|
||||
{
|
||||
while(!Parser_ReadCloseStruct(&Parser))
|
||||
{
|
||||
animation_block Block = {0};
|
||||
|
||||
if (Parser_ReadOpenStruct(&Parser, AnimField_Block))
|
||||
{
|
||||
if (Parser_ReadOpenStruct(&Parser, AnimField_BlockFrameRange))
|
||||
{
|
||||
Block.Range.Min = Parser_ReadU32Value(&Parser, AnimField_BlockFrameRangeMin);
|
||||
Block.Range.Max = Parser_ReadU32Value(&Parser, AnimField_BlockFrameRangeMax);
|
||||
|
||||
Parser_ReadCloseStruct(&Parser);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs): Error
|
||||
}
|
||||
|
||||
Block.Layer = Parser_ReadU32Value(&Parser, AnimField_BlockLayerIndex);
|
||||
|
||||
// TODO(pjs): AnimName -> Animation Proc Handle
|
||||
gs_string AnimName = Parser_ReadStringValue(&Parser, AnimField_BlockAnimName);
|
||||
Block.AnimationProcHandle = {0};
|
||||
for (u32 i = 0; i < AnimPatterns.Count; i++)
|
||||
{
|
||||
animation_pattern Pattern = AnimPatterns.Values[i];
|
||||
if (StringEqualsCharArray(AnimName.ConstString, Pattern.Name, Pattern.NameLength))
|
||||
{
|
||||
Block.AnimationProcHandle = Patterns_IndexToHandle(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert(IsValid(Block.AnimationProcHandle));
|
||||
if (Parser_ReadCloseStruct(&Parser))
|
||||
{
|
||||
AnimBlockArray_Push(&Result.Blocks_, Block);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs): Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
internal animation
|
||||
AnimParser_Parse(gs_data File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns)
|
||||
{
|
||||
gs_string FileString = MakeString((char*)File.Memory, File.Size);
|
||||
return AnimParser_Parse(FileString, Arena, AnimPatterns);
|
||||
}
|
||||
|
||||
internal animation_handle
|
||||
AnimationSystem_LoadAnimationFromFile(animation_system* System, animation_pattern_array AnimPatterns, context Context, gs_const_string FilePath)
|
||||
{
|
||||
animation_handle NewAnimHandle = InvalidAnimHandle();
|
||||
gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FilePath);
|
||||
if (AnimFile.Size > 0)
|
||||
{
|
||||
animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns);
|
||||
NewAnim.FileInfo = AnimFile.FileInfo;
|
||||
NewAnim.FileInfo.Path = PushStringF(System->Storage, AnimFile.FileInfo.Path.Length, "%S", AnimFile.FileInfo.Path).ConstString;
|
||||
NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim);
|
||||
}
|
||||
return NewAnimHandle;
|
||||
}
|
||||
#define FOLDHAUS_ANIMATION_SERIALIZER_CPP
|
||||
#endif // FOLDHAUS_ANIMATION_SERIALIZER_CPP
|
|
@ -1,10 +0,0 @@
|
|||
//
|
||||
// File: artnet.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
/* For future artnet implementation */
|
||||
#ifndef ARTNET_H
|
||||
|
||||
#define ARTNET_H
|
||||
#endif // ARTNET_H
|
|
@ -1,283 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_assembly.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_ASSEMBLY_CPP
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
// Assembly Array
|
||||
//
|
||||
///////////////////////////
|
||||
|
||||
internal assembly_array
|
||||
AssemblyArray_Create(u32 CountMax, gs_memory_arena* Storage)
|
||||
{
|
||||
assembly_array Result = {0};
|
||||
Result.CountMax = CountMax;
|
||||
Result.Values = PushArray(Storage, assembly, Result.CountMax);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
AssemblyArray_Push(assembly_array* Array, assembly Assembly)
|
||||
{
|
||||
Assert(Array->Count < Array->CountMax);
|
||||
u32 Index = Array->Count++;
|
||||
Array->Values[Index] = Assembly;
|
||||
Array->Values[Index].AssemblyIndex = Index;
|
||||
return Index;
|
||||
}
|
||||
|
||||
internal assembly*
|
||||
AssemblyArray_Take(assembly_array* Array)
|
||||
{
|
||||
u32 Index = AssemblyArray_Push(Array, {});
|
||||
assembly* Result = Array->Values + Index;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyArray_RemoveAt(assembly_array* Array, u32 Index)
|
||||
{
|
||||
u32 LastAssemblyIndex = --Array->Count;
|
||||
Array->Values[Index] = Array->Values[LastAssemblyIndex];
|
||||
}
|
||||
|
||||
typedef bool assembly_array_filter_proc(assembly A);
|
||||
bool AssemblyFilter_OutputsViaSACN(assembly A) { return A.OutputMode == NetworkProtocol_SACN; }
|
||||
bool AssemblyFilter_OutputsViaUART(assembly A) { return A.OutputMode == NetworkProtocol_UART; }
|
||||
|
||||
internal assembly_array
|
||||
AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, gs_memory_arena* Storage)
|
||||
{
|
||||
assembly_array Result = AssemblyArray_Create(Array.Count, Storage);
|
||||
|
||||
for (u32 i = 0; i < Array.Count; i++)
|
||||
{
|
||||
assembly At = Array.Values[i];
|
||||
if (Filter(At))
|
||||
{
|
||||
AssemblyArray_Push(&Result, At);
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
// LedSystem
|
||||
//
|
||||
///////////////////////////
|
||||
|
||||
internal led_system
|
||||
LedSystem_Create(gs_allocator PlatformMemory, u32 BuffersMax)
|
||||
{
|
||||
led_system Result = {};
|
||||
Result.PlatformMemory = PlatformMemory;
|
||||
// TODO(Peter): Since we have access to PlatformMemory, just realloc Buffers when we fill it up
|
||||
Result.BuffersCountMax = BuffersMax;
|
||||
Result.Buffers = AllocArray(PlatformMemory, led_buffer, Result.BuffersCountMax, "led system");
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
LedSystemTakeFreeBuffer(led_system* System, u32 LedCount)
|
||||
{
|
||||
s32 Result = -1;
|
||||
|
||||
if (System->BuffersCount < System->BuffersCountMax)
|
||||
{
|
||||
Result = System->BuffersCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(Peter): Look for a buffer that's flagged as empty
|
||||
for (u32 i = 0; i < System->BuffersCount; i++)
|
||||
{
|
||||
if (System->Buffers[i].LedCount == 0
|
||||
&& System->Buffers[i].Colors == 0
|
||||
&& System->Buffers[i].Positions == 0)
|
||||
{
|
||||
Result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert(Result >= 0); // NOTE(Peter): We ran out of room for led buffers
|
||||
}
|
||||
|
||||
led_buffer* Buffer = &System->Buffers[Result];
|
||||
Buffer->A = MemoryArenaCreate(KB(16),Bytes(8),System->PlatformMemory,0,0,"Led Buffer Arena");
|
||||
Buffer->LedCount = LedCount;
|
||||
Buffer->Colors = PushArray(&Buffer->A, pixel, Buffer->LedCount);
|
||||
Buffer->Positions = PushArray(&Buffer->A, v4, Buffer->LedCount);
|
||||
|
||||
System->LedsCountTotal += LedCount;
|
||||
|
||||
return (u32)Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
LedSystemFreeBuffer(led_system* System, u32 BufferIndex)
|
||||
{
|
||||
Assert(BufferIndex < System->BuffersCountMax);
|
||||
led_buffer* Buffer = &System->Buffers[BufferIndex];
|
||||
MemoryArenaFree(&Buffer->A);
|
||||
System->LedsCountTotal -= Buffer->LedCount;
|
||||
*Buffer = {};
|
||||
}
|
||||
|
||||
internal void
|
||||
LedBufferSetLed(led_buffer* Buffer, u32 Led, v4 Position)
|
||||
{
|
||||
Assert(Led < Buffer->LedCount);
|
||||
Buffer->Positions[Led] = Position;
|
||||
}
|
||||
|
||||
internal u32
|
||||
Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex, u32 LedLUTStartIndex)
|
||||
{
|
||||
u32 LedsAdded = 0;
|
||||
|
||||
switch (GenData.Method)
|
||||
{
|
||||
case StripGeneration_InterpolatePoints:
|
||||
{
|
||||
strip_gen_interpolate_points InterpPoints = GenData.InterpolatePoints;
|
||||
v4 WS_StripStart = RootPosition + ToV4Point(InterpPoints.StartPosition * Assembly->Scale);
|
||||
v4 WS_StripEnd = RootPosition + ToV4Point(InterpPoints.EndPosition * Assembly->Scale);
|
||||
|
||||
v4 SingleStep = (WS_StripEnd - WS_StripStart) / (r32)InterpPoints.LedCount;
|
||||
for (u32 Step = 0; Step < InterpPoints.LedCount; Step++)
|
||||
{
|
||||
s32 LedIndex = LedStartIndex + LedsAdded++;
|
||||
v4 LedPosition = WS_StripStart + (SingleStep * Step);
|
||||
LedBufferSetLed(LedBuffer, LedIndex, LedPosition);
|
||||
StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex;
|
||||
}
|
||||
}break;
|
||||
|
||||
case StripGeneration_Sequence:
|
||||
{
|
||||
strip_gen_sequence Sequence = GenData.Sequence;
|
||||
for (u32 i = 0; i < Sequence.ElementsCount; i++)
|
||||
{
|
||||
strip_gen_data SegmentGenData = Sequence.Elements[i];
|
||||
LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded);
|
||||
}
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
return LedsAdded;
|
||||
}
|
||||
|
||||
internal void
|
||||
ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem)
|
||||
{
|
||||
Assembly->LedBufferIndex = LedSystemTakeFreeBuffer(LedSystem, Assembly->LedCountTotal);
|
||||
led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex);
|
||||
|
||||
v4 RootPosition = ToV4Vec(Assembly->Center);
|
||||
|
||||
// Add Leds
|
||||
u32 LedsAdded = 0;
|
||||
for (u32 StripIdx = 0; StripIdx < Assembly->StripCount; StripIdx++)
|
||||
{
|
||||
v2_strip* StripAt = &Assembly->Strips[StripIdx];
|
||||
StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount);
|
||||
|
||||
strip_gen_data GenData = StripAt->GenerationData;
|
||||
LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal assembly*
|
||||
LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, log_buffer* GlobalLog)
|
||||
{
|
||||
assembly* NewAssembly = 0;
|
||||
|
||||
gs_file AssemblyFile = ReadEntireFile(Context.ThreadContext.FileHandler, Path);
|
||||
if (FileNoError(AssemblyFile))
|
||||
{
|
||||
gs_string AssemblyFileText = MakeString((char*)AssemblyFile.Memory);
|
||||
|
||||
s32 IndexOfLastSlash = FindLast(Path, '\\');
|
||||
gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length);
|
||||
|
||||
NewAssembly = AssemblyArray_Take(Assemblies);
|
||||
NewAssembly->Arena = MemoryArenaCreate(MB(4), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "Assembly Arena");
|
||||
|
||||
parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch);
|
||||
if (AssemblyParser.Success)
|
||||
{
|
||||
ConstructAssemblyFromDefinition(NewAssembly, LedSystem);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryArenaFree(&NewAssembly->Arena);
|
||||
Assemblies->Count -= 1;
|
||||
}
|
||||
|
||||
for (parser_error* ErrorAt = AssemblyParser.ErrorsRoot;
|
||||
ErrorAt != 0;
|
||||
ErrorAt = ErrorAt->Next)
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, ErrorAt->Message.Str);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_Error(GlobalLog, "Unable to load assembly file");
|
||||
}
|
||||
|
||||
return NewAssembly;
|
||||
}
|
||||
|
||||
internal void
|
||||
UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context)
|
||||
{
|
||||
Assert(AssemblyIndex < State->Assemblies.Count);
|
||||
assembly* Assembly = &State->Assemblies.Values[AssemblyIndex];
|
||||
LedSystemFreeBuffer(&State->LedSystem, Assembly->LedBufferIndex);
|
||||
MemoryArenaFree(&Assembly->Arena);
|
||||
AssemblyArray_RemoveAt(&State->Assemblies, AssemblyIndex);
|
||||
}
|
||||
|
||||
// Querying Assemblies
|
||||
|
||||
internal led_strip_list
|
||||
AssemblyStripsGetWithTagValue(assembly Assembly, gs_const_string TagName, gs_const_string TagValue, gs_memory_arena* Storage)
|
||||
{
|
||||
led_strip_list Result = {0};
|
||||
// TODO(pjs): @Optimization
|
||||
// We can probably come back here and do this allocation procedurally, or in buckets, or with
|
||||
// a linked list. But for now, I just want to get this up and running
|
||||
Result.CountMax = Assembly.StripCount;
|
||||
Result.StripIndices = PushArray(Storage, u32, Result.CountMax);
|
||||
|
||||
u64 NameHash = HashDJB2ToU32(StringExpand(TagName));
|
||||
u64 ValueHash = 0;
|
||||
if (TagValue.Length > 0)
|
||||
{
|
||||
ValueHash = HashDJB2ToU32(StringExpand(TagValue));
|
||||
}
|
||||
|
||||
for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
|
||||
{
|
||||
v2_strip StripAt = Assembly.Strips[StripIndex];
|
||||
if (AssemblyStrip_HasTagValue(StripAt, NameHash, ValueHash))
|
||||
{
|
||||
Result.StripIndices[Result.Count++] = StripIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ASSEMBLY_CPP
|
||||
#endif // FOLDHAUS_ASSEMBLY_CPP
|
|
@ -1,298 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_assembly.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_ASSEMBLY_H
|
||||
|
||||
enum network_protocol
|
||||
{
|
||||
NetworkProtocol_SACN,
|
||||
NetworkProtocol_ArtNet,
|
||||
NetworkProtocol_UART,
|
||||
|
||||
NetworkProtocol_Count,
|
||||
};
|
||||
|
||||
union pixel
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 R;
|
||||
u8 G;
|
||||
u8 B;
|
||||
};
|
||||
u8 Channels[3];
|
||||
};
|
||||
|
||||
struct led_buffer
|
||||
{
|
||||
// NOTE(PS): This is just a tracking structure,
|
||||
// that enables allocations for a particular buffer
|
||||
// to occur all in contiguous memory
|
||||
// and should not be pushed to after the initial
|
||||
// allocation
|
||||
gs_memory_arena A;
|
||||
|
||||
u32 LedCount;
|
||||
pixel* Colors;
|
||||
v4* Positions;
|
||||
};
|
||||
|
||||
struct led_buffer_range
|
||||
{
|
||||
u32 First;
|
||||
u32 OnePastLast;
|
||||
};
|
||||
|
||||
struct led_system
|
||||
{
|
||||
gs_allocator PlatformMemory;
|
||||
|
||||
u32 BuffersCountMax;
|
||||
u32 BuffersCount;
|
||||
led_buffer* Buffers;
|
||||
|
||||
u32 LedsCountTotal;
|
||||
};
|
||||
|
||||
struct v2_tag
|
||||
{
|
||||
u64 NameHash;
|
||||
u64 ValueHash;
|
||||
};
|
||||
|
||||
struct strip_sacn_addr
|
||||
{
|
||||
s32 StartUniverse;
|
||||
s32 StartChannel;
|
||||
};
|
||||
|
||||
struct strip_uart_addr
|
||||
{
|
||||
u8 Channel;
|
||||
|
||||
gs_string ComPort;
|
||||
// This may not be used based on the value of the parent
|
||||
// assembly's NetworkPortMode field
|
||||
};
|
||||
|
||||
enum strip_gen_method
|
||||
{
|
||||
StripGeneration_InterpolatePoints,
|
||||
StripGeneration_Sequence,
|
||||
|
||||
StripGeneration_Count,
|
||||
};
|
||||
|
||||
typedef struct strip_gen_data strip_gen_data;
|
||||
|
||||
struct strip_gen_interpolate_points
|
||||
{
|
||||
v3 StartPosition;
|
||||
v3 EndPosition;
|
||||
u32 LedCount;
|
||||
};
|
||||
|
||||
struct strip_gen_sequence
|
||||
{
|
||||
strip_gen_data* Elements;
|
||||
u32 ElementsCount;
|
||||
};
|
||||
|
||||
struct strip_gen_data
|
||||
{
|
||||
strip_gen_method Method;
|
||||
|
||||
strip_gen_interpolate_points InterpolatePoints;
|
||||
strip_gen_sequence Sequence;
|
||||
};
|
||||
|
||||
struct v2_strip
|
||||
{
|
||||
s32 ControlBoxID; // TODO(Peter): I don't think we need this anymore
|
||||
|
||||
strip_sacn_addr SACNAddr;
|
||||
strip_uart_addr UARTAddr;
|
||||
|
||||
strip_gen_data GenerationData;
|
||||
|
||||
u32 LedCount;
|
||||
u32* LedLUT;
|
||||
|
||||
u32 TagsCount;
|
||||
v2_tag* Tags;
|
||||
};
|
||||
|
||||
struct led_strip_list
|
||||
{
|
||||
u32 Count;
|
||||
u32 CountMax;
|
||||
u32* StripIndices;
|
||||
};
|
||||
|
||||
enum network_port_mode
|
||||
{
|
||||
// This enum defines the scope which contains what network
|
||||
// port each address should be sent over.
|
||||
|
||||
NetworkPortMode_GlobalPort,
|
||||
// GlobalPort means that the port is defined in the assembly structure
|
||||
|
||||
NetworkPortMode_PortPerStrip,
|
||||
// PortPerStrip means that the address stored in the strip structure
|
||||
// should be used, and each strip might have a different port
|
||||
|
||||
NetworkPortMode_Count,
|
||||
};
|
||||
|
||||
struct assembly
|
||||
{
|
||||
gs_memory_arena Arena;
|
||||
|
||||
u32 AssemblyIndex;
|
||||
gs_string Name;
|
||||
gs_string FilePath;
|
||||
|
||||
r32 Scale;
|
||||
v3 Center;
|
||||
v3 MinLedPos, MaxLedPos;
|
||||
s32 LedCountTotal;
|
||||
u32 LedBufferIndex;
|
||||
|
||||
u32 StripCount;
|
||||
v2_strip* Strips;
|
||||
|
||||
network_protocol OutputMode;
|
||||
network_port_mode NetPortMode;
|
||||
gs_string UARTComPort;
|
||||
};
|
||||
|
||||
struct assembly_array
|
||||
{
|
||||
u32 CountMax;
|
||||
u32 Count;
|
||||
assembly* Values;
|
||||
};
|
||||
|
||||
typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData);
|
||||
|
||||
internal led_buffer*
|
||||
LedSystemGetBuffer(led_system* System, u32 Index)
|
||||
{
|
||||
led_buffer* Result = &System->Buffers[Index];
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
LedBuffer_ClearToBlack(led_buffer* Buffer)
|
||||
{
|
||||
for (u32 i = 0; i < Buffer->LedCount; i++)
|
||||
{
|
||||
Buffer->Colors[i].R = 0;
|
||||
Buffer->Colors[i].G = 0;
|
||||
Buffer->Colors[i].B = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
LedBuffer_Copy(led_buffer From, led_buffer* To)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
Assert(From.LedCount == To->LedCount);
|
||||
u32 LedCount = To->LedCount;
|
||||
for (u32 i = 0; i < LedCount; i++)
|
||||
{
|
||||
To->Colors[i] = From.Colors[i];
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
Assert(A.LedCount == B.LedCount);
|
||||
Assert(Dest->LedCount == A.LedCount);
|
||||
Assert(BlendProc);
|
||||
|
||||
u32 LedCount = Dest->LedCount;
|
||||
for (u32 i = 0; i < LedCount; i++)
|
||||
{
|
||||
pixel PA = A.Colors[i];
|
||||
pixel PB = B.Colors[i];
|
||||
Dest->Colors[i] = BlendProc(PA, PB, UserData);
|
||||
}
|
||||
}
|
||||
|
||||
internal led_buffer
|
||||
LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
led_buffer Result = {};
|
||||
Result.LedCount = Buffer.LedCount;
|
||||
Result.Positions = Buffer.Positions;
|
||||
Result.Colors = PushArray(Arena, pixel, Buffer.LedCount);
|
||||
LedBuffer_ClearToBlack(&Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
StripGenData_CountLeds(strip_gen_data Data)
|
||||
{
|
||||
u32 Result = 0;
|
||||
|
||||
switch (Data.Method)
|
||||
{
|
||||
case StripGeneration_InterpolatePoints:
|
||||
{
|
||||
Result += Data.InterpolatePoints.LedCount;
|
||||
}break;
|
||||
|
||||
case StripGeneration_Sequence:
|
||||
{
|
||||
for (u32 i = 0; i < Data.Sequence.ElementsCount; i++)
|
||||
{
|
||||
Result += StripGenData_CountLeds(Data.Sequence.Elements[i]);
|
||||
}
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
AssemblyStrip_HasTagValue(v2_strip Strip, u64 NameHash, u64 ValueHash)
|
||||
{
|
||||
bool Result = false;
|
||||
for (u32 i = 0; i < Strip.TagsCount; i++)
|
||||
{
|
||||
v2_tag TagAt = Strip.Tags[i];
|
||||
if (TagAt.NameHash == NameHash)
|
||||
{
|
||||
// NOTE(pjs): We can pass an empty string to the Value parameter,
|
||||
// and it will match all values of Tag
|
||||
if (ValueHash == 0 || ValueHash == TagAt.ValueHash)
|
||||
{
|
||||
Result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
AssemblyStrip_HasTagValueSLOW(v2_strip Strip, char* Name, char* Value)
|
||||
{
|
||||
u64 NameHash = HashDJB2ToU32(Name);
|
||||
u64 ValueHash = HashDJB2ToU32(Value);
|
||||
return AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ASSEMBLY_H
|
||||
#endif // FOLDHAUS_ASSEMBLY_H
|
|
@ -1,238 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_assembly_debug.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-15
|
||||
//
|
||||
#ifndef FOLDHAUS_ASSEMBLY_DEBUG_H
|
||||
|
||||
enum override_type
|
||||
{
|
||||
ADS_Override_None,
|
||||
|
||||
ADS_Override_Strip,
|
||||
ADS_Override_SoloStrip,
|
||||
ADS_Override_AllRed,
|
||||
ADS_Override_AllGreen,
|
||||
ADS_Override_AllBlue,
|
||||
ADS_Override_AllOff,
|
||||
ADS_Override_AllWhite,
|
||||
ADS_Override_AllHue,
|
||||
ADS_Override_TagWhite,
|
||||
ADS_Override_TagStripWhite,
|
||||
ADS_Override_ChannelWhite,
|
||||
|
||||
ADS_Override_Count,
|
||||
};
|
||||
|
||||
global gs_const_string OverrideTypeStrings[] = {
|
||||
LitString("Override_None"),
|
||||
LitString("Override_Strip"),
|
||||
LitString("Override_SoloStrip" ),
|
||||
LitString("Override_AllRed" ),
|
||||
LitString("Override_AllGreen" ),
|
||||
LitString("Override_AllBlue" ),
|
||||
LitString("ADS_Override_AllOff" ),
|
||||
LitString("ADS_Override_AllWhite" ),
|
||||
LitString("ADS_Override_AllHue" ),
|
||||
LitString("ADS_Override_TagWhite" ),
|
||||
LitString("ADS_Override_TagStripWhite" ),
|
||||
LitString("ADS_Override_ChannelWhite," ),
|
||||
LitString("Override_Count"),
|
||||
};
|
||||
|
||||
struct assembly_debug_state
|
||||
{
|
||||
override_type Override;
|
||||
|
||||
bool AllAssemblies;
|
||||
u32 TargetAssembly;
|
||||
u32 TargetStrip;
|
||||
|
||||
gs_string TagName;
|
||||
gs_string TagValue;
|
||||
|
||||
u32 TargetHue;
|
||||
pixel TargetColor;
|
||||
|
||||
u32 TargetChannel;
|
||||
|
||||
u8 Brightness;
|
||||
};
|
||||
|
||||
internal assembly_debug_state
|
||||
AssemblyDebug_Create(gs_memory_arena* Storage)
|
||||
{
|
||||
assembly_debug_state Result = {};
|
||||
Result.TagName = PushString(Storage, 256);
|
||||
Result.TagValue = PushString(Storage, 256);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyDebug_OverrideStripWithColor(v2_strip Strip, led_buffer LedBuffer, pixel Color)
|
||||
{
|
||||
for (u32 i = 0; i < Strip.LedCount; i++)
|
||||
{
|
||||
u32 LedIdx = Strip.LedLUT[i];
|
||||
LedBuffer.Colors[LedIdx] = Color;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyDebug_OverrideWithColor(assembly Assembly, led_buffer LedBuffer, pixel Color)
|
||||
{
|
||||
for (u32 s = 0; s < Assembly.StripCount; s++)
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[s];
|
||||
AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, Color);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyDebug_OverrideTagValueWithColor(assembly Assembly, led_buffer LedBuffer, pixel Color, gs_string TagName, gs_string TagValue)
|
||||
{
|
||||
u64 NameHash = HashDJB2ToU32(StringExpand(TagName));
|
||||
u64 ValueHash = HashDJB2ToU32(StringExpand(TagValue));
|
||||
|
||||
for (u32 s = 0; s < Assembly.StripCount; s++)
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[s];
|
||||
if (AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash))
|
||||
{
|
||||
AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyDebug_OverrideOutputForAssembly(assembly_debug_state State, led_system LedSystem,
|
||||
assembly Assembly)
|
||||
{
|
||||
led_buffer LedBuffer = LedSystem.Buffers[Assembly.LedBufferIndex];
|
||||
|
||||
u8 V = State.Brightness;
|
||||
|
||||
switch (State.Override)
|
||||
{
|
||||
case ADS_Override_Strip:
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[State.TargetStrip];
|
||||
AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, State.TargetColor);
|
||||
}break;
|
||||
|
||||
case ADS_Override_SoloStrip:
|
||||
{
|
||||
for (u32 s = 0; s < Assembly.StripCount; s++)
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[s];
|
||||
|
||||
pixel Color = pixel{0,0,0};
|
||||
if (s == State.TargetStrip)
|
||||
{
|
||||
Color = State.TargetColor;
|
||||
}
|
||||
|
||||
AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, Color);
|
||||
}
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllRed:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{V, 0, 0});
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllGreen:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, V, 0});
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllBlue:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, V});
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllOff:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllWhite:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{V, V, V});
|
||||
}break;
|
||||
|
||||
case ADS_Override_AllHue:
|
||||
{
|
||||
v4 HSV = v4{(r32)State.TargetHue, 1, 1, 1};
|
||||
v4 RGB = HSVToRGB(HSV);
|
||||
pixel P = V4ToRGBPixel(RGB);
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, P);
|
||||
}break;
|
||||
|
||||
case ADS_Override_TagWhite:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
|
||||
AssemblyDebug_OverrideTagValueWithColor(Assembly, LedBuffer, pixel{255, 255, 255}, State.TagName, State.TagValue);
|
||||
|
||||
}break;
|
||||
|
||||
case ADS_Override_TagStripWhite:
|
||||
{
|
||||
u64 NameHash = HashDJB2ToU32(StringExpand(State.TagName));
|
||||
u64 ValueHash = HashDJB2ToU32(StringExpand(State.TagValue));
|
||||
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
|
||||
|
||||
v2_strip Strip = Assembly.Strips[State.TargetStrip];
|
||||
if (AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash))
|
||||
{
|
||||
AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer,
|
||||
pixel{255, 255, 255});
|
||||
}
|
||||
}break;
|
||||
|
||||
case ADS_Override_ChannelWhite:
|
||||
{
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
|
||||
for (u32 s = 0; s < Assembly.StripCount; s++)
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[s];
|
||||
if (Strip.UARTAddr.Channel == State.TargetChannel)
|
||||
{
|
||||
AssemblyDebug_OverrideStripWithColor(Strip, LedBuffer, pixel{255, 255, 255});
|
||||
}
|
||||
}
|
||||
}break;
|
||||
|
||||
case ADS_Override_None:
|
||||
{
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assemblies, led_system LedSystem)
|
||||
{
|
||||
if (State.Override == ADS_Override_None) return;
|
||||
State.TargetColor = pixel{255,255,255};
|
||||
|
||||
if (State.AllAssemblies)
|
||||
{
|
||||
for (u32 i = 0; i < Assemblies.Count; i++)
|
||||
{
|
||||
assembly Assembly = Assemblies.Values[i];
|
||||
AssemblyDebug_OverrideOutputForAssembly(State, LedSystem, Assembly);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assembly Assembly = Assemblies.Values[State.TargetAssembly];
|
||||
AssemblyDebug_OverrideOutputForAssembly(State, LedSystem, Assembly);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_ASSEMBLY_DEBUG_H
|
||||
#endif // FOLDHAUS_ASSEMBLY_DEBUG_H
|
|
@ -1,336 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_assembly_parser.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_ASSEMBLY_PARSER_CPP
|
||||
|
||||
// TODO(pjs): This is good for meta generation
|
||||
// ie. It would be great to have
|
||||
// // enum ident enum prefix
|
||||
// BEGIN_GEN_ENUM(assembly_field, AssemblyField_)
|
||||
// // value name gen string of the value name the paired string identifier
|
||||
// ADD_ENUM_VALUE(AssemblyName, DO_GEN_STRING, "assembly_name")
|
||||
// ADD_ENUM_VALUE(AssemblyScale, DO_GEN_STRING, "assembly_scale")
|
||||
// END_GEN_ENUM(assembly_field)
|
||||
|
||||
enum assembly_field
|
||||
{
|
||||
AssemblyField_AssemblyName,
|
||||
AssemblyField_AssemblyScale,
|
||||
AssemblyField_AssemblyCenter,
|
||||
AssemblyField_LedStripCount,
|
||||
AssemblyField_OutputMode,
|
||||
|
||||
AssemblyField_LedStrip,
|
||||
|
||||
AssemblyField_OutputSACN,
|
||||
AssemblyField_SACN_StartUniverse,
|
||||
AssemblyField_SACN_StartChannel,
|
||||
|
||||
AssemblyField_OutputUART,
|
||||
AssemblyField_UART_Channel,
|
||||
AssemblyField_UART_ComPort,
|
||||
|
||||
AssemblyField_PointPlacementType,
|
||||
|
||||
AssemblyField_InterpolatePoints,
|
||||
AssemblyField_Start,
|
||||
AssemblyField_End,
|
||||
AssemblyField_LedCount,
|
||||
|
||||
AssemblyField_SegmentSequence,
|
||||
AssemblyField_SegmentSequenceLength,
|
||||
AssemblyField_Segment,
|
||||
|
||||
AssemblyField_TagsCount,
|
||||
AssemblyField_Tag,
|
||||
AssemblyField_Name,
|
||||
AssemblyField_Value,
|
||||
|
||||
AssemblyField_Count,
|
||||
};
|
||||
|
||||
global gs_const_string AssemblyFieldIdentifiers[] = {
|
||||
ConstString("assembly_name"), // AssemblyField_AssemblyName
|
||||
ConstString("assembly_scale"), // AssemblyField_AssemblyScale
|
||||
ConstString("assembly_center"), // AssemblyField_AssemblyCenter
|
||||
ConstString("led_strip_count"), // AssemblyField_LedStripCount
|
||||
ConstString("output_mode"), // AssemblyField_OutputMode
|
||||
|
||||
ConstString("led_strip"), // AssemblyField_LedStrip
|
||||
|
||||
ConstString("output_sacn"), // AssemblyField_OutputSACN
|
||||
ConstString("start_universe"), // AssemblyField_SACN_StartUniverse
|
||||
ConstString("start_channel"), // AssemblyField_SACN_StartChannel
|
||||
|
||||
ConstString("output_uart"), // AssemblyField_OutputUART
|
||||
ConstString("channel"), // AssemblyField_UART_Channel
|
||||
ConstString("com_port"), // AssemblyField_UART_ComPort
|
||||
|
||||
ConstString("point_placement_type"), // AssemblyField_PointPlacementType
|
||||
|
||||
ConstString("interpolate_points"), // AssemblyField_InterpolatePoints
|
||||
ConstString("start"), // AssemblyField_Start
|
||||
ConstString("end"), // AssemblyField_End
|
||||
ConstString("led_count"), // AssemblyField_LedCount
|
||||
|
||||
ConstString("segment_sequence"), // AssemblyField_SegmentSequence
|
||||
ConstString("segment_count"), // AssemblyField_SegmentSequenceLength
|
||||
ConstString("segment"), // AssemblyField_Segment
|
||||
|
||||
ConstString("tags_count"), // AssemblyField_TagCount
|
||||
ConstString("tag"), // AssemblyField_Tag
|
||||
ConstString("name"), // AssemblyField_Name
|
||||
ConstString("value"), // AssemblyField_Value
|
||||
};
|
||||
|
||||
internal void
|
||||
StripSetTag(v2_strip* Strip, u32 TagIndex, gs_const_string TagName, gs_const_string TagValue)
|
||||
{
|
||||
Assert(TagIndex < Strip->TagsCount);
|
||||
v2_tag* TagAt = &Strip->Tags[TagIndex];
|
||||
TagAt->NameHash = HashDJB2ToU32(StringExpand(TagName));
|
||||
TagAt->ValueHash = HashDJB2ToU32(StringExpand(TagValue));
|
||||
}
|
||||
|
||||
internal strip_sacn_addr
|
||||
AssemblyParser_ReadSACNAddr(parser* Parser, assembly Assembly)
|
||||
{
|
||||
strip_sacn_addr Result = {0};
|
||||
|
||||
if (Parser_ReadOpenStruct(Parser, AssemblyField_OutputSACN))
|
||||
{
|
||||
Result.StartUniverse = Parser_ReadU32Value(Parser, AssemblyField_SACN_StartUniverse);
|
||||
Result.StartChannel = Parser_ReadU32Value(Parser, AssemblyField_SACN_StartChannel);
|
||||
|
||||
if (!Parser_ReadCloseStruct(Parser))
|
||||
{
|
||||
//TokenizerPushError(&Tokenizer, "Struct doesn't close where expected");
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal strip_uart_addr
|
||||
AssemblyParser_ReadUARTAddr(parser* Parser, assembly Assembly)
|
||||
{
|
||||
strip_uart_addr Result = {0};
|
||||
|
||||
if (Parser_ReadOpenStruct(Parser, AssemblyField_OutputUART))
|
||||
{
|
||||
Result.Channel = (u8)Parser_ReadU32Value(Parser, AssemblyField_UART_Channel);
|
||||
|
||||
bool HasNetPort = Parser_ReadStringValue(Parser, AssemblyField_UART_ComPort, &Result.ComPort, true);
|
||||
if (Assembly.NetPortMode == NetworkPortMode_PortPerStrip && !HasNetPort)
|
||||
{
|
||||
Parser_PushErrorF(Parser, "NetPortMode for assembly is PortPerStrip, but this strip doesn't have an output port.");
|
||||
}
|
||||
|
||||
if (!Parser_ReadCloseStruct(Parser))
|
||||
{
|
||||
Parser_PushErrorF(Parser, "Struct doesn't close where expected");
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AssemblyParser_ReadTag(parser* Parser, v2_strip* StripAt, u32 TagIndex)
|
||||
{
|
||||
if (Parser_ReadOpenStruct(Parser, AssemblyField_Tag))
|
||||
{
|
||||
// TODO(Peter): Need to store the gs_string somewhere we can look it up for display in the interface
|
||||
// right now they are stored in temp memory and won't persist
|
||||
gs_string TagName = Parser_ReadStringValue(Parser, AssemblyField_Name);
|
||||
gs_string TagValue = Parser_ReadStringValue(Parser, AssemblyField_Value);
|
||||
StripSetTag(StripAt, TagIndex, TagName.ConstString, TagValue.ConstString);
|
||||
if (!Parser_ReadCloseStruct(Parser))
|
||||
{
|
||||
Parser_PushErrorF(Parser, "Tag struct doesn't close where expected");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(Parser, "Expected a tag struct, but none was found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void
|
||||
AssemblyParser_ReadTagList(parser* Parser, v2_strip* StripAt, assembly* Assembly)
|
||||
{
|
||||
StripAt->TagsCount = Parser_ReadU32Value(Parser, AssemblyField_TagsCount);
|
||||
// NOTE(pjs): Always add one tag to the input to leave room for the assembly name
|
||||
StripAt->TagsCount += 1;
|
||||
StripAt->Tags = PushArray(&Assembly->Arena, v2_tag, StripAt->TagsCount);
|
||||
|
||||
StripSetTag(StripAt, 0, ConstString("assembly"), Assembly->Name.ConstString);
|
||||
|
||||
for (u32 Tag = 1; Tag < StripAt->TagsCount; Tag++)
|
||||
{
|
||||
AssemblyParser_ReadTag(Parser, StripAt, Tag);
|
||||
}
|
||||
}
|
||||
|
||||
internal strip_gen_data AssemblyParser_ReadStripGenData(parser* Parser, assembly* Assembly);
|
||||
|
||||
internal strip_gen_interpolate_points
|
||||
AssemblyParser_ReadInterpolatePoints(parser* Parser)
|
||||
{
|
||||
strip_gen_interpolate_points Result = {0};
|
||||
if (Parser_ReadOpenStruct(Parser, AssemblyField_InterpolatePoints))
|
||||
{
|
||||
Result.StartPosition = Parser_ReadV3Value(Parser, AssemblyField_Start);
|
||||
Result.EndPosition = Parser_ReadV3Value(Parser, AssemblyField_End);
|
||||
Result.LedCount = Parser_ReadU32Value(Parser, AssemblyField_LedCount);
|
||||
if (!Parser_ReadCloseStruct(Parser))
|
||||
{
|
||||
// TODO(pjs):
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs):
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal strip_gen_sequence
|
||||
AssemblyParser_ReadSequence(parser* Parser, assembly* Assembly)
|
||||
{
|
||||
strip_gen_sequence Result = {0};
|
||||
if (Parser_ReadOpenStruct(Parser, AssemblyField_SegmentSequence))
|
||||
{
|
||||
Result.ElementsCount = Parser_ReadU32Value(Parser, AssemblyField_SegmentSequenceLength);
|
||||
Result.Elements = PushArray(&Assembly->Arena, strip_gen_data, Result.ElementsCount);
|
||||
for (u32 i = 0; i < Result.ElementsCount; i++)
|
||||
{
|
||||
Result.Elements[i] = AssemblyParser_ReadStripGenData(Parser, Assembly);
|
||||
}
|
||||
if (!Parser_ReadCloseStruct(Parser))
|
||||
{
|
||||
// TODO(pjs):
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs):
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal strip_gen_data
|
||||
AssemblyParser_ReadStripGenData(parser* Parser, assembly* Assembly)
|
||||
{
|
||||
strip_gen_data Result = {};
|
||||
|
||||
if (Parser_ReadOpenStruct(Parser, AssemblyField_Segment))
|
||||
{
|
||||
gs_string PointPlacementType = Parser_ReadStringValue(Parser, AssemblyField_PointPlacementType);
|
||||
|
||||
// TODO(pjs): We want to store enum strings in some unified way
|
||||
// :EnumStringsGen
|
||||
if (StringsEqual(PointPlacementType.ConstString, ConstString("InterpolatePoints")))
|
||||
{
|
||||
Result.Method = StripGeneration_InterpolatePoints;
|
||||
Result.InterpolatePoints = AssemblyParser_ReadInterpolatePoints(Parser);
|
||||
}
|
||||
else if (StringsEqual(PointPlacementType.ConstString,
|
||||
ConstString("SegmentSequence")))
|
||||
{
|
||||
Result.Method = StripGeneration_Sequence;
|
||||
Result.Sequence = AssemblyParser_ReadSequence(Parser, Assembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(Parser, "Incorrect Point Placement Type found for segment");
|
||||
}
|
||||
|
||||
if (!Parser_ReadCloseStruct(Parser))
|
||||
{
|
||||
Parser_PushErrorF(Parser, "Strip Gen Data did not close the struct where expected");
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal parser
|
||||
ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileText, gs_memory_arena* Transient)
|
||||
{
|
||||
Assembly->LedCountTotal = 0;
|
||||
|
||||
parser Parser = {0};
|
||||
Parser.FileName = FileName;
|
||||
Parser.String = FileText;
|
||||
Parser.Identifiers = &AssemblyFieldIdentifiers[0];
|
||||
Parser.IdentifiersCount = AssemblyField_Count;
|
||||
Parser.At = Parser.String.Str;
|
||||
Parser.LineStart = Parser.At;
|
||||
Parser.Arena = &Assembly->Arena;
|
||||
Parser.Transient = Transient;
|
||||
Parser.Success = true;
|
||||
|
||||
Assembly->Name = Parser_ReadStringValue(&Parser, AssemblyField_AssemblyName);
|
||||
Assembly->Scale = Parser_ReadR32Value(&Parser, AssemblyField_AssemblyScale);
|
||||
Assembly->Center = Parser_ReadV3Value(&Parser, AssemblyField_AssemblyCenter) * Assembly->Scale;
|
||||
Assembly->StripCount = Parser_ReadU32Value(&Parser, AssemblyField_LedStripCount);
|
||||
Assembly->Strips = PushArray(&Assembly->Arena, v2_strip, Assembly->StripCount);
|
||||
|
||||
gs_string OutputModeString = Parser_ReadStringValue(&Parser, AssemblyField_OutputMode);
|
||||
if (StringsEqual(OutputModeString.ConstString, ConstString("UART")))
|
||||
{
|
||||
Assembly->OutputMode = NetworkProtocol_UART;
|
||||
if (Parser_ReadStringValue(&Parser, AssemblyField_UART_ComPort, &Assembly->UARTComPort, true))
|
||||
{
|
||||
Assembly->NetPortMode = NetworkPortMode_GlobalPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assembly->NetPortMode = NetworkPortMode_PortPerStrip;
|
||||
}
|
||||
}
|
||||
else if (StringsEqual(OutputModeString.ConstString, ConstString("SACN")))
|
||||
{
|
||||
Assembly->OutputMode = NetworkProtocol_SACN;
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(&Parser, "Invalid output mode specified for assembly.");
|
||||
Parser.Success = false;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < Assembly->StripCount; i++)
|
||||
{
|
||||
v2_strip* StripAt = Assembly->Strips + i;
|
||||
if (Parser_ReadOpenStruct(&Parser, AssemblyField_LedStrip))
|
||||
{
|
||||
StripAt->SACNAddr = AssemblyParser_ReadSACNAddr(&Parser, *Assembly);
|
||||
StripAt->UARTAddr = AssemblyParser_ReadUARTAddr(&Parser, *Assembly);
|
||||
StripAt->GenerationData = AssemblyParser_ReadStripGenData(&Parser, Assembly);
|
||||
StripAt->LedCount = StripGenData_CountLeds(StripAt->GenerationData);
|
||||
AssemblyParser_ReadTagList(&Parser, StripAt, Assembly);
|
||||
|
||||
Assembly->LedCountTotal += StripAt->LedCount;
|
||||
|
||||
if (!Parser_ReadCloseStruct(&Parser))
|
||||
{
|
||||
Parser_PushErrorF(&Parser, "Strip struct doesn't close where expected");
|
||||
Parser.Success = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(&Parser, "Expected a strip struct but none was found");
|
||||
Parser.Success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Parser;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ASSEMBLY_PARSER_CPP
|
||||
#endif // FOLDHAUS_ASSEMBLY_PARSER_CPP
|
|
@ -1,108 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_addressed_data.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
// addressed_data_buffer is a generic buffer of data that also contains information
|
||||
// regarding how it should be sent to a sculpture.
|
||||
// This decouples the encoding step from the sending step.
|
||||
//
|
||||
#ifndef FOLDHAUS_ADDRESSED_DATA_H
|
||||
|
||||
enum data_buffer_address_type
|
||||
{
|
||||
AddressType_NetworkIP,
|
||||
AddressType_ComPort,
|
||||
AddressType_Invalid,
|
||||
};
|
||||
|
||||
struct addressed_data_buffer
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8* Memory;
|
||||
u32 MemorySize;
|
||||
};
|
||||
gs_data Data;
|
||||
};
|
||||
|
||||
data_buffer_address_type AddressType;
|
||||
|
||||
// IP Address
|
||||
platform_socket_handle SendSocket;
|
||||
u32 V4SendAddress;
|
||||
u32 SendPort;
|
||||
|
||||
// COM
|
||||
gs_const_string ComPort;
|
||||
|
||||
addressed_data_buffer* Next;
|
||||
};
|
||||
|
||||
struct addressed_data_buffer_list
|
||||
{
|
||||
gs_memory_arena* Arena;
|
||||
addressed_data_buffer* Root;
|
||||
addressed_data_buffer* Head;
|
||||
};
|
||||
|
||||
internal void
|
||||
AddressedDataBufferList_Clear(addressed_data_buffer_list* List)
|
||||
{
|
||||
List->Root = 0;
|
||||
List->Head = 0;
|
||||
MemoryArenaClear(List->Arena);
|
||||
}
|
||||
|
||||
internal addressed_data_buffer*
|
||||
AddressedDataBufferList_PushEmpty(addressed_data_buffer_list* List)
|
||||
{
|
||||
addressed_data_buffer* Result = PushStruct(List->Arena, addressed_data_buffer);
|
||||
*Result = {0};
|
||||
Result->Next = 0;
|
||||
Result->MemorySize = 0;
|
||||
Result->Memory = 0;
|
||||
|
||||
SLLPushOrInit(List->Root, List->Head, Result);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal addressed_data_buffer*
|
||||
AddressedDataBufferList_Push(addressed_data_buffer_list* List, u32 BufferSize)
|
||||
{
|
||||
addressed_data_buffer* Result = AddressedDataBufferList_PushEmpty(List);
|
||||
Result->MemorySize = BufferSize;
|
||||
Result->Memory = PushArray(List->Arena, u8, Result->MemorySize);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AddressedDataBuffer_SetNetworkAddress(addressed_data_buffer* Buffer, platform_socket_handle SendSocket, u32 V4SendAddress, u32 SendPort)
|
||||
{
|
||||
Buffer->AddressType = AddressType_NetworkIP;
|
||||
Buffer->SendSocket = SendSocket;
|
||||
Buffer->V4SendAddress = V4SendAddress;
|
||||
Buffer->SendPort = SendPort;
|
||||
}
|
||||
|
||||
internal void
|
||||
AddressedDataBuffer_SetCOMPort(addressed_data_buffer* Buffer, gs_const_string ComPort)
|
||||
{
|
||||
Buffer->AddressType = AddressType_ComPort;
|
||||
Buffer->ComPort = ComPort;
|
||||
}
|
||||
|
||||
internal addressed_data_buffer_list
|
||||
AddressedDataBufferList_Create(gs_thread_context TC)
|
||||
{
|
||||
addressed_data_buffer_list Result = {};
|
||||
Result.Arena = AllocStruct(TC.Allocator, gs_memory_arena, "Addressed Data");
|
||||
*Result.Arena = MemoryArenaCreate(KB(256), Bytes(8), TC.Allocator, 0, 0, "Addressed Data Buffer List Arena");
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ADDRESSED_DATA_H
|
||||
#endif // FOLDHAUS_ADDRESSED_DATA_H
|
|
@ -1,126 +0,0 @@
|
|||
/* date = April 12th 2021 4:25 pm */
|
||||
|
||||
#ifndef FOLDHAUS_LOG_H
|
||||
#define FOLDHAUS_LOG_H
|
||||
|
||||
enum log_entry_type
|
||||
{
|
||||
LogEntry_Message,
|
||||
LogEntry_Error,
|
||||
};
|
||||
|
||||
struct log_entry
|
||||
{
|
||||
log_entry_type Type;
|
||||
gs_string String;
|
||||
};
|
||||
|
||||
struct log_buffer
|
||||
{
|
||||
gs_memory_arena* Arena;
|
||||
|
||||
u64 EntriesCount;
|
||||
u64 NextEntry;
|
||||
log_entry* Entries;
|
||||
};
|
||||
|
||||
struct log_buffer_iter
|
||||
{
|
||||
log_buffer* Buffer;
|
||||
u64 Start;
|
||||
u64 IndexAt;
|
||||
log_entry* At;
|
||||
};
|
||||
|
||||
internal log_buffer
|
||||
Log_Init(gs_memory_arena* A, u64 Count)
|
||||
{
|
||||
log_buffer Result = {};
|
||||
Result.Arena = A;
|
||||
Result.EntriesCount = Count;
|
||||
Result.Entries = PushArray(A, log_entry, Result.EntriesCount);
|
||||
|
||||
u64 LogStringLength = 512;
|
||||
u64 LogStringBufferSize = LogStringLength * Result.EntriesCount;
|
||||
char* LogStringBuffer = PushArray(A, char, LogStringBufferSize);
|
||||
char* LogStringBufferAt = LogStringBuffer;
|
||||
for (u32 i = 0; i < Result.EntriesCount; i++)
|
||||
{
|
||||
Result.Entries[i].String = MakeString(LogStringBufferAt, 0, LogStringLength);
|
||||
LogStringBufferAt += LogStringLength;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u64
|
||||
Log_GetNextIndex(log_buffer Log, u64 At)
|
||||
{
|
||||
u64 Result = At + 1;
|
||||
if (Result >= Log.EntriesCount)
|
||||
{
|
||||
Result = 0;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal log_entry*
|
||||
Log_TakeNextEntry(log_buffer* Log)
|
||||
{
|
||||
log_entry* Result = Log->Entries + Log->NextEntry;
|
||||
Log->NextEntry = Log_GetNextIndex(*Log, Log->NextEntry);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Log_PrintFVarArgs(log_buffer* Log, log_entry_type Type, char* Format, va_list Args)
|
||||
{
|
||||
log_entry* NextEntry = Log_TakeNextEntry(Log);
|
||||
NextEntry->String.Length = 0;
|
||||
NextEntry->Type = Type;
|
||||
PrintFArgsList(&NextEntry->String, Format, Args);
|
||||
NullTerminate(&NextEntry->String);
|
||||
|
||||
#if DEBUG
|
||||
OutputDebugStringA(NextEntry->String.Str);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define Log_Message(log, fmt, ...) Log_PrintF(log, LogEntry_Message, fmt, __VA_ARGS__)
|
||||
#define Log_Error(log, fmt, ...) Log_PrintF(log, LogEntry_Error, fmt, __VA_ARGS__)
|
||||
internal void
|
||||
Log_PrintF(log_buffer* Log, log_entry_type Type, char* Format, ...)
|
||||
{
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
Log_PrintFVarArgs(Log, Type, Format, Args);
|
||||
va_end(Args);
|
||||
}
|
||||
|
||||
internal log_buffer_iter
|
||||
Log_GetIter(log_buffer* Buffer)
|
||||
{
|
||||
log_buffer_iter Result = {};
|
||||
Result.Buffer = Buffer;
|
||||
Result.Start = Buffer->NextEntry;
|
||||
Result.IndexAt = Result.Start;
|
||||
Result.At = Result.Buffer->Entries + Result.IndexAt;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
LogIter_CanAdvance(log_buffer_iter Iter)
|
||||
{
|
||||
u64 Next = Log_GetNextIndex(*Iter.Buffer, Iter.IndexAt);
|
||||
bool Result = Next != Iter.Start;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
LogIter_Advance(log_buffer_iter* Iter)
|
||||
{
|
||||
Iter->IndexAt = Log_GetNextIndex(*Iter->Buffer, Iter->IndexAt);
|
||||
Iter->At = Iter->Buffer->Entries + Iter->IndexAt;
|
||||
}
|
||||
|
||||
#endif //FOLDHAUS_LOG_H
|
|
@ -1,154 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_network_ordering.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_NETWORK_ORDERING_H
|
||||
|
||||
// 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 u16
|
||||
PackB2(u16 Value)
|
||||
{
|
||||
u16 Result = 0;
|
||||
u8* Array = (u8*)&Result;
|
||||
Array[1] = (u8)(Value & 0xFF);
|
||||
Array[0] = (u8)((Value & 0xFF00) >> 8);
|
||||
return Result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_NETWORK_ORDERING_H
|
||||
#endif // FOLDHAUS_NETWORK_ORDERING_H
|
|
@ -1,588 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_serializer.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-09
|
||||
//
|
||||
#ifndef FOLDHAUS_SERIALIZER_H
|
||||
|
||||
struct serializer
|
||||
{
|
||||
gs_string String;
|
||||
u32 Indent;
|
||||
gs_const_string* Identifiers;
|
||||
u32 IdentifiersCount;
|
||||
};
|
||||
|
||||
internal gs_const_string
|
||||
Serializer_GetIdent(serializer* Serializer, u32 Index)
|
||||
{
|
||||
gs_const_string Result = Serializer->Identifiers[Index];
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Serializing
|
||||
|
||||
internal void
|
||||
Serializer_WriteIndent(serializer* Serializer)
|
||||
{
|
||||
for (u32 i = 0; i < Serializer->Indent; i++)
|
||||
{
|
||||
AppendPrintF(&Serializer->String, " ");
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteF(serializer* Serializer, char* Format, ...)
|
||||
{
|
||||
Serializer_WriteIndent(Serializer);
|
||||
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
PrintFArgsList(&Serializer->String, Format, Args);
|
||||
va_end(Args);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_OpenStruct(serializer* Serializer, gs_const_string StructName)
|
||||
{
|
||||
Serializer_WriteF(Serializer, "%S:{\n", StructName);
|
||||
Serializer->Indent++;
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_OpenStruct(serializer* Serializer, u32 StructIdentifier)
|
||||
{
|
||||
gs_const_string Ident = Serializer_GetIdent(Serializer, StructIdentifier);
|
||||
Serializer_WriteF(Serializer, "%S:{\n", Ident);
|
||||
Serializer->Indent++;
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_CloseStruct(serializer* Serializer)
|
||||
{
|
||||
Serializer->Indent--;
|
||||
Serializer_WriteF(Serializer, "};\n");
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteValue(serializer* Serializer, gs_const_string Ident, gs_const_string Value)
|
||||
{
|
||||
Serializer_WriteF(Serializer, "%S: %S;\n", Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteValue(serializer* Serializer, u32 IdentIndex, gs_const_string Value)
|
||||
{
|
||||
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
|
||||
Serializer_WriteValue(Serializer, Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteValue(serializer* Serializer, gs_const_string Ident, u32 Value)
|
||||
{
|
||||
Serializer_WriteF(Serializer, "%S: %d;\n", Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteValue(serializer* Serializer, u32 IdentIndex, u32 Value)
|
||||
{
|
||||
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
|
||||
Serializer_WriteValue(Serializer, Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteValue(serializer* Serializer, gs_const_string Ident, r32 Value)
|
||||
{
|
||||
Serializer_WriteF(Serializer, "%S: %f;\n", Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteValue(serializer* Serializer, u32 IdentIndex, r32 Value)
|
||||
{
|
||||
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
|
||||
Serializer_WriteValue(Serializer, Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteStringValue(serializer* Serializer, gs_const_string Ident, gs_const_string Value)
|
||||
{
|
||||
Serializer_WriteF(Serializer, "%S: \"%S\";\n", Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteStringValue(serializer* Serializer, u32 IdentIndex, gs_const_string Value)
|
||||
{
|
||||
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
|
||||
Serializer_WriteStringValue(Serializer, Ident, Value);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteV3Value(serializer* Serializer, gs_const_string Ident, v3 Value)
|
||||
{
|
||||
Serializer_WriteF(Serializer, "%S: (%f, %f, %f);\n", Ident, Value.x, Value.y, Value.z);
|
||||
}
|
||||
|
||||
internal void
|
||||
Serializer_WriteV3Value(serializer* Serializer, u32 IdentIndex, v3 Value)
|
||||
{
|
||||
gs_const_string Ident = Serializer_GetIdent(Serializer, IdentIndex);
|
||||
Serializer_WriteV3Value(Serializer, Ident, Value);
|
||||
}
|
||||
|
||||
// Parsing
|
||||
|
||||
struct parser_error
|
||||
{
|
||||
gs_string Message;
|
||||
|
||||
gs_const_string FileName;
|
||||
u32 LineNumber;
|
||||
|
||||
parser_error* Next;
|
||||
};
|
||||
|
||||
struct parser
|
||||
{
|
||||
gs_const_string FileName;
|
||||
|
||||
gs_string String;
|
||||
|
||||
gs_const_string* Identifiers;
|
||||
u32 IdentifiersCount;
|
||||
|
||||
u32 Line;
|
||||
|
||||
char* LineStart;
|
||||
char* At;
|
||||
|
||||
gs_memory_arena* Arena;
|
||||
gs_memory_arena* Transient;
|
||||
|
||||
parser_error* ErrorsRoot;
|
||||
parser_error* ErrorsHead;
|
||||
|
||||
bool Success;
|
||||
};
|
||||
|
||||
internal void
|
||||
Parser_PushErrorF(parser* Parser, char* Format, ...)
|
||||
{
|
||||
parser_error* Error = PushStruct(Parser->Transient, parser_error);
|
||||
Error->FileName = Parser->FileName;
|
||||
Error->LineNumber = Parser->Line;
|
||||
|
||||
Error->Message = PushString(Parser->Transient, 1024);
|
||||
PrintF(&Error->Message, "Error:\n");
|
||||
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
PrintFArgsList(&Error->Message, Format, Args);
|
||||
va_end(Args);
|
||||
|
||||
AppendPrintF(&Error->Message, "\n\tFile: %S\n\tLine: %d\n",
|
||||
Error->FileName, Error->LineNumber);
|
||||
NullTerminate(&Error->Message);
|
||||
|
||||
SLLPushOrInit(Parser->ErrorsRoot, Parser->ErrorsHead, Error);
|
||||
}
|
||||
|
||||
internal gs_const_string
|
||||
Parser_GetIdent(parser Parser, u32 Index)
|
||||
{
|
||||
gs_const_string Result = Parser.Identifiers[Index];
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_AtValidPosition(parser Parser)
|
||||
{
|
||||
u64 Offset = Parser.At - Parser.String.Str;
|
||||
bool Result = (Offset < Parser.String.Length);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Parser_AdvanceChar(parser* P)
|
||||
{
|
||||
if (IsNewline(P->At[0]))
|
||||
{
|
||||
P->Line += 1;
|
||||
P->LineStart = P->At + 1;
|
||||
|
||||
if ((P->At[0] == '\n' && P->At[1] == '\r') ||
|
||||
(P->At[0] == '\r' && P->At[1] == '\n'))
|
||||
{
|
||||
P->At++;
|
||||
P->At++;
|
||||
}
|
||||
else if (P->At[0] == '\n')
|
||||
{
|
||||
P->At++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(pjs): Not sure this is actually invalid
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
P->At++;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Parser_EatWhitespace(parser* P)
|
||||
{
|
||||
while(Parser_AtValidPosition(*P) && IsNewlineOrWhitespace(P->At[0]))
|
||||
{
|
||||
Parser_AdvanceChar(P);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Parser_EatToNewLine(parser* P)
|
||||
{
|
||||
while(Parser_AtValidPosition(*P) && !IsNewline(P->At[0]))
|
||||
{
|
||||
Parser_AdvanceChar(P);
|
||||
}
|
||||
Parser_EatWhitespace(P);
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_AdvanceIfTokenEquals(parser* P, gs_const_string Value)
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
char* PAt = P->At;
|
||||
char* VAt = Value.Str;
|
||||
while (*VAt != 0)
|
||||
{
|
||||
if (*PAt != *VAt)
|
||||
{
|
||||
Result = false;
|
||||
break;
|
||||
}
|
||||
PAt += 1;
|
||||
VAt += 1;
|
||||
}
|
||||
|
||||
// TODO(Peter): What if the token is a subset of Value? ie. this would return true for
|
||||
// T->At = hello_world and Value = hello_
|
||||
// But the next token we read would fail
|
||||
|
||||
if (Result)
|
||||
{
|
||||
P->At = PAt;
|
||||
Parser_EatWhitespace(P);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_AdvanceIfLineEnd(parser* P)
|
||||
{
|
||||
bool Result = Parser_AdvanceIfTokenEquals(P, ConstString(";"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadString(parser* P, gs_const_string String)
|
||||
{
|
||||
// string;
|
||||
bool Result = Parser_AdvanceIfTokenEquals(P, String);
|
||||
Result &= Parser_AdvanceIfLineEnd(P);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadString(parser* P, u32 IdentIndex)
|
||||
{
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
return Parser_ReadString(P, Ident);
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadStringValue(parser* P, gs_const_string Ident, gs_string* Output, bool ShouldNullTerminate = false)
|
||||
{
|
||||
Assert(Output != 0);
|
||||
|
||||
// ident: "value";
|
||||
bool Result = false;
|
||||
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
|
||||
Parser_AdvanceIfTokenEquals(P, ConstString(":")) &&
|
||||
Parser_AdvanceIfTokenEquals(P, ConstString("\"")))
|
||||
{
|
||||
gs_const_string FileString = {0};
|
||||
FileString.Str = P->At;
|
||||
|
||||
while (Parser_AtValidPosition(*P) && P->At[0] != '"')
|
||||
{
|
||||
Parser_AdvanceChar(P);
|
||||
}
|
||||
|
||||
FileString.Length = P->At - FileString.Str;
|
||||
if (Parser_AdvanceIfTokenEquals(P, ConstString("\"")) &&
|
||||
Parser_AdvanceIfLineEnd(P))
|
||||
{
|
||||
u32 StringLength = FileString.Length;
|
||||
if (ShouldNullTerminate)
|
||||
{
|
||||
StringLength += 1;
|
||||
}
|
||||
|
||||
Result = true;
|
||||
*Output = PushStringF(P->Arena, StringLength, "%S", FileString);
|
||||
if (ShouldNullTerminate)
|
||||
{
|
||||
NullTerminate(Output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(P, "String doesn't have a closing quote, or line doesn't end with a semicolon");
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadStringValue(parser* P, u32 IdentIndex, gs_string* Result, bool ShouldNullTerminate = false)
|
||||
{
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
return Parser_ReadStringValue(P, Ident, Result, ShouldNullTerminate);
|
||||
}
|
||||
|
||||
internal gs_string
|
||||
Parser_ReadStringValue(parser* P, gs_const_string Ident, bool ShouldNullTerminate = false)
|
||||
{
|
||||
gs_string Result = {0};
|
||||
Parser_ReadStringValue(P, Ident, &Result, ShouldNullTerminate);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_string
|
||||
Parser_ReadStringValue(parser* P, u32 IdentIndex, bool ShouldNullTerminate = false)
|
||||
{
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
return Parser_ReadStringValue(P, Ident, ShouldNullTerminate);
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadOpenStruct(parser* P, gs_const_string Ident)
|
||||
{
|
||||
// ident: {
|
||||
bool Result = Parser_AdvanceIfTokenEquals(P, Ident);
|
||||
Result &= Parser_AdvanceIfTokenEquals(P, ConstString(":"));
|
||||
Result &= Parser_AdvanceIfTokenEquals(P, ConstString("{"));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadOpenStruct(parser* P, u32 IdentIndex)
|
||||
{
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
return Parser_ReadOpenStruct(P, Ident);
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadCloseStruct(parser* P)
|
||||
{
|
||||
// }
|
||||
bool Result = Parser_AdvanceIfTokenEquals(P, ConstString("}"));
|
||||
Result &= Parser_AdvanceIfLineEnd(P);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadNumberString(parser* P, gs_const_string* Output)
|
||||
{
|
||||
Assert(Output != 0);
|
||||
|
||||
bool Success = false;
|
||||
|
||||
if (IsNumericExtended(P->At[0]))
|
||||
{
|
||||
char* NumStart = P->At;
|
||||
while(Parser_AtValidPosition(*P) && IsNumericExtended(P->At[0]))
|
||||
{
|
||||
Parser_AdvanceChar(P);
|
||||
}
|
||||
|
||||
Output->Str = NumStart;
|
||||
Output->Length = P->At - NumStart;
|
||||
Success = true;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal gs_const_string
|
||||
Parser_ReadNumberString(parser* P)
|
||||
{
|
||||
gs_const_string Result = {0};
|
||||
Parser_ReadNumberString(P, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadU32Value(parser* P, gs_const_string Ident, u32* Result)
|
||||
{
|
||||
// ident: value;
|
||||
bool Success = false;
|
||||
|
||||
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
|
||||
Parser_AdvanceIfTokenEquals(P, ConstString(":")))
|
||||
{
|
||||
gs_const_string NumStr = Parser_ReadNumberString(P);
|
||||
if (Parser_AdvanceIfLineEnd(P))
|
||||
{
|
||||
*Result = (u32)ParseInt(NumStr);
|
||||
Success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(P, "U32 Value doesn't end with semicolon");
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal u32
|
||||
Parser_ReadU32Value(parser* P, gs_const_string Ident)
|
||||
{
|
||||
u32 Result = 0;
|
||||
Parser_ReadU32Value(P, Ident, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
Parser_ReadU32Value(parser* P, u32 IdentIndex)
|
||||
{
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
return Parser_ReadU32Value(P, Ident);
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadR32(parser* P, r32* Result)
|
||||
{
|
||||
bool Success = false;
|
||||
gs_const_string NumStr = {0};
|
||||
if (Parser_ReadNumberString(P, &NumStr))
|
||||
{
|
||||
*Result = (r32)ParseFloat(NumStr);
|
||||
Success = true;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Parser_ReadR32(parser* P)
|
||||
{
|
||||
r32 Result = 0;
|
||||
Parser_ReadR32(P, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadR32Value(parser* P, gs_const_string Ident, r32* Result)
|
||||
{
|
||||
// ident: value;
|
||||
bool Success = false;
|
||||
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
|
||||
Parser_AdvanceIfTokenEquals(P, ConstString(":")))
|
||||
{
|
||||
r32 Value = Parser_ReadR32(P);
|
||||
if (Parser_AdvanceIfLineEnd(P))
|
||||
{
|
||||
*Result = Value;
|
||||
Success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(P, "R32 Value doesn't end with semicolon");
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Parser_ReadR32Value(parser* P, gs_const_string Ident)
|
||||
{
|
||||
r32 Result = 0;
|
||||
Parser_ReadR32Value(P, Ident, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Parser_ReadR32Value(parser* P, u32 IdentIndex)
|
||||
{
|
||||
r32 Result = 0;
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
Parser_ReadR32Value(P, Ident, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Parser_ReadV3Value(parser* P, gs_const_string Ident, v3* Result)
|
||||
{
|
||||
Assert(Result != 0);
|
||||
bool Success = false;
|
||||
if (Parser_AdvanceIfTokenEquals(P, Ident) &&
|
||||
Parser_AdvanceIfTokenEquals(P, ConstString(":")) &&
|
||||
Parser_AdvanceIfTokenEquals(P, ConstString("(")))
|
||||
{
|
||||
r32 X = Parser_ReadR32(P);
|
||||
if (!Parser_AdvanceIfTokenEquals(P, ConstString(",")))
|
||||
{
|
||||
Parser_PushErrorF(P, "V3 Value doesn't have comma separated values");
|
||||
}
|
||||
|
||||
r32 Y = Parser_ReadR32(P);
|
||||
if (!Parser_AdvanceIfTokenEquals(P, ConstString(",")))
|
||||
{
|
||||
Parser_PushErrorF(P, "V3 Value doesn't have comma separated values");
|
||||
}
|
||||
|
||||
r32 Z = Parser_ReadR32(P);
|
||||
if (Parser_AdvanceIfTokenEquals(P, ConstString(")")) &&
|
||||
Parser_AdvanceIfLineEnd(P))
|
||||
{
|
||||
Result->x = X;
|
||||
Result->y = Y;
|
||||
Result->z = Z;
|
||||
Success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser_PushErrorF(P, "V3 Value doesn't end correctly");
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal v3
|
||||
Parser_ReadV3Value(parser* P, gs_const_string Ident)
|
||||
{
|
||||
v3 Result = {0};
|
||||
Parser_ReadV3Value(P, Ident, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal v3
|
||||
Parser_ReadV3Value(parser* P, u32 IdentIndex)
|
||||
{
|
||||
v3 Result = {0};
|
||||
gs_const_string Ident = Parser_GetIdent(*P, IdentIndex);
|
||||
Parser_ReadV3Value(P, Ident, &Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_SERIALIZER_H
|
||||
#endif // FOLDHAUS_SERIALIZER_H
|
|
@ -1,411 +0,0 @@
|
|||
//
|
||||
// File: sacn.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef SACN_H
|
||||
|
||||
#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 streaming_acn
|
||||
{
|
||||
platform_socket_handle SendSocket;
|
||||
cid CID;
|
||||
s32 SequenceIterator;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////
|
||||
//
|
||||
// SACN Data Header Functions
|
||||
//
|
||||
///////////////////////////////////////////////
|
||||
|
||||
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
|
||||
gs_stringToCID_ (const char* gs_string)
|
||||
{
|
||||
cid Result = {};
|
||||
|
||||
const char* Src = gs_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;
|
||||
}
|
||||
|
||||
internal void
|
||||
InitStreamHeader (u8* Buffer, s32 BufferSize,
|
||||
u16 SlotCount,
|
||||
u8 StartCode,
|
||||
u16 Universe,
|
||||
u8 Priority,
|
||||
u16 Reserved,
|
||||
u8 Options,
|
||||
const char* SourceName,
|
||||
cid CID
|
||||
)
|
||||
{
|
||||
// TODO(pjs): Replace packing with gs_memory_cursor
|
||||
|
||||
u8* Cursor = Buffer;
|
||||
|
||||
// Preamble Size
|
||||
Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE);
|
||||
Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE);
|
||||
|
||||
CopyMemoryTo(ACN_IDENTIFIER, Cursor, 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
|
||||
// :Check
|
||||
CopyMemoryTo(SourceName, (char*)Cursor, 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 = PackB1(Cursor, 0);
|
||||
|
||||
// DMP Address Increment
|
||||
Cursor = PackB1(Cursor, ADDRESS_INC);
|
||||
|
||||
// Property Value Count -- Includes one byte for start code
|
||||
Cursor = PackB2(Cursor, SlotCount + 1);
|
||||
|
||||
Cursor = PackB1(Cursor, StartCode);
|
||||
|
||||
Assert(Cursor - Buffer == STREAM_HEADER_SIZE);
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// New SACN
|
||||
//
|
||||
|
||||
internal streaming_acn
|
||||
SACN_Initialize (context Context)
|
||||
{
|
||||
streaming_acn SACN = {};
|
||||
|
||||
s32 Multicast_TimeToLive = 20;
|
||||
SACN.SendSocket = Context.PlatformGetSocketHandle(Multicast_TimeToLive);
|
||||
SACN.CID = gs_stringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
|
||||
|
||||
return SACN;
|
||||
}
|
||||
|
||||
internal void
|
||||
SACN_Cleanup(streaming_acn* SACN, context Context)
|
||||
{
|
||||
}
|
||||
|
||||
internal void
|
||||
SACN_UpdateSequence (streaming_acn* SACN)
|
||||
{
|
||||
// Never use 0 after the first one
|
||||
if (++SACN->SequenceIterator == 0)
|
||||
{
|
||||
++SACN->SequenceIterator;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
SACN_PrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN)
|
||||
{
|
||||
Assert(SizeReservedForHeader == STREAM_HEADER_SIZE);
|
||||
Assert(Buffer && BufferSize > 0);
|
||||
|
||||
s32 Priority = 0;
|
||||
InitStreamHeader(Buffer, BufferSize, STREAM_BODY_SIZE, STARTCODE_DMX, Universe, Priority, 0, 0, "Lumenarium", SACN.CID);
|
||||
SetStreamHeaderSequence_(Buffer, SACN.SequenceIterator, false);
|
||||
}
|
||||
|
||||
internal u32
|
||||
SACN_GetUniverseSendAddress(s32 Universe)
|
||||
{
|
||||
u8 MulticastAddressBuffer[4] = {};
|
||||
MulticastAddressBuffer[0] = 239;
|
||||
MulticastAddressBuffer[1] = 255;
|
||||
MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit
|
||||
MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit
|
||||
|
||||
u32 V4Address = (u32)UpackB4(MulticastAddressBuffer);
|
||||
return V4Address;
|
||||
}
|
||||
|
||||
internal u64
|
||||
SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, u64 LedsPlaced, led_buffer LedBuffer)
|
||||
{
|
||||
u8* DestChannel = BufferStart;
|
||||
u64 FirstLed = LedsPlaced;
|
||||
u64 LedsToAdd = Min(Strip.LedCount - LedsPlaced, STREAM_BODY_SIZE / 3);
|
||||
u64 OnePastLastLed = FirstLed + LedsToAdd;
|
||||
for (u32 i = FirstLed; i < OnePastLastLed; i++)
|
||||
{
|
||||
u32 LedIndex = Strip.LedLUT[i];
|
||||
pixel Color = LedBuffer.Colors[LedIndex];
|
||||
|
||||
DestChannel[0] = Color.R;
|
||||
DestChannel[1] = Color.G;
|
||||
DestChannel[2] = Color.B;
|
||||
DestChannel += 3;
|
||||
}
|
||||
return LedsToAdd;
|
||||
}
|
||||
|
||||
internal void
|
||||
SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem)
|
||||
{
|
||||
SACN_UpdateSequence(SACN);
|
||||
|
||||
// TODO(pjs): 512 is a magic number - make it a constant?
|
||||
s32 BufferHeaderSize = STREAM_HEADER_SIZE;
|
||||
s32 BufferBodySize = STREAM_BODY_SIZE;
|
||||
s32 BufferSize = BufferHeaderSize + BufferBodySize;
|
||||
|
||||
for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++)
|
||||
{
|
||||
assembly Assembly = Assemblies.Values[AssemblyIdx];
|
||||
led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
|
||||
|
||||
for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++)
|
||||
{
|
||||
v2_strip StripAt = Assembly.Strips[StripIdx];
|
||||
|
||||
// NOTE(PS): This isn't actually invalid, we just haven't needed to implement
|
||||
// something more complex than only allowing strips to start at the first
|
||||
// channel of a universe
|
||||
Assert(StripAt.SACNAddr.StartChannel == 1);
|
||||
|
||||
u32 UniverseAt = StripAt.SACNAddr.StartUniverse;
|
||||
u64 LedsPlaced = 0;
|
||||
while (LedsPlaced < StripAt.LedCount)
|
||||
{
|
||||
u32 V4SendAddress = SACN_GetUniverseSendAddress(UniverseAt);
|
||||
u32 SendPort = DEFAULT_STREAMING_ACN_PORT;
|
||||
|
||||
addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize);
|
||||
AddressedDataBuffer_SetNetworkAddress(Data, SACN->SendSocket, V4SendAddress, SendPort);
|
||||
|
||||
SACN_PrepareBufferHeader(UniverseAt, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN);
|
||||
LedsPlaced += SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, LedsPlaced, *LedBuffer);
|
||||
|
||||
UniverseAt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define SACN_H
|
||||
#endif // SACN_H
|
|
@ -1,173 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_uart.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-10
|
||||
//
|
||||
#ifndef FOLDHAUS_UART_CPP
|
||||
|
||||
|
||||
internal void
|
||||
UART_SetChannelBuffer_Create(gs_memory_cursor* WriteCursor, uart_channel ChannelSettings, v2_strip Strip, led_buffer LedBuffer)
|
||||
{
|
||||
// NOTE(pjs): This is just here because the information is duplicated and I want to be sure
|
||||
// to catch the error where they are different
|
||||
Assert(ChannelSettings.PixelsCount == Strip.LedCount);
|
||||
|
||||
uart_header* Header = MemoryCursorPushStruct(WriteCursor, uart_header);
|
||||
UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812);
|
||||
|
||||
uart_channel* Channel = MemoryCursorPushStruct(WriteCursor, uart_channel);
|
||||
*Channel = ChannelSettings;
|
||||
|
||||
for (u32 i = 0; i < Channel->PixelsCount; i++)
|
||||
{
|
||||
u32 LedIndex = Strip.LedLUT[i];
|
||||
pixel Color = LedBuffer.Colors[LedIndex];
|
||||
|
||||
u8* OutputPixel = MemoryCursorPushArray(WriteCursor, u8, 3);
|
||||
|
||||
// TODO(pjs): Use the Output mask
|
||||
#if 1
|
||||
OutputPixel[0] = Color.R;
|
||||
OutputPixel[1] = Color.G;
|
||||
OutputPixel[2] = Color.B;
|
||||
#else
|
||||
OutputPixel[0] = 255;
|
||||
OutputPixel[1] = 255;
|
||||
OutputPixel[2] = 255;
|
||||
#endif
|
||||
if (Channel->ElementsCount == 4)
|
||||
{
|
||||
// TODO(pjs): Calculate white from the RGB components?
|
||||
// Generally we just need a good way to handle the white channel,
|
||||
// both in the renderer and in output
|
||||
|
||||
//OutputPixel[Channel->WhiteIndex] = Color.W;
|
||||
}
|
||||
}
|
||||
|
||||
uart_footer* Footer = MemoryCursorPushStruct(WriteCursor, uart_footer);
|
||||
UART_FillFooter(Footer, (u8*)Header);
|
||||
}
|
||||
|
||||
internal void
|
||||
UART_DrawAll_Create(gs_memory_cursor* WriteCursor)
|
||||
{
|
||||
uart_header* Header = MemoryCursorPushStruct(WriteCursor, uart_header);
|
||||
UART_FillHeader(Header, 1, UART_DRAW_ALL);
|
||||
|
||||
uart_footer* Footer = MemoryCursorPushStruct(WriteCursor, uart_footer);
|
||||
UART_FillFooter(Footer, (u8*)Header);
|
||||
}
|
||||
|
||||
internal void
|
||||
UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* Transient)
|
||||
{
|
||||
uart_channel ChannelSettings = {0};
|
||||
ChannelSettings.ElementsCount = 3;
|
||||
ChannelSettings.ColorPackingOrder = 36;
|
||||
|
||||
// NOTE(pjs): This is the minimum size of every UART message. SetChannelBuffer messages will
|
||||
// be bigger than this, but their size is based on the number of pixels in each channel
|
||||
u32 MessageBaseSize = UART_MESSAGE_MIN_SIZE;
|
||||
|
||||
for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++)
|
||||
{
|
||||
assembly Assembly = Assemblies.Values[AssemblyIdx];
|
||||
led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
|
||||
|
||||
struct strips_to_data_buffer
|
||||
{
|
||||
gs_const_string ComPort;
|
||||
|
||||
u32* StripIndices;
|
||||
u32 StripIndicesCount;
|
||||
u32 StripIndicesCountMax;
|
||||
|
||||
u64 LedCount;
|
||||
|
||||
u8** ChannelsStart;
|
||||
|
||||
strips_to_data_buffer* Next;
|
||||
};
|
||||
|
||||
u32 BuffersNeededCount = 0;
|
||||
strips_to_data_buffer* BuffersNeededHead = 0;
|
||||
strips_to_data_buffer* BuffersNeededTail = 0;
|
||||
|
||||
for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++)
|
||||
{
|
||||
v2_strip StripAt = Assembly.Strips[StripIdx];
|
||||
|
||||
// If there is a buffer for this com port already created
|
||||
// we use that
|
||||
strips_to_data_buffer* BufferSelected = 0;
|
||||
for (strips_to_data_buffer* At = BuffersNeededHead;
|
||||
At!= 0;
|
||||
At = At->Next)
|
||||
{
|
||||
if (StringsEqual(At->ComPort, StripAt.UARTAddr.ComPort.ConstString))
|
||||
{
|
||||
BufferSelected = At;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no existing buffer for this com port
|
||||
// create a new one
|
||||
if (!BufferSelected)
|
||||
{
|
||||
BufferSelected = PushStruct(Transient, strips_to_data_buffer);
|
||||
*BufferSelected = {};
|
||||
BufferSelected->ComPort = StripAt.UARTAddr.ComPort.ConstString;
|
||||
// we don't know at this point how many indices per
|
||||
// com port so just make enough room to fit all the strips
|
||||
// if necessary
|
||||
BufferSelected->StripIndicesCountMax = Assembly.StripCount;
|
||||
BufferSelected->StripIndices = PushArray(Transient, u32, BufferSelected->StripIndicesCountMax);
|
||||
BufferSelected->LedCount = 0;
|
||||
BufferSelected->Next = 0;
|
||||
|
||||
SLLPushOrInit(BuffersNeededHead, BuffersNeededTail, BufferSelected);
|
||||
BuffersNeededCount += 1;
|
||||
}
|
||||
|
||||
Assert(BufferSelected->StripIndicesCount < BufferSelected->StripIndicesCountMax);
|
||||
u32 Index = BufferSelected->StripIndicesCount++;
|
||||
BufferSelected->StripIndices[Index] = StripIdx;
|
||||
BufferSelected->LedCount += StripAt.LedCount;
|
||||
}
|
||||
|
||||
for (strips_to_data_buffer* At = BuffersNeededHead;
|
||||
At!= 0;
|
||||
At = At->Next)
|
||||
{
|
||||
u32 TotalBufferSize = MessageBaseSize * Assembly.StripCount; // SetChannelBuffer messages
|
||||
TotalBufferSize += MessageBaseSize; // DrawAll message
|
||||
TotalBufferSize += ChannelSettings.ElementsCount * At->LedCount; // pixels * channels per pixel
|
||||
|
||||
At->ChannelsStart = PushArray(Transient, u8*, At->StripIndicesCount);
|
||||
|
||||
addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, TotalBufferSize);
|
||||
gs_const_string ComPort = At->ComPort;
|
||||
AddressedDataBuffer_SetCOMPort(Buffer, ComPort);
|
||||
|
||||
gs_memory_cursor WriteCursor = MemoryCursorCreate(Buffer->Memory, Buffer->MemorySize);
|
||||
|
||||
for (u32 i = 0; i < At->StripIndicesCount; i++)
|
||||
{
|
||||
u32 StripIdx = At->StripIndices[i];
|
||||
v2_strip StripAt = Assembly.Strips[StripIdx];
|
||||
|
||||
ChannelSettings.PixelsCount = StripAt.LedCount;
|
||||
UART_SetChannelBuffer_Create(&WriteCursor, ChannelSettings, StripAt, *LedBuffer);
|
||||
}
|
||||
|
||||
UART_DrawAll_Create(&WriteCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_UART_CPP
|
||||
#endif // FOLDHAUS_UART_CPP
|
|
@ -1,88 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_uart.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
#ifndef FOLDHAUS_UART_H
|
||||
|
||||
enum uart_record_type
|
||||
{
|
||||
UART_SET_CHANNEL_WS2812 = 1,
|
||||
UART_DRAW_ALL = 2,
|
||||
};
|
||||
|
||||
// NOTE(pjs): these are data packet structures and need to have 1 byte packing
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct uart_header
|
||||
{
|
||||
s8 MagicNumber[4];
|
||||
u8 Channel;
|
||||
u8 RecordType;
|
||||
};
|
||||
|
||||
struct uart_channel
|
||||
{
|
||||
u8 ElementsCount;
|
||||
u8 ColorPackingOrder;
|
||||
u16 PixelsCount;
|
||||
};
|
||||
|
||||
struct uart_footer
|
||||
{
|
||||
u32 CRC;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define UART_MESSAGE_MIN_SIZE sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer)
|
||||
|
||||
global u32 UART_CRCTable[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
internal void
|
||||
UART_FillHeader(uart_header* Header, u8 Channel, u8 RecordType)
|
||||
{
|
||||
Header->MagicNumber[0] = 'U';
|
||||
Header->MagicNumber[1] = 'P';
|
||||
Header->MagicNumber[2] = 'X';
|
||||
Header->MagicNumber[3] = 'L';
|
||||
|
||||
Header->Channel = Channel;
|
||||
Header->RecordType = RecordType;
|
||||
}
|
||||
|
||||
internal u32
|
||||
UART_CalculateCRC(u8* BufferStart, u8* BufferEnd)
|
||||
{
|
||||
// Calculate the CRC
|
||||
u32 CRC = 0xFFFFFFFF;
|
||||
u32 BytesCount = (u8*)BufferEnd - BufferStart;
|
||||
for (u32 i = 0; i < BytesCount; i++)
|
||||
{
|
||||
u8 At = BufferStart[i];
|
||||
#if 0
|
||||
// Cameron's Version
|
||||
CRC = UART_CRCTable[(CRC ^ At) & 0x0F] ^ (CRC >> 4);
|
||||
CRC = UART_CRCTable[(CRC ^ (At >> 4)) & 0x0F] ^ (CRC >> 4);
|
||||
#else
|
||||
// https://github.com/simap/pixelblaze_output_expander/blob/master/firmware/Core/Src/uart.c
|
||||
u32 TableIndex = (CRC ^ At) & 0xFF;
|
||||
CRC = (UART_CRCTable[TableIndex] ^ (CRC >> 8)) & 0xFFFFFFFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
CRC = CRC ^ 0xFFFFFFFF;
|
||||
return CRC;
|
||||
}
|
||||
|
||||
internal void
|
||||
UART_FillFooter(uart_footer* Footer, u8* BufferStart)
|
||||
{
|
||||
Footer->CRC = UART_CalculateCRC(BufferStart, (u8*)Footer);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_UART_H
|
||||
#endif // FOLDHAUS_UART_H
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// File: userspace.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-30
|
||||
//
|
||||
#ifndef USERSPACE_CPP
|
||||
|
||||
internal void
|
||||
US_LoadPatterns(user_space_desc* Desc, app_state* State, context Context)
|
||||
{
|
||||
if (Desc->LoadPatterns)
|
||||
{
|
||||
Desc->LoadPatterns(State);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
US_CustomInit(user_space_desc* Desc, app_state* State, context Context)
|
||||
{
|
||||
if (Desc->CustomInit)
|
||||
{
|
||||
Desc->UserData = Desc->CustomInit(State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
US_CustomUpdate(user_space_desc* Desc, app_state* State, context* Context)
|
||||
{
|
||||
if (Desc->CustomUpdate)
|
||||
{
|
||||
Desc->CustomUpdate(Desc->UserData, State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
US_CustomDebugUI(user_space_desc* Desc, panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer,
|
||||
app_state* State, context Context)
|
||||
{
|
||||
if (Desc->CustomDebugUI)
|
||||
{
|
||||
Desc->CustomDebugUI(Desc->UserData, Panel, PanelBounds, RenderBuffer, State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
US_CustomCleanup(user_space_desc* Desc, app_state* State, context Context)
|
||||
{
|
||||
if (Desc->CustomCleanup)
|
||||
{
|
||||
Desc->CustomCleanup(Desc->UserData, State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define USERSPACE_CPP
|
||||
#endif // USERSPACE_CPP
|
|
@ -1,35 +0,0 @@
|
|||
//
|
||||
// File: userspace.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-30
|
||||
//
|
||||
#ifndef USERSPACE_H
|
||||
|
||||
#define US_LOAD_PATTERNS(name) void name(app_state* State)
|
||||
typedef US_LOAD_PATTERNS(us_load_patterns_proc);
|
||||
|
||||
#define US_CUSTOM_INIT(name) gs_data name (app_state* State, context Context)
|
||||
typedef US_CUSTOM_INIT(us_custom_init_proc);
|
||||
|
||||
#define US_CUSTOM_UPDATE(name) void name(gs_data UserData, app_state* State, context* Context)
|
||||
typedef US_CUSTOM_UPDATE(us_custom_update_proc);
|
||||
|
||||
#define US_CUSTOM_DEBUG_UI(name) void name(gs_data UserData, panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
typedef US_CUSTOM_DEBUG_UI(us_custom_debug_ui);
|
||||
|
||||
#define US_CUSTOM_CLEANUP(name) void name(gs_data UserData, app_state* State, context Context)
|
||||
typedef US_CUSTOM_CLEANUP(us_custom_cleanup_proc);
|
||||
|
||||
typedef struct user_space_desc
|
||||
{
|
||||
us_load_patterns_proc* LoadPatterns;
|
||||
us_custom_init_proc* CustomInit;
|
||||
us_custom_update_proc* CustomUpdate;
|
||||
us_custom_debug_ui* CustomDebugUI;
|
||||
us_custom_cleanup_proc* CustomCleanup;
|
||||
|
||||
gs_data UserData;
|
||||
} user_space_desc;
|
||||
|
||||
#define USERSPACE_H
|
||||
#endif // USERSPACE_H
|
|
@ -1,200 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_app.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_APP_CPP
|
||||
|
||||
#include "foldhaus_platform.h"
|
||||
#include "foldhaus_app.h"
|
||||
|
||||
RELOAD_STATIC_DATA(ReloadStaticData)
|
||||
{
|
||||
GlobalDebugServices = DebugServices;
|
||||
GlobalLogBuffer = LogBuffer;
|
||||
if (AppReady)
|
||||
{
|
||||
app_state* State = (app_state*)Context.MemoryBase;
|
||||
State->PanelSystem.PanelDefs = GlobalPanelDefs;
|
||||
State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount;
|
||||
|
||||
gs_data UserData = State->UserSpaceDesc.UserData;
|
||||
State->UserSpaceDesc = BlumenLumen_UserSpaceCreate();
|
||||
if (UserData.Memory && !State->UserSpaceDesc.UserData.Memory)
|
||||
{
|
||||
State->UserSpaceDesc.UserData = UserData;
|
||||
}
|
||||
US_LoadPatterns(&State->UserSpaceDesc, State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
INITIALIZE_APPLICATION(InitializeApplication)
|
||||
{
|
||||
Context->MemorySize = sizeof(app_state);
|
||||
Context->MemoryBase = Alloc(Context->ThreadContext.Allocator, Context->MemorySize, "Memory Base");
|
||||
app_state* State = (app_state*)Context->MemoryBase;
|
||||
*State = {};
|
||||
|
||||
State->Permanent = MemoryArenaCreate(MB(4), Bytes(8), Context->ThreadContext.Allocator,0, 0, "Permanent");
|
||||
State->Transient = Context->ThreadContext.Transient;
|
||||
State->Assemblies = AssemblyArray_Create(8, &State->Permanent);
|
||||
|
||||
State->CommandQueue = CommandQueue_Create(&State->Permanent, 32);
|
||||
|
||||
animation_system_desc AnimSysDesc = {};
|
||||
AnimSysDesc.Storage = &State->Permanent;
|
||||
AnimSysDesc.AnimArrayCount = 32;
|
||||
AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f;
|
||||
State->AnimationSystem = AnimationSystem_Init(AnimSysDesc);
|
||||
|
||||
if (!Context->Headless)
|
||||
{
|
||||
interface_config IConfig = {0};
|
||||
IConfig.FontSize = 14;
|
||||
IConfig.PanelBG = v4{ .3f, .3f, .3f, 1.f };
|
||||
IConfig.ButtonColor_Inactive = BlackV4;
|
||||
IConfig.ButtonColor_Active = v4{ .1f, .1f, .1f, 1.f };
|
||||
IConfig.ButtonColor_Selected = v4{ .3f, .3f, .3f, 1.f };
|
||||
IConfig.TextColor = WhiteV4;
|
||||
IConfig.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f };
|
||||
IConfig.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f };
|
||||
IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f };
|
||||
IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f };
|
||||
IConfig.Margin = v2{5, 5};
|
||||
State->Interface = ui_InterfaceCreate(*Context, IConfig, &State->Permanent);
|
||||
|
||||
PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent);
|
||||
|
||||
}
|
||||
|
||||
State->SACN = SACN_Initialize(*Context);
|
||||
|
||||
State->LedSystem = LedSystem_Create(Context->ThreadContext.Allocator, 128);
|
||||
State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent);
|
||||
State->AssemblyDebugState.Brightness = 255;
|
||||
State->AssemblyDebugState.Override = ADS_Override_None;
|
||||
|
||||
State->Modes = OperationModeSystemInit(&State->Permanent, Context->ThreadContext);
|
||||
|
||||
ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, true);
|
||||
US_CustomInit(&State->UserSpaceDesc, State, *Context);
|
||||
|
||||
if (!Context->Headless)
|
||||
{
|
||||
// NOTE(pjs): This just sets up the default panel layout
|
||||
panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, *Context);
|
||||
SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, *Context);
|
||||
|
||||
panel* AnimPanel = RootPanel->Bottom;
|
||||
Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, *Context);
|
||||
|
||||
panel* TopPanel = RootPanel->Top;
|
||||
SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context);
|
||||
|
||||
panel* LeftPanel = TopPanel->Left;
|
||||
SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context);
|
||||
|
||||
panel* Profiler = LeftPanel->Right;
|
||||
Panel_SetType(Profiler, &State->PanelSystem, PanelType_MessageLog, State, *Context);
|
||||
|
||||
panel* Hierarchy = LeftPanel->Left;
|
||||
Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, *Context);
|
||||
|
||||
}
|
||||
|
||||
State->RunEditor = !Context->Headless;
|
||||
}
|
||||
|
||||
internal void
|
||||
BuildAssemblyData (app_state* State, context Context, addressed_data_buffer_list* OutputData)
|
||||
{
|
||||
|
||||
#define SEND_DATA
|
||||
#ifdef SEND_DATA
|
||||
// NOTE(pjs): Building data buffers to be sent out to the sculpture
|
||||
// This array is used on the platform side to actually send the information
|
||||
assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient);
|
||||
assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient);
|
||||
SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem);
|
||||
UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient);
|
||||
#endif
|
||||
}
|
||||
|
||||
UPDATE_AND_RENDER(UpdateAndRender)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
app_state* State = (app_state*)Context->MemoryBase;
|
||||
|
||||
// NOTE(Peter): We do this at the beginning because all the render commands are stored in Transient,
|
||||
// and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't
|
||||
// zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically
|
||||
// incorrect to clear the arena, and then access the memory later.
|
||||
MemoryArenaClear(State->Transient);
|
||||
Assert(State->UserSpaceDesc.UserData.Memory != 0);
|
||||
|
||||
if (State->RunEditor)
|
||||
{
|
||||
Editor_Update(State, Context, InputQueue);
|
||||
}
|
||||
|
||||
AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime);
|
||||
if (AnimationSystem_NeedsRender(State->AnimationSystem))
|
||||
{
|
||||
Assert(State->UserSpaceDesc.UserData.Memory != 0);
|
||||
AnimationSystem_RenderToLedBuffers(&State->AnimationSystem,
|
||||
State->Assemblies,
|
||||
&State->LedSystem,
|
||||
State->Patterns,
|
||||
State->Transient,
|
||||
*Context,
|
||||
State->UserSpaceDesc.UserData.Memory);
|
||||
}
|
||||
|
||||
Assert(State->UserSpaceDesc.UserData.Memory != 0);
|
||||
US_CustomUpdate(&State->UserSpaceDesc, State, Context);
|
||||
Assert(State->UserSpaceDesc.UserData.Memory != 0);
|
||||
|
||||
AssemblyDebug_OverrideOutput(State->AssemblyDebugState,
|
||||
State->Assemblies,
|
||||
State->LedSystem);
|
||||
|
||||
if (State->RunEditor)
|
||||
{
|
||||
Editor_Render(State, Context, RenderBuffer);
|
||||
}
|
||||
ResetWorkQueue(Context->GeneralWorkQueue);
|
||||
|
||||
Assert(State->UserSpaceDesc.UserData.Memory != 0);
|
||||
BuildAssemblyData(State, *Context, OutputData);
|
||||
|
||||
// NOTE(PS): We introduced this in order to test some things on the
|
||||
// blumen lumen circuit boards, to see if they were getting out
|
||||
// of sync
|
||||
if (State->SendEmptyPackets) {
|
||||
for (addressed_data_buffer* At = OutputData->Root;
|
||||
At != 0;
|
||||
At = At->Next)
|
||||
{
|
||||
ZeroMemoryBlock(At->Memory, At->MemorySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLEANUP_APPLICATION(CleanupApplication)
|
||||
{
|
||||
app_state* State = (app_state*)Context.MemoryBase;
|
||||
|
||||
for (u32 i = 0; i < State->Assemblies.Count; i++)
|
||||
{
|
||||
assembly Assembly = State->Assemblies.Values[i];
|
||||
led_buffer LedBuffer = State->LedSystem.Buffers[Assembly.LedBufferIndex];
|
||||
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
|
||||
}
|
||||
BuildAssemblyData(State, Context, OutputData);
|
||||
|
||||
US_CustomCleanup(&State->UserSpaceDesc, State, Context);
|
||||
SACN_Cleanup(&State->SACN, Context);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_APP_CPP
|
||||
#endif // FOLDHAUS_APP_CPP
|
|
@ -1,128 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_app.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_APP_H
|
||||
|
||||
#include "../meta/gs_meta_include.h"
|
||||
#include "../meta/gs_meta_lexer.h"
|
||||
|
||||
#include "engine/foldhaus_serializer.h"
|
||||
|
||||
#include "../gs_libs/gs_font.h"
|
||||
|
||||
#include "editor/interface.h"
|
||||
|
||||
#include "engine/foldhaus_network_ordering.h"
|
||||
|
||||
#include "engine/assembly/foldhaus_assembly.h"
|
||||
|
||||
#include "ss_blumen_lumen/gfx_math.h"
|
||||
|
||||
#include "engine/assembly/foldhaus_assembly_parser.cpp"
|
||||
#include "engine/assembly/foldhaus_assembly_debug.h"
|
||||
|
||||
#include "engine/sacn/foldhaus_sacn.h"
|
||||
#include "engine/uart/foldhaus_uart.h"
|
||||
#include "engine/uart/foldhaus_uart.cpp"
|
||||
|
||||
typedef struct app_state app_state;
|
||||
|
||||
typedef struct panel panel;
|
||||
|
||||
#include "editor/foldhaus_command_dispatch.h"
|
||||
#include "editor/foldhaus_operation_mode.h"
|
||||
|
||||
// TODO(Peter): something we can do later is to remove all reliance on app_state and context
|
||||
// from foldhaus_pane.h. It should just emit lists of things that the app can iterate over and
|
||||
// perform operations on, like panel_draw_requests = { bounds, panel* } etc.
|
||||
#include "editor/foldhaus_panel.h"
|
||||
|
||||
#include "engine/animation/foldhaus_animation.h"
|
||||
#include "engine/animation/foldhaus_animation_serializer.cpp"
|
||||
#include "engine/animation/foldhaus_animation_renderer.cpp"
|
||||
|
||||
#include "engine/user_space.h"
|
||||
#include "ss_blumen_lumen/phrase_hue_map.h"
|
||||
#include "ss_blumen_lumen/blumen_lumen.h"
|
||||
|
||||
struct app_state
|
||||
{
|
||||
gs_memory_arena Permanent;
|
||||
gs_memory_arena* Transient;
|
||||
|
||||
// Engine
|
||||
//
|
||||
network_protocol NetworkProtocol;
|
||||
streaming_acn SACN;
|
||||
led_system LedSystem;
|
||||
assembly_array Assemblies;
|
||||
assembly_debug_state AssemblyDebugState;
|
||||
animation_system AnimationSystem;
|
||||
animation_pattern_array Patterns;
|
||||
|
||||
// Interface
|
||||
//
|
||||
rect2 WindowBounds;
|
||||
|
||||
operation_mode_system Modes;
|
||||
input_command_queue CommandQueue;
|
||||
|
||||
ui_interface Interface;
|
||||
panel_system PanelSystem;
|
||||
panel* HotPanel;
|
||||
|
||||
user_space_desc UserSpaceDesc;
|
||||
bool ShowingUserSpaceDebug;
|
||||
|
||||
bool RunEditor;
|
||||
bool SendEmptyPackets;
|
||||
};
|
||||
|
||||
internal void OpenColorPicker(app_state* State, v4* Address);
|
||||
|
||||
#include "engine/assembly/foldhaus_assembly.cpp"
|
||||
|
||||
internal assembly*
|
||||
LoadAssembly(gs_const_string Path, app_state* State, context Context)
|
||||
{
|
||||
return LoadAssembly(&State->Assemblies,
|
||||
&State->LedSystem,
|
||||
State->Transient,
|
||||
Context,
|
||||
Path,
|
||||
GlobalLogBuffer);
|
||||
}
|
||||
|
||||
#include "engine/user_space.cpp"
|
||||
|
||||
#include "ss_blumen_lumen/sdf.h"
|
||||
#include "patterns/blumen_patterns.h"
|
||||
#include "ss_blumen_lumen/blumen_lumen.cpp"
|
||||
|
||||
internal void
|
||||
EndCurrentOperationMode(app_state* State)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
#include "editor/panels/foldhaus_panel_types.h"
|
||||
|
||||
#include "editor/panels/foldhaus_panel_file_view.h"
|
||||
#include "editor/panels/foldhaus_panel_sculpture_view.h"
|
||||
#include "editor/panels/foldhaus_panel_profiler.h"
|
||||
#include "editor/panels/foldhaus_panel_dmx_view.h"
|
||||
#include "editor/panels/foldhaus_panel_animation_timeline.h"
|
||||
#include "editor/panels/foldhaus_panel_hierarchy.h"
|
||||
#include "editor/panels/foldhaus_panel_assembly_debug.h"
|
||||
#include "editor/panels/foldhaus_panel_message_log.h"
|
||||
|
||||
#include "editor/panels/foldhaus_panel_types.cpp"
|
||||
|
||||
#include "editor/foldhaus_interface.cpp"
|
||||
#include "editor/foldhaus_editor_draw.h"
|
||||
#include "editor/foldhaus_editor.cpp"
|
||||
|
||||
#define FOLDHAUS_APP_H
|
||||
#endif // FOLDHAUS_APP_H
|
|
@ -1,474 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_debug.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// This file contains profiling capabilities for both the engine and app
|
||||
//
|
||||
#ifndef FOLDHAUS_DEBUG_H
|
||||
|
||||
#define SCOPE_NAME_LENGTH 256
|
||||
struct scope_record
|
||||
{
|
||||
u32 NameHash;
|
||||
s64 StartCycles;
|
||||
s64 EndCycles;
|
||||
s32 CallDepth;
|
||||
};
|
||||
|
||||
struct collated_scope_record
|
||||
{
|
||||
u32 NameHash;
|
||||
s64 TotalCycles;
|
||||
s32 CallCount;
|
||||
|
||||
r32 PercentFrameTime;
|
||||
r32 TotalSeconds;
|
||||
|
||||
r32 AverageSecondsPerCall;
|
||||
};
|
||||
|
||||
#define SCOPE_NAME_BUFFER_LENGTH 128
|
||||
struct scope_name
|
||||
{
|
||||
u32 Hash;
|
||||
gs_string Name;
|
||||
char Buffer[SCOPE_NAME_BUFFER_LENGTH];
|
||||
};
|
||||
|
||||
struct debug_scope_record_list
|
||||
{
|
||||
s32 ThreadId;
|
||||
s32 Max;
|
||||
s32 Count;
|
||||
scope_record* Calls;
|
||||
|
||||
s32 CurrentScopeCallDepth;
|
||||
};
|
||||
|
||||
#define DEBUG_FRAME_GROW_SIZE 8102
|
||||
struct debug_frame
|
||||
{
|
||||
s64 FrameStartCycles;
|
||||
s64 FrameEndCycles;
|
||||
|
||||
s32 ScopeNamesMax;
|
||||
s32 ScopeNamesCount;
|
||||
scope_name* ScopeNamesHash;
|
||||
|
||||
s32 ThreadCount;
|
||||
debug_scope_record_list* ThreadCalls;
|
||||
|
||||
s32 CollatedScopesMax;
|
||||
collated_scope_record* CollatedScopes;
|
||||
};
|
||||
|
||||
enum debug_ui_view
|
||||
{
|
||||
DebugUI_Profiler,
|
||||
DebugUI_ScopeList,
|
||||
DebugUI_MemoryView,
|
||||
|
||||
DebugUI_Count,
|
||||
};
|
||||
|
||||
struct debug_interface
|
||||
{
|
||||
s32 FrameView;
|
||||
};
|
||||
|
||||
typedef s32 debug_get_thread_id();
|
||||
typedef s64 debug_timing_proc();
|
||||
typedef u8* debug_alloc(s32 ElementSize, s32 ElementCount);
|
||||
typedef u8* debug_realloc(u8* Memory, s32 OldSize, s32 NewSize);
|
||||
|
||||
#define HISTOGRAM_DEPTH 10
|
||||
struct debug_histogram_entry
|
||||
{
|
||||
char ScopeName_[SCOPE_NAME_LENGTH];
|
||||
gs_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;
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
# define DEBUG_FRAME_COUNT 128
|
||||
#else
|
||||
# define DEBUG_FRAME_COUNT 0
|
||||
#endif
|
||||
|
||||
struct debug_services
|
||||
{
|
||||
s64 PerformanceCountFrequency;
|
||||
|
||||
b32 RecordFrames;
|
||||
s32 CurrentDebugFrame;
|
||||
debug_frame* Frames;
|
||||
|
||||
debug_interface Interface;
|
||||
|
||||
gs_thread_context Ctx;
|
||||
gs_memory_arena A;
|
||||
|
||||
debug_get_thread_id* GetThreadId;
|
||||
debug_timing_proc* GetWallClock;
|
||||
};
|
||||
|
||||
internal void
|
||||
InitializeDebugFrame (debug_frame* Frame, s32 NameHashMax, s32 ThreadCount, s32 ScopeCallsMax, debug_services* Services)
|
||||
{
|
||||
Frame->ScopeNamesMax = NameHashMax;
|
||||
Frame->ScopeNamesHash = PushArray(&Services->A, scope_name, NameHashMax);
|
||||
|
||||
// NOTE(Peter): We use the same size as scope names because we're only storing a single instance
|
||||
// per scope. If ScopeNamesMax can't hold all the scopes, this will never get filled and
|
||||
// we should assert and recompile with a resized NameHashMax
|
||||
Frame->CollatedScopesMax = NameHashMax;
|
||||
Frame->CollatedScopes = PushArray(&Services->A, collated_scope_record, NameHashMax);
|
||||
|
||||
for (s32 i = 0; i < Frame->ScopeNamesMax; i++)
|
||||
{
|
||||
scope_name* Entry = Frame->ScopeNamesHash + i;
|
||||
Entry->Name = MakeString(Entry->Buffer, 0, SCOPE_NAME_BUFFER_LENGTH);
|
||||
}
|
||||
|
||||
Frame->ThreadCount = ThreadCount;
|
||||
Frame->ThreadCalls = PushArray(&Services->A, debug_scope_record_list, ThreadCount);
|
||||
|
||||
for (s32 i = 0; i < ThreadCount; i++)
|
||||
{
|
||||
Frame->ThreadCalls[i].Max = ScopeCallsMax;
|
||||
Frame->ThreadCalls[i].Count = 0;
|
||||
Frame->ThreadCalls[i].Calls = PushArray(&Services->A, scope_record, ScopeCallsMax);
|
||||
Frame->ThreadCalls[i].CurrentScopeCallDepth = 0;
|
||||
Frame->ThreadCalls[i].ThreadId = 0;
|
||||
}
|
||||
|
||||
for (s32 c = 0; c < Frame->CollatedScopesMax; c++)
|
||||
{
|
||||
Frame->CollatedScopes[c].NameHash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
StartDebugFrame(debug_frame* Frame, debug_services* Services)
|
||||
{
|
||||
Frame->FrameStartCycles = Services->GetWallClock();
|
||||
for (s32 i = 0; i < Frame->ThreadCount; i++)
|
||||
{
|
||||
Frame->ThreadCalls[i].Count = 0;
|
||||
Frame->ThreadCalls[i].CurrentScopeCallDepth = 0;
|
||||
}
|
||||
|
||||
for (s32 c = 0; c < Frame->CollatedScopesMax; c++)
|
||||
{
|
||||
s32 Hash = Frame->CollatedScopes[c].NameHash;
|
||||
Frame->CollatedScopes[c] = {};
|
||||
Frame->CollatedScopes[c].NameHash = Hash;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
InitDebugServices_OffMode (debug_services* Services,
|
||||
s64 PerformanceCountFrequency,
|
||||
debug_timing_proc* GetWallClock,
|
||||
debug_get_thread_id* GetThreadId,
|
||||
gs_thread_context Ctx,
|
||||
s32 ThreadCount)
|
||||
{
|
||||
*Services = {0};
|
||||
|
||||
Services->Ctx = Ctx;
|
||||
Services->GetWallClock = GetWallClock;
|
||||
Services->GetThreadId = GetThreadId;
|
||||
|
||||
Services->RecordFrames = false;
|
||||
Services->Frames = 0;
|
||||
|
||||
Services->CurrentDebugFrame = 0;
|
||||
Services->PerformanceCountFrequency = PerformanceCountFrequency;
|
||||
}
|
||||
|
||||
|
||||
internal void
|
||||
InitDebugServices_DebugMode (debug_services* Services,
|
||||
s64 PerformanceCountFrequency,
|
||||
debug_timing_proc* GetWallClock,
|
||||
debug_get_thread_id* GetThreadId,
|
||||
gs_thread_context Ctx,
|
||||
s32 ThreadCount)
|
||||
{
|
||||
Services->Ctx = Ctx;
|
||||
Services->GetWallClock = GetWallClock;
|
||||
Services->GetThreadId = GetThreadId;
|
||||
|
||||
Services->RecordFrames = true;
|
||||
|
||||
Services->CurrentDebugFrame = 0;
|
||||
Services->A = MemoryArenaCreate(MB(64), Bytes(8), Ctx.Allocator, 0, 0, "Debug Services Allocator");
|
||||
|
||||
s32 NameHashMax = 4096;
|
||||
s32 ScopeCallsMax = 4096;
|
||||
Services->Frames = PushArray(&Services->A, debug_frame, DEBUG_FRAME_COUNT);
|
||||
for (s32 i = 0; i < DEBUG_FRAME_COUNT; i++)
|
||||
{
|
||||
InitializeDebugFrame(&Services->Frames[i], NameHashMax, ThreadCount, ScopeCallsMax, Services);
|
||||
}
|
||||
|
||||
Services->PerformanceCountFrequency = PerformanceCountFrequency;
|
||||
}
|
||||
|
||||
internal debug_frame*
|
||||
GetCurrentDebugFrame (debug_services* Services)
|
||||
{
|
||||
debug_frame* Result = Services->Frames + Services->CurrentDebugFrame;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal debug_frame*
|
||||
GetLastDebugFrame(debug_services* Services)
|
||||
{
|
||||
if (!Services->Frames) return 0;
|
||||
|
||||
s32 Index = (Services->CurrentDebugFrame - 1);
|
||||
if (Index < 0) { Index += DEBUG_FRAME_COUNT; }
|
||||
debug_frame* Result = Services->Frames + Index;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
GetIndexForNameHash(debug_frame* Frame, u32 NameHash)
|
||||
{
|
||||
s32 Result = -1;
|
||||
|
||||
for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++)
|
||||
{
|
||||
u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax;
|
||||
if (Frame->ScopeNamesHash[Index].Hash == NameHash)
|
||||
{
|
||||
Result = Index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(Peter): Its not technically wrong to return a -1 here, just means we didn't find it.
|
||||
// At the time of writing however, this function is only being called in contexts where we
|
||||
// know there should be an entry in the Name table, so a -1 actually indicates a problem.
|
||||
Assert(Result >= 0);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal debug_scope_record_list*
|
||||
GetScopeListForThreadInFrame(debug_services* Services, debug_frame* Frame)
|
||||
{
|
||||
debug_scope_record_list* List = 0;
|
||||
|
||||
s32 CurrentThreadId = Services->GetThreadId();
|
||||
for (s32 Offset = 0; Offset < Frame->ThreadCount; Offset++)
|
||||
{
|
||||
s32 Index = (CurrentThreadId + Offset) % Frame->ThreadCount;
|
||||
if (Frame->ThreadCalls[Index].ThreadId == CurrentThreadId)
|
||||
{
|
||||
List = Frame->ThreadCalls + Index;
|
||||
break;
|
||||
}
|
||||
else if (Frame->ThreadCalls[Index].ThreadId == 0)
|
||||
{
|
||||
Frame->ThreadCalls[Index].ThreadId = CurrentThreadId;
|
||||
List = Frame->ThreadCalls + Index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(List);
|
||||
return List;
|
||||
}
|
||||
|
||||
internal void
|
||||
CollateThreadScopeCalls (debug_scope_record_list* ThreadRecords, debug_frame* Frame)
|
||||
{
|
||||
for (s32 i = 0; i < ThreadRecords->Count; i++)
|
||||
{
|
||||
scope_record Record = ThreadRecords->Calls[i];
|
||||
s32 Index = GetIndexForNameHash (Frame, Record.NameHash);
|
||||
collated_scope_record* CollatedRecord = Frame->CollatedScopes + Index;
|
||||
|
||||
if (CollatedRecord->NameHash != Record.NameHash)
|
||||
{
|
||||
CollatedRecord->NameHash = Record.NameHash;
|
||||
CollatedRecord->TotalCycles = 0;
|
||||
CollatedRecord->CallCount = 0;
|
||||
}
|
||||
|
||||
CollatedRecord->TotalCycles += Record.EndCycles - Record.StartCycles;
|
||||
CollatedRecord->CallCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
EndDebugFrame (debug_services* Services)
|
||||
{
|
||||
debug_frame* ClosingFrame = GetCurrentDebugFrame(Services);
|
||||
ClosingFrame->FrameEndCycles = Services->GetWallClock();
|
||||
|
||||
s64 FrameTotalCycles = ClosingFrame->FrameEndCycles - ClosingFrame->FrameStartCycles;
|
||||
|
||||
for (s32 t = 0; t < ClosingFrame->ThreadCount; t++)
|
||||
{
|
||||
CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame);
|
||||
}
|
||||
|
||||
s32 ScopeNamesCount = 0;
|
||||
for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++)
|
||||
{
|
||||
if (ClosingFrame->ScopeNamesHash[n].Hash != 0)
|
||||
{
|
||||
collated_scope_record* CollatedRecord = ClosingFrame->CollatedScopes + n;
|
||||
CollatedRecord->TotalSeconds = (r32)CollatedRecord->TotalCycles / (r32)Services->PerformanceCountFrequency;
|
||||
CollatedRecord->PercentFrameTime = (r32)CollatedRecord->TotalCycles / (r32)FrameTotalCycles;
|
||||
CollatedRecord->AverageSecondsPerCall = CollatedRecord->TotalSeconds / CollatedRecord->CallCount;
|
||||
ScopeNamesCount += 1;
|
||||
}
|
||||
}
|
||||
ClosingFrame->ScopeNamesCount = ScopeNamesCount;
|
||||
|
||||
s32 FramesCount = DEBUG_FRAME_COUNT;
|
||||
if (FramesCount > 0)
|
||||
{
|
||||
Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % FramesCount;
|
||||
}
|
||||
StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services);
|
||||
}
|
||||
|
||||
internal u32
|
||||
HashScopeName (char* ScopeName)
|
||||
{
|
||||
// djb2 hash
|
||||
u32 Hash = 5381;
|
||||
char* C = ScopeName;
|
||||
while(*C)
|
||||
{
|
||||
Hash = ((Hash << 5) + Hash) + *C;
|
||||
C++;
|
||||
}
|
||||
return Hash;
|
||||
}
|
||||
|
||||
internal scope_name*
|
||||
GetOrAddNameHashEntry(debug_frame* Frame, u32 NameHash)
|
||||
{
|
||||
scope_name* Result = 0;
|
||||
|
||||
for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++)
|
||||
{
|
||||
u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax;
|
||||
if ((Frame->ScopeNamesHash[Index].Hash == 0) || (Frame->ScopeNamesHash[Index].Hash == NameHash))
|
||||
{
|
||||
Result = Frame->ScopeNamesHash + Index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
BeginTrackingScopeAndGetNameHash (debug_services* Services, char* ScopeName)
|
||||
{
|
||||
debug_frame* CurrentFrame = GetCurrentDebugFrame(Services);
|
||||
debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame);
|
||||
|
||||
ThreadList->CurrentScopeCallDepth++;
|
||||
|
||||
u32 NameHash = HashScopeName(ScopeName);
|
||||
scope_name* Entry = GetOrAddNameHashEntry(CurrentFrame, NameHash);
|
||||
if (Entry->Hash == 0) // If its new
|
||||
{
|
||||
Entry->Hash = NameHash;
|
||||
// TODO(Peter): need to initialize all entry name gs_strings to point at the buffer
|
||||
// This will break eventually. when it does, do this ^^^^ when on startup
|
||||
PrintF(&Entry->Name, "%s", ScopeName);
|
||||
}
|
||||
|
||||
return NameHash;
|
||||
}
|
||||
|
||||
internal void
|
||||
PushScopeTimeOnFrame (debug_services* Services, s32 NameHash, u64 StartCycles, u64 EndCycles)
|
||||
{
|
||||
debug_frame* CurrentFrame = GetCurrentDebugFrame(Services);
|
||||
debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame);
|
||||
|
||||
if (ThreadList->Count >= ThreadList->Max)
|
||||
{
|
||||
s32 NewMax = (ThreadList->Max + DEBUG_FRAME_GROW_SIZE);
|
||||
FreeArray(Services->Ctx.Allocator, ThreadList->Calls, scope_record, ThreadList->Max);
|
||||
ThreadList->Calls = AllocArray(Services->Ctx.Allocator, scope_record, NewMax, "Debug Frame");
|
||||
ThreadList->Max = NewMax;
|
||||
}
|
||||
|
||||
Assert(ThreadList->Count < ThreadList->Max);
|
||||
|
||||
s32 EntryIndex = ThreadList->Count++;
|
||||
scope_record* Record = ThreadList->Calls + EntryIndex;
|
||||
Record->NameHash = NameHash;
|
||||
Record->StartCycles = StartCycles;
|
||||
Record->EndCycles = EndCycles;
|
||||
Record->CallDepth = --ThreadList->CurrentScopeCallDepth;
|
||||
}
|
||||
|
||||
internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFrequency)
|
||||
{
|
||||
r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
#define DEBUG_TRACK_FUNCTION scope_tracker ScopeTracker ((char*)__func__, 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;
|
||||
u32 ScopeNameHash;
|
||||
debug_services* DebugServices;
|
||||
|
||||
scope_tracker(char* ScopeName, debug_services* DebugServices)
|
||||
{
|
||||
if (DebugServices->RecordFrames)
|
||||
{
|
||||
this->ScopeNameHash = BeginTrackingScopeAndGetNameHash(DebugServices, ScopeName);
|
||||
this->ScopeStart = DebugServices->GetWallClock();
|
||||
this->DebugServices = DebugServices;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->DebugServices = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~scope_tracker()
|
||||
{
|
||||
if (this->DebugServices) // NOTE(Peter): If DebugServices == 0, then we werent' recording this frame
|
||||
{
|
||||
s64 ScopeEnd = this->DebugServices->GetWallClock();
|
||||
PushScopeTimeOnFrame(this->DebugServices, this->ScopeNameHash, this->ScopeStart, ScopeEnd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define FOLDHAUS_DEBUG_H
|
||||
#endif // FOLDHAUS_DEBUG_H
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_log.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-02-05
|
||||
//
|
||||
#ifndef FOLDHAUS_LOG_H
|
||||
|
||||
enum log_entry_type
|
||||
{
|
||||
LogEntry_Message,
|
||||
LogEntry_Error,
|
||||
};
|
||||
|
||||
struct log_entry
|
||||
{
|
||||
gs_string Message;
|
||||
log_entry_type Type;
|
||||
};
|
||||
|
||||
#define LOG_ENTRIES_MAX 256
|
||||
struct event_log
|
||||
{
|
||||
log_entry Entries[LOG_ENTRIES_MAX];
|
||||
// Circular buffer head position
|
||||
u32 NextEntry;
|
||||
};
|
||||
|
||||
#define LogMessage(_Log, _Message) PushLogEntry(_Log, MakeString(_Message), LogEntry_Message)
|
||||
#define LogError(_Log, _Message) PushLogEntry(_Log, MakeString(_Message), LogEntry_Error)
|
||||
|
||||
internal void
|
||||
PushLogEntry(event_log* Log, gs_string Message, log_entry_type Type)
|
||||
{
|
||||
u32 NewLogIndex = Log->NextEntry++;
|
||||
if (Log->NextEntry >= LOG_ENTRIES_MAX)
|
||||
{
|
||||
Log->NextEntry = 0;
|
||||
}
|
||||
|
||||
log_entry* NewEntry = Log->Entries + NewLogIndex;
|
||||
NewEntry->Message = Message;
|
||||
NewEntry->Type = Type;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_LOG_H
|
||||
#endif // FOLDHAUS_LOG_H
|
|
@ -1,255 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_platform.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_PLATFORM_H
|
||||
|
||||
// TODO Remove
|
||||
#include <math.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "..\gs_libs\gs_types.h"
|
||||
#include "..\gs_libs\gs_types.cpp"
|
||||
#include "..\gs_libs\gs_path.h"
|
||||
|
||||
struct handle
|
||||
{
|
||||
u32 Generation;
|
||||
u32 Index;
|
||||
};
|
||||
|
||||
inline bool
|
||||
Handle_IsValid(handle Handle)
|
||||
{
|
||||
bool Result = (Handle.Generation != 0);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#include "..\gs_libs\gs_string.h"
|
||||
#include "..\gs_libs\gs_csv.h"
|
||||
|
||||
#include "engine/foldhaus_log.h"
|
||||
global log_buffer* GlobalLogBuffer;
|
||||
|
||||
#include "foldhaus_debug.h"
|
||||
global debug_services* GlobalDebugServices;
|
||||
|
||||
//#include "..\gs_libs\gs_vector_matrix.h"
|
||||
#include "..\gs_libs\gs_input.h"
|
||||
|
||||
struct platform_network_address
|
||||
{
|
||||
s32 Family;
|
||||
u16 Port;
|
||||
u32 Address;
|
||||
};
|
||||
|
||||
typedef s32 platform_socket_handle;
|
||||
typedef s32 platform_network_address_handle;
|
||||
|
||||
#include "foldhaus_renderer.h"
|
||||
#include "engine/foldhaus_addressed_data.h"
|
||||
|
||||
typedef struct context context;
|
||||
|
||||
// Application Functions
|
||||
|
||||
// TODO(pjs): TEMP
|
||||
typedef void temp_job_req_proc(gs_thread_context* Ctx, u8* Memory);
|
||||
struct temp_job_req
|
||||
{
|
||||
temp_job_req_proc* Proc;
|
||||
u8* Memory;
|
||||
};
|
||||
// This isn't necessarily temp but I'm not sure it goes here
|
||||
#define PACKETS_MAX 32
|
||||
struct packet_ringbuffer
|
||||
{
|
||||
gs_data Values[PACKETS_MAX];
|
||||
u32 ReadHead;
|
||||
u32 WriteHead;
|
||||
};
|
||||
|
||||
#define INITIALIZE_APPLICATION(name) void name(context* Context)
|
||||
typedef INITIALIZE_APPLICATION(initialize_application);
|
||||
|
||||
#define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer, addressed_data_buffer_list* OutputData)
|
||||
typedef UPDATE_AND_RENDER(update_and_render);
|
||||
|
||||
#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices, log_buffer* LogBuffer, bool AppReady)
|
||||
typedef RELOAD_STATIC_DATA(reload_static_data);
|
||||
|
||||
#define CLEANUP_APPLICATION(name) void name(context Context, addressed_data_buffer_list* OutputData)
|
||||
typedef CLEANUP_APPLICATION(cleanup_application);
|
||||
|
||||
// Platform Functions
|
||||
|
||||
struct window_info
|
||||
{
|
||||
char* Name;
|
||||
s32 Width;
|
||||
s32 Height;
|
||||
};
|
||||
|
||||
typedef struct window window;
|
||||
|
||||
#define PLATFORM_MEMORY_NO_ERROR 0
|
||||
enum platform_memory_error
|
||||
{
|
||||
PlatformMemory_NoError,
|
||||
PlatformMemory_FileNotFound,
|
||||
|
||||
PlatformMemory_UnknownError, // You should implement handling this when you see it
|
||||
};
|
||||
|
||||
struct data
|
||||
{
|
||||
u8* Base;
|
||||
u64 Size;
|
||||
};
|
||||
|
||||
struct platform_memory_result
|
||||
{
|
||||
data Data;
|
||||
platform_memory_error Error;
|
||||
};
|
||||
|
||||
struct system_path
|
||||
{
|
||||
char* Path;
|
||||
s32 PathLength;
|
||||
s32 IndexOfLastSlash;
|
||||
};
|
||||
|
||||
struct texture_buffer
|
||||
{
|
||||
u8* Memory;
|
||||
s32 Width;
|
||||
s32 Height;
|
||||
s32 Pitch;
|
||||
s32 BytesPerPixel;
|
||||
};
|
||||
|
||||
#define PLATFORM_GET_GPU_TEXTURE_HANDLE(name) s32 name(u8* Memory, s32 Width, s32 Height)
|
||||
typedef PLATFORM_GET_GPU_TEXTURE_HANDLE(platform_get_gpu_texture_handle);
|
||||
|
||||
#define PLATFORM_GET_SOCKET_HANDLE(name) platform_socket_handle name(s32 Multicast_TimeToLive)
|
||||
typedef PLATFORM_GET_SOCKET_HANDLE(platform_get_socket_handle);
|
||||
|
||||
// Font
|
||||
struct platform_font_info
|
||||
{
|
||||
s32 PixelHeight;
|
||||
s32 Ascent, Descent, Leading;
|
||||
s32 MaxCharWidth;
|
||||
s32 CodepointStart;
|
||||
s32 CodepointOnePastLast;
|
||||
};
|
||||
|
||||
enum font_weight
|
||||
{
|
||||
FontWeight_Invalid = 0,
|
||||
FontWeight_Thin = 100,
|
||||
FontWeight_ExtraLight = 200,
|
||||
FontWeight_Light = 300,
|
||||
FontWeight_Normal = 400,
|
||||
FontWeight_Medium = 500,
|
||||
FontWeight_SemiBold = 600,
|
||||
FontWeight_Bold = 700,
|
||||
FontWeight_ExtraBold = 800,
|
||||
FontWeight_Heavy = 900,
|
||||
};
|
||||
|
||||
#define GET_FONT_INFO(name) platform_font_info name(char* FontName, s32 PixelHeight, font_weight FontWeight, b32 Italic, b32 Underline, b32 Strikeout)
|
||||
typedef GET_FONT_INFO(platform_get_font_info);
|
||||
|
||||
#define DRAW_FONT_CODEPOINT(name) void name(u8* DestBuffer, s32 DestBufferWidth, s32 DestBufferHeight, u32 XOffset, u32 YOffset, char Codepoint, platform_font_info FontInfo, u32* OutWidth, u32* OutHeight)
|
||||
typedef DRAW_FONT_CODEPOINT(platform_draw_font_codepoint);
|
||||
|
||||
// Worker Threads
|
||||
|
||||
#define PLATFORM_THREAD_COUNT 3
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Time
|
||||
|
||||
internal r32
|
||||
GetSecondsElapsed (s64 Start, s64 End, s64 PerformanceCountFrequency)
|
||||
{
|
||||
r32 Result = ((r32)(End - Start) / (r32) PerformanceCountFrequency);
|
||||
return Result;
|
||||
}
|
||||
|
||||
typedef struct system_time
|
||||
{
|
||||
u64 NanosSinceEpoch;
|
||||
|
||||
s32 Year;
|
||||
s32 Month;
|
||||
s32 Day;
|
||||
s32 Hour; // [0:23]
|
||||
s32 Minute;
|
||||
s32 Second;
|
||||
} system_time;
|
||||
|
||||
internal r64
|
||||
SecondsElapsed(system_time Start, system_time End)
|
||||
{
|
||||
u64 N = End.NanosSinceEpoch - Start.NanosSinceEpoch;
|
||||
r64 S = (r64)N * NanosToSeconds;
|
||||
return S;
|
||||
}
|
||||
|
||||
struct context
|
||||
{
|
||||
gs_thread_context ThreadContext;
|
||||
|
||||
u8* MemoryBase;
|
||||
u32 MemorySize;
|
||||
|
||||
b32 WindowIsVisible;
|
||||
rect2 WindowBounds;
|
||||
r64 TotalTime;
|
||||
r32 DeltaTime;
|
||||
mouse_state Mouse;
|
||||
|
||||
// Application Services
|
||||
initialize_application* InitializeApplication;
|
||||
reload_static_data* ReloadStaticData;
|
||||
update_and_render* UpdateAndRender;
|
||||
cleanup_application* CleanupApplication;
|
||||
|
||||
platform_thread_manager* ThreadManager;
|
||||
platform_socket_manager* SocketManager;
|
||||
|
||||
// Platform Services
|
||||
gs_work_queue* GeneralWorkQueue;
|
||||
|
||||
platform_get_gpu_texture_handle* PlatformGetGPUTextureHandle;
|
||||
platform_get_font_info* PlatformGetFontInfo;
|
||||
platform_draw_font_codepoint* PlatformDrawFontCodepoint;
|
||||
|
||||
platform_get_socket_handle* PlatformGetSocketHandle;
|
||||
|
||||
system_time SystemTime_Last;
|
||||
system_time SystemTime_Current;
|
||||
|
||||
//
|
||||
bool Headless;
|
||||
};
|
||||
|
||||
#define FOLDHAUS_PLATFORM_H
|
||||
#endif // FOLDHAUS_PLATFORM_H
|
|
@ -1,198 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_renderer.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_RENDERER_CPP
|
||||
|
||||
internal render_command_buffer
|
||||
AllocateRenderCommandBuffer (u8* Memory, s32 Size, gs_thread_context Ctx)
|
||||
{
|
||||
render_command_buffer Result = {};
|
||||
Result.CommandMemory = Memory;
|
||||
Result.CommandMemorySize = Size;
|
||||
Result.CommandMemoryUsed = 0;
|
||||
Result.Ctx = Ctx;
|
||||
return Result;
|
||||
}
|
||||
internal render_command_buffer
|
||||
AllocateRenderCommandBuffer(u32 MemorySize,
|
||||
gs_memory_arena* Arena,
|
||||
gs_thread_context Ctx)
|
||||
{
|
||||
u8* Memory = PushSize(Arena, MemorySize).Memory;
|
||||
return AllocateRenderCommandBuffer(Memory, MemorySize, Ctx);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
#else
|
||||
OpenGLRenderTriBuffer((u8*)Vertecies, 4, (u8*)UVs, 2, (u8*)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, (u8*)UVs, 2, (u8*)Colors, 4, QuadCount * 2 * 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderCommandBuffer (render_command_buffer CommandBuffer)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
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:
|
||||
{
|
||||
DEBUG_TRACK_SCOPE(SetRenderMode);
|
||||
|
||||
render_command_set_render_mode* Command = (render_command_set_render_mode*)(CommandHeader + 1);
|
||||
|
||||
glViewport(Command->ViewOffsetX, Command->ViewOffsetY,
|
||||
Command->ViewWidth, Command->ViewHeight);
|
||||
|
||||
LoadModelView(Command->ModelView.Array);
|
||||
LoadProjection(Command->Projection.Array);
|
||||
|
||||
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:
|
||||
{
|
||||
DEBUG_TRACK_SCOPE(RendererClearScreen);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_RENDERER_CPP
|
||||
#endif // FOLDHAUS_RENDERER_CPP
|
|
@ -1,719 +0,0 @@
|
|||
//
|
||||
// File: foldhaus_renderer.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef FOLDHAUS_RENDERER_H
|
||||
|
||||
#define IMMEDIATE_MODE_RENDERING 0
|
||||
|
||||
struct camera
|
||||
{
|
||||
r32 FieldOfView;
|
||||
r32 AspectRatio;
|
||||
r32 Near, Far;
|
||||
v3 Position;
|
||||
v3 LookAt;
|
||||
};
|
||||
|
||||
inline m44
|
||||
GetCameraModelViewMatrix (camera Camera)
|
||||
{
|
||||
m44 RotationMatrix = M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt));
|
||||
m44 PositionMatrix = M44Translation(ToV4Point(-Camera.Position));
|
||||
m44 ModelViewMatrix = RotationMatrix * PositionMatrix;
|
||||
return ModelViewMatrix;
|
||||
}
|
||||
|
||||
inline m44
|
||||
GetCameraPerspectiveProjectionMatrix(camera Camera)
|
||||
{
|
||||
m44 Result = M44ProjectionPerspective(Camera.FieldOfView, Camera.AspectRatio, Camera.Near, Camera.Far);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal m44
|
||||
GetCameraMatrix(camera Camera)
|
||||
{
|
||||
m44 ModelView = GetCameraModelViewMatrix(Camera);
|
||||
m44 Projection = GetCameraPerspectiveProjectionMatrix(Camera);
|
||||
m44 Result = Projection * ModelView;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal v2
|
||||
ProjectWorldPointToScreen(v4 WorldSpacePoint, camera Camera, rect2 WindowBounds)
|
||||
{
|
||||
v2 WindowExtents = v2{Rect2Width(WindowBounds), Rect2Height(WindowBounds)};
|
||||
v4 ProjectedPosition = GetCameraMatrix(Camera) * WorldSpacePoint;
|
||||
ProjectedPosition.xyz /= ProjectedPosition.w;
|
||||
v2 ScreenPosition = V2MultiplyPairwise(ProjectedPosition.xy, (WindowExtents / 2)) + (WindowExtents / 2);
|
||||
|
||||
return ScreenPosition;
|
||||
}
|
||||
|
||||
internal v4_ray
|
||||
ProjectScreenPointToWorldRay(v2 ScreenPoint, camera Camera, rect2 WindowBounds)
|
||||
{
|
||||
v4_ray Result = {0};
|
||||
|
||||
r32 TanFOVOverTwo = TanR32(DegToRadR32(Camera.FieldOfView / 2.0f));
|
||||
r32 Aspect = RectAspectRatio(WindowBounds);
|
||||
|
||||
r32 NormalizedX = ScreenPoint.x / Rect2Width(WindowBounds);
|
||||
r32 NormalizedY = ScreenPoint.y / Rect2Height(WindowBounds);
|
||||
|
||||
r32 CenteredX = (2.0f * NormalizedX) - 1.0f;
|
||||
r32 CenteredY = (2.0f * NormalizedY) - 1.0f;
|
||||
|
||||
r32 ScaledX = CenteredX * Aspect;
|
||||
r32 ScaledY = CenteredY;
|
||||
|
||||
r32 CameraX = ScaledX * TanFOVOverTwo;
|
||||
r32 CameraY = ScaledY * TanFOVOverTwo;
|
||||
|
||||
r32 Near = Camera.Near;
|
||||
r32 Far = Camera.Far;
|
||||
v3 MousePointOnNearPlane = v3{CameraX, CameraY, -1} * Near;
|
||||
v3 MousePointOnFarPlane = v3{CameraX, CameraY, -1} * Far;
|
||||
|
||||
v4 MouseRayDirection = ToV4Vec(V3Normalize(MousePointOnFarPlane - MousePointOnNearPlane));
|
||||
m44 CameraTransform = M44Transpose(M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt)));
|
||||
|
||||
Result.Origin = ToV4Point(Camera.Position);
|
||||
Result.Direction = CameraTransform * MouseRayDirection;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
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 ViewOffsetX, ViewOffsetY;
|
||||
r32 ViewWidth, ViewHeight;
|
||||
b32 UseDepthBuffer;
|
||||
};
|
||||
|
||||
typedef u8* renderer_realloc(u8* Base, s32 CurrentSize, s32 NewSize);
|
||||
|
||||
#define COMMAND_BUFFER_MIN_GROW_SIZE MB(2)
|
||||
|
||||
struct render_command_buffer
|
||||
{
|
||||
u8* CommandMemory;
|
||||
s32 CommandMemoryUsed;
|
||||
s32 CommandMemorySize;
|
||||
|
||||
gs_thread_context Ctx;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
internal void
|
||||
ResizeBufferIfNecessary(render_command_buffer* Buffer, s32 DataSize)
|
||||
{
|
||||
if (Buffer->CommandMemoryUsed + DataSize > Buffer->CommandMemorySize)
|
||||
{
|
||||
// NOTE(Peter): If this becomes a problem just go back to the original solution of
|
||||
// NewSize = Buffer->CommandMemorySize + (2 * DataSize);
|
||||
s32 SpaceAvailable = Buffer->CommandMemorySize - Buffer->CommandMemoryUsed;
|
||||
s32 SpaceNeeded = DataSize - SpaceAvailable; // This is known to be positive at this point
|
||||
s32 AdditionSize = Max(SpaceNeeded, COMMAND_BUFFER_MIN_GROW_SIZE);
|
||||
s32 NewSize = Buffer->CommandMemorySize + AdditionSize;
|
||||
|
||||
Free(Buffer->Ctx.Allocator, Buffer->CommandMemory, Buffer->CommandMemorySize);
|
||||
Buffer->CommandMemory = Alloc(Buffer->Ctx.Allocator, NewSize, "Renderer");
|
||||
Buffer->CommandMemorySize = NewSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Batch
|
||||
|
||||
internal s32
|
||||
PushQuad3DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, u8* MemStart, s32 TriCount, s32 DataSize, b32 UseIntegerColor = false)
|
||||
{
|
||||
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, s32 DataSize, u8* MemStart)
|
||||
{
|
||||
ZeroMemoryBlock(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;
|
||||
}
|
||||
|
||||
struct quad_batch_constructor_reserved_range
|
||||
{
|
||||
s32 Start;
|
||||
s32 OnePastLast;
|
||||
};
|
||||
|
||||
internal quad_batch_constructor_reserved_range
|
||||
ReserveRangeInQuadConstructor(render_quad_batch_constructor* Constructor, s32 TrisNeeded)
|
||||
{
|
||||
quad_batch_constructor_reserved_range Result = {};
|
||||
Result.OnePastLast = Constructor->Count + TrisNeeded;
|
||||
Constructor->Count = Result.OnePastLast;
|
||||
Result.Start = Result.OnePastLast - TrisNeeded;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal quad_batch_constructor_reserved_range
|
||||
ThreadSafeReserveRangeInQuadConstructor(render_quad_batch_constructor* Constructor, s32 TrisNeeded)
|
||||
{
|
||||
quad_batch_constructor_reserved_range Result = {};
|
||||
Result.OnePastLast = InterlockedAdd((long*)&Constructor->Count, TrisNeeded);
|
||||
Result.Start = Result.OnePastLast - TrisNeeded;
|
||||
return Result;
|
||||
}
|
||||
|
||||
inline void
|
||||
SetTri3DInBatch (render_quad_batch_constructor* Constructor, s32 TriIndex,
|
||||
v4 P0, v4 P1, v4 P2,
|
||||
v2 UV0, v2 UV1, v2 UV2,
|
||||
v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
//Assert(P0.w != 0 && P1.w != 0 && P2.w != 0); // Passing vectors, rather than positions. Will draw wrong
|
||||
|
||||
// Vertecies
|
||||
Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 0)] = P0;
|
||||
Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 1)] = P1;
|
||||
Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 2)] = P2;
|
||||
|
||||
// UVs
|
||||
Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 0)] = UV0;
|
||||
Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 1)] = UV1;
|
||||
Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 2)] = UV1;
|
||||
|
||||
// Color V0
|
||||
Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 0)] = C0;
|
||||
Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 1)] = C1;
|
||||
Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 2)] = C2;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
PushTri3DOnBatch (render_quad_batch_constructor* Constructor,
|
||||
v4 P0, v4 P1, v4 P2,
|
||||
v2 UV0, v2 UV1, v2 UV2,
|
||||
v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
// TODO(Peter): I think we avoid doing cross thread filling of a batch so do we need this?
|
||||
s32 Tri = ThreadSafeIncrementQuadConstructorCount(Constructor);
|
||||
SetTri3DInBatch(Constructor, Tri, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
|
||||
};
|
||||
|
||||
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 + 2 <= Constructor->Max);
|
||||
PushTri3DOnBatch(Constructor, P0, P1, P2, UVMin, v2{UVMax.x, UVMin.y}, UVMax, Color, Color, Color);
|
||||
PushTri3DOnBatch(Constructor, P0, P2, P3, UVMin, UVMax, v2{UVMin.x, UVMax.y}, Color, Color, Color);
|
||||
}
|
||||
|
||||
internal void
|
||||
PushQuad3DOnBatch (render_quad_batch_constructor* Constructor,
|
||||
v4 P0, v4 P1, v4 P2, v4 P3,
|
||||
v2 UV0, v2 UV1, v2 UV2, v2 UV3,
|
||||
v4 C0, v4 C1, v4 C2, v4 C3)
|
||||
{
|
||||
Assert(Constructor->Count <= Constructor->Max);
|
||||
PushTri3DOnBatch(Constructor, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
|
||||
PushTri3DOnBatch(Constructor, P0, P2, P3, UV0, UV2, UV3, C0, C2, C3);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
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)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
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
|
||||
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, rect2 Rect, v4 Color)
|
||||
{
|
||||
PushQuad2DOnBatch(Constructor, v2{Rect.Min.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Max.y}, v2{Rect.Min.x, Rect.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 = V2Normalize(V2PerpendicularCCW(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)
|
||||
{
|
||||
ResizeBufferIfNecessary(CommandBuffer, 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 render_command_set_render_mode*
|
||||
PushRenderPerspective (render_command_buffer* Buffer, s32 OffsetX, s32 OffsetY, s32 ViewWidth, s32 ViewHeight, camera Camera)
|
||||
{
|
||||
render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
|
||||
|
||||
Command->ModelView = M44Transpose(GetCameraModelViewMatrix(Camera));
|
||||
Command->Projection = M44Transpose(GetCameraPerspectiveProjectionMatrix(Camera));
|
||||
|
||||
Command->ViewOffsetX = (r32)OffsetX;
|
||||
Command->ViewOffsetY = (r32)OffsetY;
|
||||
Command->ViewWidth = (r32)ViewWidth;
|
||||
Command->ViewHeight = (r32)ViewHeight;
|
||||
|
||||
Command->UseDepthBuffer = true;
|
||||
|
||||
return Command;
|
||||
}
|
||||
|
||||
internal void
|
||||
PushRenderPerspective(render_command_buffer* Buffer, rect2 Viewport, camera Camera)
|
||||
{
|
||||
PushRenderPerspective(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport), Camera);
|
||||
}
|
||||
|
||||
internal void
|
||||
PushRenderOrthographic (render_command_buffer* Buffer, s32 OffsetX, s32 OffsetY, s32 ViewWidth, s32 ViewHeight)
|
||||
{
|
||||
render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
|
||||
Command->ModelView = M44Identity();
|
||||
Command->Projection = M44ProjectionOrtho((r32)ViewWidth, (r32)ViewHeight, 0, 100, ViewWidth, 0, ViewHeight, 0);
|
||||
|
||||
Command->ViewOffsetX = (r32)OffsetX;
|
||||
Command->ViewOffsetY = (r32)OffsetY;
|
||||
Command->ViewWidth = ViewWidth;
|
||||
Command->ViewHeight = ViewHeight;
|
||||
|
||||
Command->UseDepthBuffer = false;;
|
||||
}
|
||||
|
||||
internal void
|
||||
PushRenderOrthographic(render_command_buffer* Buffer, rect2 Viewport)
|
||||
{
|
||||
PushRenderOrthographic(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
s32 DataSize = BATCH_2D_SIZE(QuadCount);
|
||||
ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_2d));
|
||||
Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
|
||||
|
||||
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, DataSize, (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
|
||||
PushRenderQuad2D (render_command_buffer* Buffer, rect2 Rect, v4 Color)
|
||||
{
|
||||
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
|
||||
PushQuad2DOnBatch(&Batch, Rect.Min, Rect.Max, Color);
|
||||
}
|
||||
|
||||
internal void
|
||||
PushRenderQuad2DClipped (render_command_buffer* Buffer, rect2 Rect, rect2 ClippingBox, v4 Color)
|
||||
{
|
||||
rect2 Clipped = Rect2Union(Rect, ClippingBox);
|
||||
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
|
||||
PushQuad2DOnBatch(&Batch, Clipped.Min, Clipped.Max, Color);
|
||||
}
|
||||
|
||||
internal void
|
||||
PushRenderQuad2D(render_command_buffer* Buffer, v2 P0, v2 P1, v2 P2, v2 P3, v4 Color)
|
||||
{
|
||||
render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
|
||||
PushQuad2DOnBatch(&Batch, P0, P1, P2, P3, v2{0,0}, v2{1,1}, 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)
|
||||
{
|
||||
s32 TriCount = QuadCount * 2;
|
||||
s32 DataSize = BATCH_3D_SIZE(TriCount);
|
||||
ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_3d));
|
||||
Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
|
||||
|
||||
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, (u8*)(Command + 1), TriCount, DataSize);
|
||||
|
||||
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)
|
||||
{
|
||||
s32 DataSize = BATCH_2D_SIZE(QuadCount);
|
||||
ResizeBufferIfNecessary(Buffer, DataSize);
|
||||
Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
|
||||
|
||||
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, DataSize, (u8*)(Command + 1));
|
||||
Command->Texture = Texture;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal render_quad_batch_constructor
|
||||
PushRenderTexture2DBatch (render_command_buffer* Buffer, s32 QuadCount,
|
||||
u8* TextureMemory, s32 TextureHandle, s32 TextureWidth, s32 TextureHeight,
|
||||
s32 TextureBytesPerPixel, s32 TextureStride)
|
||||
{
|
||||
render_texture Texture = render_texture{
|
||||
TextureMemory,
|
||||
TextureHandle,
|
||||
TextureWidth,
|
||||
TextureHeight,
|
||||
TextureBytesPerPixel,
|
||||
TextureStride};
|
||||
return PushRenderTexture2DBatch(Buffer, QuadCount, Texture);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_RENDERER_H
|
||||
#endif // FOLDHAUS_RENDERER_H
|
|
@ -1,32 +0,0 @@
|
|||
enum node_type
|
||||
{
|
||||
NodeType_SolidColorProc,
|
||||
NodeType_RevolvingDiscs,
|
||||
NodeType_VerticalColorFadeProc,
|
||||
NodeType_Count,
|
||||
};
|
||||
|
||||
static node_specification_ NodeSpecifications[] = {
|
||||
{ NodeType_SolidColorProc, {"SolidColorProc", 14}, gsm_StructType_solid_color_data },
|
||||
{ NodeType_RevolvingDiscs, {"RevolvingDiscs", 14}, gsm_StructType_revolving_discs_data },
|
||||
{ NodeType_VerticalColorFadeProc, {"VerticalColorFadeProc", 21}, gsm_StructType_vertical_color_fade_data },
|
||||
};
|
||||
|
||||
void CallNodeProc(node_type Type, u8* NodeData)
|
||||
{
|
||||
switch(Type) {
|
||||
case NodeType_SolidColorProc:
|
||||
{
|
||||
SolidColorProc((solid_color_data*)NodeData);
|
||||
} break;
|
||||
case NodeType_RevolvingDiscs:
|
||||
{
|
||||
RevolvingDiscs((revolving_discs_data*)NodeData);
|
||||
} break;
|
||||
case NodeType_VerticalColorFadeProc:
|
||||
{
|
||||
VerticalColorFadeProc((vertical_color_fade_data*)NodeData);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
enum gsm_meta_tag_type
|
||||
{
|
||||
MetaTag_panel_type_file_view,
|
||||
MetaTag_panel_type_node_graph,
|
||||
MetaTag_node_output,
|
||||
MetaTag_node_struct,
|
||||
MetaTag_panel_cleanup,
|
||||
MetaTag_node_input,
|
||||
MetaTag_panel_init,
|
||||
MetaTag_panel_type_animation_timeline,
|
||||
MetaTag_panel_commands,
|
||||
MetaTag_panel_type_sculpture_view,
|
||||
MetaTag_node_proc,
|
||||
MetaTag_panel_type_hierarchy,
|
||||
MetaTag_panel_type_profiler,
|
||||
MetaTag_panel_render,
|
||||
MetaTag_panel_type_dmx_view,
|
||||
};
|
||||
gsm_meta_tag MetaTaggs_strings[] = {
|
||||
{ "panel_type_file_view", 20 },
|
||||
{ "panel_type_node_graph", 21 },
|
||||
{ "node_output", 11 },
|
||||
{ "node_struct", 11 },
|
||||
{ "panel_cleanup", 13 },
|
||||
{ "node_input", 10 },
|
||||
{ "panel_init", 10 },
|
||||
{ "panel_type_animation_timeline", 29 },
|
||||
{ "panel_commands", 14 },
|
||||
{ "panel_type_sculpture_view", 25 },
|
||||
{ "node_proc", 9 },
|
||||
{ "panel_type_hierarchy", 20 },
|
||||
{ "panel_type_profiler", 19 },
|
||||
{ "panel_render", 12 },
|
||||
{ "panel_type_dmx_view", 19 },
|
||||
};
|
||||
enum gsm_struct_type
|
||||
{
|
||||
gsm_StructType_r32,
|
||||
gsm_StructType_solid_color_data,
|
||||
gsm_StructType_v4,
|
||||
gsm_StructType_float,
|
||||
gsm_StructType_color_buffer,
|
||||
gsm_StructType_pixel,
|
||||
gsm_StructType_u8,
|
||||
gsm_StructType_s32,
|
||||
gsm_StructType_revolving_discs_data,
|
||||
gsm_StructType_vertical_color_fade_data,
|
||||
gsm_StructType_Count,
|
||||
};
|
||||
|
||||
static gsm_struct_member_type_info StructMembers_v4[] = {
|
||||
{ "x", 1, (u64)&((v4*)0)->x, {}, 0},
|
||||
{ "y", 1, (u64)&((v4*)0)->y, {}, 0},
|
||||
{ "z", 1, (u64)&((v4*)0)->z, {}, 0},
|
||||
{ "w", 1, (u64)&((v4*)0)->w, {}, 0},
|
||||
{ "r", 1, (u64)&((v4*)0)->r, {}, 0},
|
||||
{ "g", 1, (u64)&((v4*)0)->g, {}, 0},
|
||||
{ "b", 1, (u64)&((v4*)0)->b, {}, 0},
|
||||
{ "a", 1, (u64)&((v4*)0)->a, {}, 0},
|
||||
{ "xy", 2, (u64)&((v4*)0)->xy, {}, 0},
|
||||
{ "yz", 2, (u64)&((v4*)0)->yz, {}, 0},
|
||||
{ "xyz", 3, (u64)&((v4*)0)->xyz, {}, 0},
|
||||
{ "z", 1, (u64)&((v4*)0)->z, {}, 0},
|
||||
{ "E", 1, (u64)&((v4*)0)->E, {}, 0},
|
||||
};
|
||||
static gsm_struct_member_type_info StructMembers_pixel[] = {
|
||||
{ "R", 1, (u64)&((pixel*)0)->R, {}, 0},
|
||||
{ "G", 1, (u64)&((pixel*)0)->G, {}, 0},
|
||||
{ "B", 1, (u64)&((pixel*)0)->B, {}, 0},
|
||||
{ "Channels", 8, (u64)&((pixel*)0)->Channels, {}, 0},
|
||||
};
|
||||
static gsm_struct_member_type_info StructMembers_color_buffer[] = {
|
||||
{ "LedPositions", 12, (u64)&((color_buffer*)0)->LedPositions, {}, 0},
|
||||
{ "Colors", 6, (u64)&((color_buffer*)0)->Colors, {}, 0},
|
||||
{ "LEDCount", 8, (u64)&((color_buffer*)0)->LEDCount, {}, 0},
|
||||
};
|
||||
static gsm_struct_member_type_info StructMembers_solid_color_data[] = {
|
||||
{ "Color", 5, (u64)&((solid_color_data*)0)->Color, {MetaTag_node_input, }, 1},
|
||||
{ "Result", 6, (u64)&((solid_color_data*)0)->Result, {MetaTag_node_output, }, 1},
|
||||
};
|
||||
static gsm_struct_member_type_info StructMembers_revolving_discs_data[] = {
|
||||
{ "Rotation", 8, (u64)&((revolving_discs_data*)0)->Rotation, {MetaTag_node_input, }, 1},
|
||||
{ "ThetaZ", 6, (u64)&((revolving_discs_data*)0)->ThetaZ, {MetaTag_node_input, }, 1},
|
||||
{ "ThetaY", 6, (u64)&((revolving_discs_data*)0)->ThetaY, {MetaTag_node_input, }, 1},
|
||||
{ "DiscWidth", 9, (u64)&((revolving_discs_data*)0)->DiscWidth, {MetaTag_node_input, }, 1},
|
||||
{ "InnerRadius", 11, (u64)&((revolving_discs_data*)0)->InnerRadius, {MetaTag_node_input, }, 1},
|
||||
{ "OuterRadius", 11, (u64)&((revolving_discs_data*)0)->OuterRadius, {MetaTag_node_input, }, 1},
|
||||
{ "Color", 5, (u64)&((revolving_discs_data*)0)->Color, {MetaTag_node_input, }, 1},
|
||||
{ "Result", 6, (u64)&((revolving_discs_data*)0)->Result, {MetaTag_node_output, }, 1},
|
||||
};
|
||||
static gsm_struct_member_type_info StructMembers_vertical_color_fade_data[] = {
|
||||
{ "Color", 5, (u64)&((vertical_color_fade_data*)0)->Color, {MetaTag_node_input, }, 1},
|
||||
{ "Min", 3, (u64)&((vertical_color_fade_data*)0)->Min, {MetaTag_node_input, }, 1},
|
||||
{ "Max", 3, (u64)&((vertical_color_fade_data*)0)->Max, {MetaTag_node_input, }, 1},
|
||||
{ "Result", 6, (u64)&((vertical_color_fade_data*)0)->Result, {MetaTag_node_output, }, 1},
|
||||
};
|
||||
|
||||
static gsm_struct_type_info StructTypes[] = {
|
||||
{ gsm_StructType_r32, "r32", 3, 4, 0, 0, 0, 0 },
|
||||
{ gsm_StructType_solid_color_data, "solid_color_data", 16, 32, 0, 0, StructMembers_solid_color_data, 2 },
|
||||
{ gsm_StructType_v4, "v4", 2, 16, 0, 0, StructMembers_v4, 5 },
|
||||
{ gsm_StructType_float, "float", 5, 4, 0, 0, 0, 0 },
|
||||
{ gsm_StructType_color_buffer, "color_buffer", 12, 16, 0, 0, StructMembers_color_buffer, 3 },
|
||||
{ gsm_StructType_pixel, "pixel", 5, 0, 0, 0, StructMembers_pixel, 2 },
|
||||
{ gsm_StructType_u8, "u8", 2, 0, 0, 0, 0, 0 },
|
||||
{ gsm_StructType_s32, "s32", 3, 0, 0, 0, 0, 0 },
|
||||
{ gsm_StructType_revolving_discs_data, "revolving_discs_data", 20, 56, 0, 0, StructMembers_revolving_discs_data, 8 },
|
||||
{ gsm_StructType_vertical_color_fade_data, "vertical_color_fade_data", 24, 40, 0, 0, StructMembers_vertical_color_fade_data, 4 },
|
||||
};
|
||||
static gsm_u32 StructTypesCount = 12;
|
|
@ -1,879 +0,0 @@
|
|||
//
|
||||
// File: blumen_patterns.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-15
|
||||
//
|
||||
#ifndef BLUMEN_PATTERNS_H
|
||||
|
||||
#define FLOWER_COLORS_COUNT 12
|
||||
|
||||
internal void
|
||||
Pattern_None(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
// just here so you can fade in from black
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
v3 SphereCenter = Assembly.Center - v3{0, -150, 0};
|
||||
r32 SphereRadius = Time;
|
||||
r32 SphereBrightness = 1;
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
r32 Sphere = SDF_SphereNormalized(P, SphereCenter, SphereRadius);
|
||||
Sphere = Clamp01(-Sphere);
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(WhiteV4 * Sphere);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
r32 Height = SinR32(Time) * 25;
|
||||
|
||||
r32 CycleLength = 5.0f;
|
||||
r32 CycleProgress = FractR32(Time / CycleLength);
|
||||
r32 CycleBlend = (SinR32(Time) * .5f) + .5f;
|
||||
|
||||
#if 0
|
||||
phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3];
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
v4 HSV = {};
|
||||
if (CycleProgress < .25f)
|
||||
{
|
||||
r32 P = CycleProgress * 4;
|
||||
HSV = V4Lerp(C0,
|
||||
}
|
||||
else if (CycleProgress >= .25f && CycleProgress < .5f)
|
||||
{
|
||||
|
||||
}
|
||||
else if (CycleProgress >= .5f && CycleProgress < .75f)
|
||||
{
|
||||
|
||||
}
|
||||
else if (CycleProgress >= .75f)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
v4 HSV = { CycleProgress * 360, 1, 1, 1 };
|
||||
v4 RGB = HSVToRGB(HSV);
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v4 Pos = Leds->Positions[LedIndex];
|
||||
r32 Dist = Pos.y - Height;
|
||||
|
||||
//v4 HSV = { (ModR32(Dist, 25) / 25) * 360, 1, 1, 1 };
|
||||
//v4 RGB = HSVToRGB(HSV);
|
||||
|
||||
u8 R = (u8)(RGB.x * 255);
|
||||
u8 G = (u8)(RGB.y * 255);
|
||||
u8 B = (u8)(RGB.z * 255);
|
||||
|
||||
Leds->Colors[LedIndex].R = R;
|
||||
Leds->Colors[LedIndex].G = G;
|
||||
Leds->Colors[LedIndex].B = B;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_Rainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
r32 HueBase = ModR32(Time * 50, 360);
|
||||
|
||||
r32 CycleLength = 5.0f;
|
||||
r32 CycleProgress = FractR32(Time / CycleLength);
|
||||
r32 CycleBlend = (SinR32(Time) * .5f) + .5f;
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v4 Pos = Leds->Positions[LedIndex];
|
||||
r32 Hue = HueBase + Pos.y + Pos.x;
|
||||
v4 HSV = { Hue, 1, 1, 1 };
|
||||
v4 RGB = HSVToRGB(HSV);
|
||||
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_RadialRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
v2 RefVector = V2Normalize(v2{ SinR32(Time), CosR32(Time) });
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v2 Vector = v2{
|
||||
Leds->Positions[LedIndex].x,
|
||||
Leds->Positions[LedIndex].z
|
||||
};
|
||||
Vector = V2Normalize(Vector);
|
||||
|
||||
r32 Angle = V2Dot(RefVector, Vector);
|
||||
|
||||
v4 HSV = { (Angle * 30) + (Time * 10), 1, 1, 1 };
|
||||
v4 RGB = HSVToRGB(HSV);
|
||||
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3];
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[StripIndex];
|
||||
r32 CycleT = ModR32(Time, 10) * 20;
|
||||
|
||||
for (u32 i = 0; i < Strip.LedCount; i++)
|
||||
{
|
||||
u32 LedIndex = Strip.LedLUT[i];
|
||||
v4 P = Leds->Positions[LedIndex];
|
||||
|
||||
r32 T = ModR32(P.y + CycleT, 200) / 200.f;
|
||||
T = Clamp01(T);
|
||||
|
||||
v4 Color = {};
|
||||
if (T < 0.5f)
|
||||
{
|
||||
Color = V4Lerp(T * 2, C0, C1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Color = V4Lerp((T - 0.5f) * 2, C1, C2);
|
||||
}
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_WavyOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
r32 Top = 120 + (SinR32(Time) * 10);
|
||||
r32 Mid = 70 + (CosR32(Time * 2.13) * 20);
|
||||
r32 Bot = 0;
|
||||
|
||||
r32 TopD = Top - Mid;
|
||||
r32 BotD = Mid - Bot;
|
||||
r32 MidD = Min(TopD, BotD);
|
||||
|
||||
//r32 MaxFadeDistance = 10;
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
|
||||
r32 PercentTop = Clamp01(1.0f - ((Top - P.y) / TopD));
|
||||
|
||||
r32 PercentMid = Clamp01(1.0f - Abs(P.y - Mid) / MidD);
|
||||
r32 N = Noise3D((P / 17) + v3{Time, -Time, 0});
|
||||
N = Clamp01(N) * 2;
|
||||
N = Smoothstep(N);
|
||||
N *= N;
|
||||
N = Smoothstep(N);
|
||||
N *= 1.0f - PowR32(1.0f - PercentMid, 4);
|
||||
PercentMid = Clamp01(PercentMid + N);
|
||||
|
||||
r32 PercentBot = Clamp01(1.0f - ((P.y - Bot) / BotD));
|
||||
|
||||
v4 TopC = (C0 * PercentTop);
|
||||
v4 MidC = (C1 * PercentMid);
|
||||
v4 BotC = (C2 * PercentBot);
|
||||
|
||||
v4 C = {};
|
||||
if (PercentTop > PercentMid && PercentTop > PercentBot)
|
||||
{
|
||||
C = C0;
|
||||
}
|
||||
else if (PercentMid > PercentBot)
|
||||
{
|
||||
C = C1;
|
||||
}
|
||||
else
|
||||
{
|
||||
C = C2;
|
||||
}
|
||||
|
||||
r32 ScaleFactor = PercentTop + PercentMid + PercentBot;
|
||||
C = (TopC + MidC + BotC) / ScaleFactor;
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
Pattern_WavyOptions(Leds, Range, Assembly, Time, Transient, UserData, 1, C0, C1, C2);
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_PatchyOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
|
||||
r32 BaseGA = 50.000f * (1 / Granularity);
|
||||
r32 BaseGB = 135.20f * (1 / Granularity);
|
||||
r32 BaseGC = 260.74f * (1 / Granularity);
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v4 P = Leds->Positions[LedIndex];
|
||||
r32 LedRange = 300.0f;
|
||||
r32 ScaleFactor = 1.0f / LedRange;
|
||||
v3 Pp = P.xyz + v3{150, 100, 0};
|
||||
|
||||
r32 NoiseA = Noise3D((Pp / BaseGA) + v3{0, 0, Time});
|
||||
NoiseA = PowR32(NoiseA, 3);
|
||||
NoiseA = Smoothstep(NoiseA);
|
||||
|
||||
r32 NoiseB = Noise3D((Pp / BaseGB) + v3{Time * 0.5f, 0, 0});
|
||||
NoiseB = PowR32(NoiseB, 3);
|
||||
NoiseB = Smoothstep(NoiseB);
|
||||
|
||||
#if 1
|
||||
r32 NoiseC = Noise3D((Pp / BaseGC) + v3{Time * 0.5f, 0, 0});
|
||||
NoiseC = PowR32(NoiseC, 3);
|
||||
NoiseC = Smoothstep(NoiseC);
|
||||
#else
|
||||
r32 NoiseC = 0;
|
||||
#endif
|
||||
|
||||
v4 C = (C0 * NoiseA) + (C1 * NoiseB) + (C2 * NoiseC);
|
||||
C /= (NoiseA + NoiseB + NoiseC);
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
Pattern_PatchyOptions(Leds, Range, Assembly, Time, Transient, UserData, 5, C0, C1, C2);
|
||||
}
|
||||
|
||||
internal r32
|
||||
Leafy_BandSDF(v3 P, gs_random_series* Random, r32 Time)
|
||||
{
|
||||
r32 MinBandThickness = 5;
|
||||
r32 MaxBandThickness = 10;
|
||||
r32 MaxTransitionPeriod = 120.0f;
|
||||
|
||||
r32 BandTransitionPeriod = NextRandomUnilateral(Random) * MaxTransitionPeriod;
|
||||
r32 BandTransitionBias = (1 - Clamp(0, (Time / (MaxTransitionPeriod / 2)), 0.7f)); // approaches 0.5 over time
|
||||
BandTransitionPeriod *= BandTransitionBias;
|
||||
|
||||
r32 BandPercent = ModR32(Time, BandTransitionPeriod) / BandTransitionPeriod;
|
||||
BandPercent = Smoothstep(BandPercent);
|
||||
r32 BandY = -150 + (BandPercent * 290);
|
||||
|
||||
r32 ThickRand = NextRandomUnilateral(Random);
|
||||
// 1 - 4((ThickRand - .5)^2) - distribution curve
|
||||
ThickRand = 1.0f - ((4 * PowR32(ThickRand, 2)) - (4 * ThickRand) + 1);
|
||||
r32 BandThickness = MinBandThickness + (ThickRand * (MaxBandThickness - MinBandThickness));
|
||||
|
||||
// BandBrightness = 1 - ((2x - 1) ^ 8) where x is BandPercent
|
||||
r32 BandBrightness = 1.0f - PowR32((2 * BandPercent) - 1, 8);
|
||||
BandBrightness *= RemapR32(NextRandomUnilateral(Random), 0, 1, .25f, 1);
|
||||
r32 Result = 1 - Clamp01(Abs(P.y - BandY) / BandThickness);
|
||||
Result *= BandBrightness;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed * .3f;
|
||||
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v4 P = Leds->Positions[LedIndex];
|
||||
|
||||
v4 C = {};
|
||||
r32 B = 0;
|
||||
|
||||
// NOTE(PS): initializing the Random seed inside the Led Loop
|
||||
// so that the bands are consistently calculated for each led
|
||||
// ie. each time you calculate a band, the random numbers requested
|
||||
// will always be the same
|
||||
gs_random_series Random = InitRandomSeries(24601);
|
||||
u32 BandCount = 25;
|
||||
for (u32 Band = 0; Band < BandCount; Band++)
|
||||
{
|
||||
B += Leafy_BandSDF(P.xyz, &Random, Time);
|
||||
}
|
||||
B = Clamp01(B);
|
||||
|
||||
C = WhiteV4 * B;
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v4 P = Leds->Positions[LedIndex];
|
||||
v3 Pp = P.xyz + v3{150, 100, 0};
|
||||
|
||||
r32 NoiseA = Fbm3D((Pp / 18), Time * 0.25f);
|
||||
NoiseA = Smoothstep(NoiseA);
|
||||
|
||||
r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 0.5f});
|
||||
NoiseB = PowR32(NoiseB, 3);
|
||||
NoiseB = Smoothstep(NoiseB);
|
||||
|
||||
r32 NoiseC = Noise3D((Pp / 25) + v3{0, 0, Time * 4});
|
||||
r32 CPresence = SinR32((P.y / 50) - Time) + (0.8f * SinR32((P.y / 25) - (Time * 5.0f)));
|
||||
CPresence = RemapR32(CPresence, -1.8, 1.8, 0, 1);
|
||||
CPresence = PowR32(CPresence, 4);
|
||||
NoiseC *= CPresence;
|
||||
|
||||
v4 C = (C0 * NoiseA * 0.5f) + (C1 * NoiseB) + (C2 * NoiseC);
|
||||
C *= 1.0f / (NoiseA + NoiseB + NoiseC);
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
gs_random_series Random = InitRandomSeries(24601);
|
||||
|
||||
r32 LightSpeedMin = 1;
|
||||
r32 LightSpeedMax = 5;
|
||||
|
||||
s32 LightTailLength = 60;
|
||||
for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
|
||||
{
|
||||
v2_strip Strip = Assembly.Strips[StripIndex];
|
||||
|
||||
r32 LightStartHeight = NextRandomUnilateral(&Random);
|
||||
r32 LightSpeed = LerpR32(NextRandomUnilateral(&Random),
|
||||
LightSpeedMin,
|
||||
LightSpeedMax);
|
||||
r32 LightCurrentHeight = LightStartHeight + (LightSpeed * Time * 0.1f);
|
||||
s32 StartIndex = (s32)(LightCurrentHeight * (r32)Strip.LedCount) % Strip.LedCount;
|
||||
|
||||
for (s32 i = 0; i < LightTailLength; i++)
|
||||
{
|
||||
s32 StripLedIndex = StartIndex + i;
|
||||
if (StripLedIndex >= (s32)Strip.LedCount) continue;
|
||||
|
||||
u32 LedIndex = Strip.LedLUT[StripLedIndex];
|
||||
r32 PctTail = ((r32)i / (r32)LightTailLength);
|
||||
v4 C = WhiteV4 * PctTail;
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_RotaryOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 BGColor, v4 FGColor)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
gs_random_series Random = InitRandomSeries((u32)(24601 * (Assembly.Center.x + 1.032f)));
|
||||
|
||||
#define SphereCount 32
|
||||
v3 SphereCenter[SphereCount];
|
||||
|
||||
r32 G = RemapR32(Granularity, 1, 5, .75f, 2);
|
||||
r32 MaxHeightOffset = 250;
|
||||
r32 MaxSpeed = 10;
|
||||
r32 SphereRotationRadius = 3.0f;
|
||||
r32 SphereRadius = 2.0f / G;
|
||||
for (u32 i = 0; i < SphereCount; i++)
|
||||
{
|
||||
r32 SphereSeedA = NextRandomUnilateral(&Random);
|
||||
SphereSeedA = PowR32(SphereSeedA, 2);
|
||||
r32 SphereSeedB = NextRandomBilateral(&Random);
|
||||
r32 SphereSpeed = NextRandomUnilateral(&Random) * MaxSpeed;
|
||||
|
||||
r32 SphereTime = Time + SphereSpeed;
|
||||
r32 HeightOffset = 150 - (SphereSeedA * MaxHeightOffset);
|
||||
r32 RotationOffset = SphereTime + SphereSeedB * TauR32;
|
||||
r32 SphereRotationDir = NextRandomBilateral(&Random) < 0 ? -1 : 1;
|
||||
v3 SpherePosOffset = v3{
|
||||
SinR32(RotationOffset * SphereRotationDir) * (SphereRotationRadius * 2),
|
||||
HeightOffset,
|
||||
CosR32(RotationOffset * SphereRotationDir) * (SphereRotationRadius * 2)
|
||||
};
|
||||
SphereCenter[i] = Assembly.Center + SpherePosOffset;
|
||||
}
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
|
||||
r32 Dist = 10000000;
|
||||
for (u32 i = 0; i < SphereCount; i++)
|
||||
{
|
||||
r32 SphereSDF = Abs(SDF_Sphere(P, SphereCenter[i], SphereRadius));
|
||||
SphereSDF = SphereSDF / SphereRadius;
|
||||
Dist = Min(Dist, SphereSDF);
|
||||
}
|
||||
|
||||
v4 C = BGColor;
|
||||
if (Dist <= 1)
|
||||
{
|
||||
r32 Brightness = Clamp01(SphereRadius - Dist);
|
||||
C = V4Lerp(Brightness, BGColor, FGColor);
|
||||
}
|
||||
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, .25f, BlackV4, WhiteV4);
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_AllOnMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
pixel White = V4ToRGBPixel(WhiteV4);
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
Leds->Colors[LedIndex] = White;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_BulbMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
r32 Top = 141;
|
||||
r32 BulbRange = 50;
|
||||
|
||||
pixel White = V4ToRGBPixel(WhiteV4);
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
|
||||
r32 BulbSDF = 1 - Clamp01(((Top - P.y) - BulbRange) / BulbRange);
|
||||
r32 N = Noise3D((P / 17) + v3{Time, -Time, 0});
|
||||
N = Clamp01(N) * 2;
|
||||
N = Smoothstep(N);
|
||||
N *= N;
|
||||
N = Smoothstep(N);
|
||||
N *= 1.0f - PowR32(1.0f - BulbSDF, 4);
|
||||
BulbSDF += N;
|
||||
BulbSDF = Clamp01(BulbSDF);
|
||||
v4 C = WhiteV4 * BulbSDF;
|
||||
Leds->Colors[LedIndex] = V4ToRGBPixel(C);
|
||||
}
|
||||
}
|
||||
|
||||
internal v4
|
||||
GenPatchyColor(v3 P, r32 Time, v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
r32 LedRange = 300.0f;
|
||||
r32 ScaleFactor = 1.0f / LedRange;
|
||||
v3 Pp = P + v3{150, 100, 0};
|
||||
|
||||
r32 ScaleA = 1;
|
||||
r32 NoiseA = Noise3D(((Pp / 38) + v3{0, 0, Time}) * ScaleA);
|
||||
NoiseA = PowR32(NoiseA, 3);
|
||||
NoiseA = Smoothstep(NoiseA);
|
||||
|
||||
r32 ScaleBP = 2;
|
||||
r32 ScaleB = 15;
|
||||
r32 NoiseBP = Noise3D(((Pp / 13) + v3{ 0, Time * -0.33f, 0}) * ScaleBP);
|
||||
NoiseBP = PowR32(NoiseBP, 3);
|
||||
r32 NoiseB = Noise3D(((Pp / 75) + v3{Time * 0.5f, 0, 0}) * ScaleB);
|
||||
NoiseB = PowR32(NoiseB, 3);
|
||||
NoiseB = Smoothstep(NoiseB) * NoiseBP;
|
||||
|
||||
r32 ScaleC = 1.5;
|
||||
r32 NoiseCP = Noise3D(((Pp / 132) + v3{Time * -0.33f, 0, 0}) * 0.5f);
|
||||
r32 NoiseC = Noise3D(((Pp / 164) + v3{Time * 0.25f, 0, 0}) * ScaleC);
|
||||
NoiseC = PowR32(NoiseC, 3);
|
||||
NoiseC = Smoothstep(NoiseC) * NoiseCP;
|
||||
|
||||
v4 C = (C0 * NoiseA) + (C1 * NoiseB) + (C2 * NoiseC);
|
||||
C /= (NoiseA + NoiseB + NoiseC);
|
||||
return C;
|
||||
}
|
||||
|
||||
internal r32
|
||||
GenVerticalStrips(v3 P, r32 Time)
|
||||
{
|
||||
v2 Right = v2{1, 0};
|
||||
v2 Pa = V2Normalize(v2{P.x, P.z});
|
||||
r32 Angle = .5f + (.5f * V2Dot(Pa, Right));
|
||||
|
||||
r32 HOffset = 70.0f;
|
||||
r32 O = 50.0f;
|
||||
r32 C = 10.0f;
|
||||
|
||||
r32 X = Angle;
|
||||
r32 Y = P.y;
|
||||
r32 I = FloorR32(Y / C) * C;
|
||||
I += (X * HOffset) + (Time * 25);
|
||||
|
||||
r32 V = FractR32(I / O);
|
||||
V = 2.0f * (0.5f - Abs(V - 0.5f));
|
||||
Assert(V >= 0 && V <= 1);
|
||||
|
||||
return V;
|
||||
}
|
||||
|
||||
internal v4
|
||||
GenVerticalLeaves(v3 P, r32 Time, v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
r32 A = GenVerticalStrips(P, Time * .25f);
|
||||
r32 B = GenVerticalStrips(P * .3f, Time);
|
||||
r32 C = GenVerticalStrips(P * .25f, Time * 2);
|
||||
|
||||
v4 R = (C0 * A) + (C1 * B) + (C2 * C);
|
||||
R /= A + B + C;
|
||||
return R;
|
||||
}
|
||||
|
||||
internal void
|
||||
AddIn_WavesPattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
v4 C2P = C2 * 255;
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
|
||||
v4 C = v4{
|
||||
(r32)Leds->Colors[LedIndex].R,
|
||||
(r32)Leds->Colors[LedIndex].G,
|
||||
(r32)Leds->Colors[LedIndex].B,
|
||||
1
|
||||
};
|
||||
|
||||
r32 Offset = -250;
|
||||
r32 Width = 30;
|
||||
r32 Bands = 0;
|
||||
for (u32 i = 1; i <= 1; i++)
|
||||
{
|
||||
P.x = FloorR32(P.x);
|
||||
P.z = FloorR32(P.z);
|
||||
|
||||
v3 P0 = v3{P.x + (23.124f * i), 0, P.z - (-12.34f * i) + Time};
|
||||
r32 S = Fbm3D(P0 * .005f, Time) * 250;
|
||||
S += ModR32((Time * 100) - (150 * i), 400);
|
||||
|
||||
r32 Y = (P.y - Offset);
|
||||
r32 V = (Width - Abs(Y - S)) / Width;
|
||||
V = Clamp01(V);
|
||||
|
||||
Bands += V;
|
||||
}
|
||||
|
||||
C = V4Lerp(Bands, C * .5f, C2P);
|
||||
|
||||
Leds->Colors[LedIndex] = pixel{(u8)C.r, (u8)C.g, (u8)C.b};
|
||||
}
|
||||
}
|
||||
|
||||
internal r32
|
||||
GenDotBands(v3 P, r32 Time)
|
||||
{
|
||||
r32 RowHeight = 25;
|
||||
r32 DotRadius = 20;
|
||||
|
||||
r32 Y = P.y + 150;
|
||||
s32 Row = (s32)FloorR32(Y / RowHeight);
|
||||
r32 RowH = Abs(FractR32(Y / RowHeight));
|
||||
r32 DotDistY = Max(0, .5f - RowH) * 2;
|
||||
|
||||
r32 Angle = (V2Dot(V2Normalize(v2{P.x, P.z}), v2{1,0}) * .5f) + .5f;
|
||||
r32 DotDistX = Abs(ModR32(Angle, .2f));
|
||||
|
||||
r32 DotDist = SqrtR32(PowR32(DotDistX, 2) + PowR32(RowH, 2));
|
||||
r32 B = (DotRadius - DotDist) / DotRadius;
|
||||
B = Clamp01(DotDist);
|
||||
|
||||
return DotDistY;
|
||||
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
Time = Time * BLState->PatternSpeed * Hue.Speed;;
|
||||
|
||||
switch (Hue.Pattern)
|
||||
{
|
||||
case HuePattern_Wavy:
|
||||
{
|
||||
Pattern_WavyOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C0);
|
||||
}break;
|
||||
|
||||
default:
|
||||
{
|
||||
Pattern_PatchyOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C2);
|
||||
}break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_VoiceAddIns(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
Time = Time * BLState->PatternSpeed * Hue.Speed;;
|
||||
|
||||
switch (Hue.AddIn)
|
||||
{
|
||||
case AddIn_Rotary:
|
||||
{
|
||||
Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, BlackV4, C2);
|
||||
}break;
|
||||
|
||||
case AddIn_Waves:
|
||||
{
|
||||
AddIn_WavesPattern(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C2);
|
||||
}break;
|
||||
|
||||
case AddIn_None:
|
||||
default:
|
||||
{
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
|
||||
pixel WhiteMask = V4ToRGBPixel(WhiteV4);
|
||||
|
||||
led_strip_list Stem = BLState->StemStrips[Assembly.AssemblyIndex];
|
||||
for (u32 s = 0; s < Stem.Count; s++)
|
||||
{
|
||||
u32 StripIndex = Stem.StripIndices[s];
|
||||
v2_strip Strip = Assembly.Strips[StripIndex];
|
||||
for (u32 i = 0; i < Strip.LedCount; i++)
|
||||
{
|
||||
u32 LedIndex = Strip.LedLUT[i];
|
||||
Leds->Colors[LedIndex] = WhiteMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_PrimaryHue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
|
||||
v4 C0 = RGBFromPhraseHue(Hue.Hue0);
|
||||
v4 C1 = RGBFromPhraseHue(Hue.Hue1);
|
||||
v4 C2 = RGBFromPhraseHue(Hue.Hue2);
|
||||
|
||||
pixel HueOut = V4ToRGBPixel(C0);
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
Leds->Colors[LedIndex] = HueOut;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_GrowFadeMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
r32 Period = 10.0f; // seconds
|
||||
r32 ElapsedPct = FractR32(Time / Period);
|
||||
|
||||
r32 ElapsedPctGrow = PowR32(ElapsedPct * 2, 2);
|
||||
r32 ElapsedPctFade = Clamp01((ElapsedPct * 2) - 1);
|
||||
|
||||
r32 Radius = 300 * ElapsedPctGrow;
|
||||
|
||||
v3 Origin = Assembly.Center - v3{0, 150, 0};
|
||||
|
||||
r32 Brightness = Smoothstep(1.0f - ElapsedPctFade);
|
||||
|
||||
pixel COutside = V4ToRGBPixel(BlackV4);
|
||||
pixel CInside = V4ToRGBPixel(WhiteV4 * Brightness);
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
r32 Dist = V3Mag(P - Origin);
|
||||
if (Dist < Radius)
|
||||
{
|
||||
Leds->Colors[LedIndex] = CInside;
|
||||
}
|
||||
else
|
||||
{
|
||||
Leds->Colors[LedIndex] = COutside;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_RainbowLoadingBar(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
|
||||
Time = Time * BLState->PatternSpeed;
|
||||
|
||||
// constants
|
||||
r32 Period = 5.0f; // seconds
|
||||
r32 CSpeed = 16.0f;
|
||||
r32 HIncrement = CSpeed * Period;
|
||||
r32 HOffset = CSpeed * Period;
|
||||
r32 MaxSphereRadius = 300;
|
||||
|
||||
// sphere
|
||||
r32 ElapsedPct = FractR32(Time / Period);
|
||||
r32 ElapsedPctGrow = PowR32(ElapsedPct, 2);
|
||||
r32 Radius = MaxSphereRadius * ElapsedPctGrow;
|
||||
v3 Origin = Assembly.Center - v3{0, 150, 0};
|
||||
|
||||
// colors
|
||||
r32 T = Time * CSpeed;
|
||||
r32 TimeStep0 = T;
|
||||
r32 TimeStep1 = T + HOffset;
|
||||
r32 Hue0 = FloorR32(TimeStep0 / HIncrement) * HIncrement;
|
||||
r32 Hue1 = FloorR32(TimeStep1 / HIncrement) * HIncrement;
|
||||
v4 H0 = v4{ModR32(Hue0, 360), 1, 1, 1};
|
||||
v4 H1 = v4{ModR32(Hue1, 360), 1, 1, 1};
|
||||
pixel C0 = V4ToRGBPixel(HSVToRGB(H0));
|
||||
pixel C1 = V4ToRGBPixel(HSVToRGB(H1));
|
||||
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
v3 P = Leds->Positions[LedIndex].xyz;
|
||||
r32 Dist = V3Mag(P - Origin);
|
||||
if (Dist < Radius)
|
||||
{
|
||||
Leds->Colors[LedIndex] = C1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Leds->Colors[LedIndex] = C0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_Blue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
|
||||
{
|
||||
pixel Blue = pixel{0, 0, 255};
|
||||
for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
|
||||
{
|
||||
Leds->Colors[LedIndex] = Blue;
|
||||
}
|
||||
}
|
||||
|
||||
#define BLUMEN_PATTERNS_H
|
||||
#endif // BLUMEN_PATTERNS_H
|
|
@ -1,76 +0,0 @@
|
|||
#include <Cocoa/Cocoa.h>
|
||||
#include "gs_osx_memory.mm"
|
||||
#include "gs_osx_window.mm"
|
||||
#include "gs_osx_fileio.mm"
|
||||
#include "gs_osx_lib.mm"
|
||||
#include "gs_osx_opengl.mm"
|
||||
#include "gs_osx_time.mm"
|
||||
|
||||
static void
|
||||
gsosx_ProcessWindowEvents(NSApplication* App, NSWindow* Window)
|
||||
{
|
||||
// Process Events
|
||||
while (true)
|
||||
{
|
||||
NSEvent* Event = [App nextEventMatchingMask: NSEventMaskAny untilDate: [NSDate distantPast] inMode: NSDefaultRunLoopMode dequeue: YES];
|
||||
if (!Event) { break; }
|
||||
|
||||
switch([Event type])
|
||||
{
|
||||
case NSEventTypeKeyUp:
|
||||
case NSEventTypeKeyDown:
|
||||
{
|
||||
// TODO: Handle Key Presses
|
||||
}break;
|
||||
|
||||
// TODO: Mouse Input
|
||||
|
||||
default:
|
||||
{
|
||||
[App sendEvent: Event];
|
||||
}break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int ArgCount, char** Args)
|
||||
{
|
||||
NSApplication* Application = [NSApplication sharedApplication];
|
||||
[Application setActivationPolicy: NSApplicationActivationPolicyRegular];
|
||||
|
||||
gsosx_ApplicationDelegate* Delegate = [[gsosx_ApplicationDelegate alloc] init];
|
||||
[Application setDelegate: Delegate];
|
||||
|
||||
int WindowWidth = 1024;
|
||||
int WindowHeight = 768;
|
||||
id AppName = @"Lumenarium";
|
||||
NSWindow* Window = gsosx_CreateWindow(Application, WindowWidth, WindowHeight, AppName);
|
||||
|
||||
// A really cryptic way of asking the window to open
|
||||
[Window makeKeyAndOrderFront: Application];
|
||||
|
||||
NSOpenGLContext* OpenGLContext = gsoo_CreateOpenGLContext(Window, WindowWidth, WindowHeight, true);
|
||||
|
||||
gsosx_time_info TimeInfo = gsosx_InitTime();
|
||||
double TargetSecondsPerFrame = 1.0 / 60;
|
||||
uint64_t LastFrameEnd = gsosx_GetTime(TimeInfo);
|
||||
while (true)
|
||||
{
|
||||
gsosx_ProcessWindowEvents(Application, Window);
|
||||
|
||||
glClearColor(1, 0, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
gsoo_SwapBuffers(OpenGLContext);
|
||||
|
||||
uint64_t ThisFrameEnd = gsosx_GetTime(TimeInfo);
|
||||
double FrameSeconds = gsosx_GetSecondsElapsed(LastFrameEnd, ThisFrameEnd, TimeInfo);
|
||||
double SleepSeconds = TargetSecondsPerFrame - FrameSeconds;
|
||||
if (SleepSeconds > 0)
|
||||
{
|
||||
gsosx_Sleep(SleepSeconds, TimeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
static uint32_t
|
||||
gsosx_GetLastFileWriteTime(char* Path)
|
||||
{
|
||||
int32_t Result = 0;
|
||||
struct stat FileStat = {0};
|
||||
if (stat(Path, &FileStat) == 0)
|
||||
{
|
||||
Result = FileStat.st_mtimespec.tv_sec;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Asserts
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
gsosx_GetFilesize(char* Path)
|
||||
{
|
||||
uint32_t Result = 0;
|
||||
|
||||
int FileHandle = open(Path, O_RDONLY);
|
||||
struct stat FileStat = {0};
|
||||
fstat(FileHandle, &FileStat);
|
||||
close(FileHandle);
|
||||
|
||||
Result = (uint32_t)FileStat.st_size;
|
||||
return Result;
|
||||
}
|
||||
|
||||
static bool
|
||||
gsosx_LoadFileIntoMemory(char* Path, uint32_t FileSize, uint8_t* FileMemory)
|
||||
{
|
||||
bool Result = false;
|
||||
int FileHandle = open(Path, O_RDONLY);
|
||||
|
||||
struct stat FileStat = {0};
|
||||
fstat(FileHandle, &FileStat);
|
||||
if (FileStat.st_size <= FileSize)
|
||||
{
|
||||
read(FileHandle, FileMemory, FileSize);
|
||||
Result = true;
|
||||
}
|
||||
close(FileHandle);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static bool
|
||||
gsosx_WriteEntireFile(char* Path, uint32_t FileSize, uint8_t* FileMemory)
|
||||
{
|
||||
bool Result = false;
|
||||
int FileHandle = open(Path, O_WRONLY | O_CREAT, 0777);
|
||||
ssize_t SizeWritten = write(FileHandle, FileMemory, FileSize);
|
||||
if (SizeWritten == FileSize)
|
||||
{
|
||||
Result = true;
|
||||
}
|
||||
close(FileHandle);
|
||||
return Result;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#include <dlfcn.h>
|
||||
|
||||
static void*
|
||||
gsosx_LoadDLL(char* Path)
|
||||
{
|
||||
void* LibHandle = 0;
|
||||
|
||||
LibHandle = dlopen(Path, RTLD_LAZY);
|
||||
if (LibHandle)
|
||||
{
|
||||
dlerror(); // Clear Last Error
|
||||
}
|
||||
else
|
||||
{
|
||||
LibHandle = 0;
|
||||
}
|
||||
|
||||
return LibHandle;
|
||||
}
|
||||
|
||||
#define gsosx_GetProcAddress(libHandle, type, name) (type*)dlsym((libHandle), name)
|
||||
|
||||
static void
|
||||
gsosx_UnloadDLL(void* LibHandle)
|
||||
{
|
||||
if (LibHandle)
|
||||
{
|
||||
dlclose(LibHandle);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
static uint8_t*
|
||||
gsosx_Alloc(size_t Size)
|
||||
{
|
||||
uint8_t* Result = 0;
|
||||
char* StartAddress = (char*)0;
|
||||
int Prot = PROT_READ | PROT_WRITE;
|
||||
int Flags = MAP_PRIVATE | MAP_ANON;
|
||||
Result = (uint8_t*)mmap(StartAddress, Size, Prot, Flags, -1, 0);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void
|
||||
gsosx_Free(uint8_t* Base, uint32_t Size)
|
||||
{
|
||||
munmap((void*)Base, (size_t)Size);
|
||||
}
|
||||
|
||||
static uint8_t*
|
||||
gsosx_Realloc(uint8_t* Base, uint32_t OldSize, uint32_t NewSize)
|
||||
{
|
||||
uint8_t* Result = gsosx_Alloc(NewSize);
|
||||
for (int32_t i = 0; i < OldSize; i++)
|
||||
{
|
||||
Result[i] = Base[i];
|
||||
}
|
||||
gsosx_Free(Base, OldSize );
|
||||
return Result;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#include <OpenGL/gl.h>
|
||||
|
||||
struct gsoo_opengl_state
|
||||
{
|
||||
NSOpenGLContext* Context;
|
||||
};
|
||||
|
||||
@interface gsoo_OpenGLView: NSOpenGLView @end
|
||||
@implementation gsoo_OpenGLView : NSOpenGLView
|
||||
- (void)
|
||||
reshape
|
||||
{
|
||||
// TODO: framebufferWidth and Height were globals in the code I was pulling from
|
||||
// Need some way to get the new window height in here.
|
||||
CGRect FrameRect = self.frame;
|
||||
glViewport(0, 0, FrameRect.size.width, FrameRect.size.height);
|
||||
//glViewport(0, 0, framebufferWidth, framebufferHeight);
|
||||
}
|
||||
@end
|
||||
|
||||
static NSOpenGLContext*
|
||||
gsoo_CreateOpenGLContext(NSWindow* Window, uint32_t Width, uint32_t Height, int32_t EnableVSync)
|
||||
{
|
||||
NSOpenGLContext* Result = 0;
|
||||
NSOpenGLPixelFormatAttribute PixelFormatAttributes[] = {
|
||||
NSOpenGLPFAClosestPolicy,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFASampleBuffers,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
NSOpenGLPixelFormat* PixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: PixelFormatAttributes];
|
||||
Result = [[NSOpenGLContext alloc] initWithFormat: PixelFormat shareContext: 0];
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
// TODO: Assert/Handle
|
||||
return 0;
|
||||
}
|
||||
|
||||
[Result makeCurrentContext];
|
||||
GLint VSync = EnableVSync;
|
||||
[Result setValues: &VSync forParameter: NSOpenGLCPSwapInterval];
|
||||
|
||||
// Set Backbuffer Resolution
|
||||
GLint BackbufferDimensions[] = { Width, Height };
|
||||
CGLSetParameter(Result.CGLContextObj, kCGLCPSurfaceBackingSize, BackbufferDimensions);
|
||||
CGLEnable(Result.CGLContextObj, kCGLCESurfaceBackingSize);
|
||||
|
||||
//
|
||||
gsoo_OpenGLView* View = [[gsoo_OpenGLView alloc] init];
|
||||
[Window setContentView: View];
|
||||
[View setOpenGLContext: Result];
|
||||
[View setPixelFormat: PixelFormat];
|
||||
[Result setView: View];
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void
|
||||
gsoo_SwapBuffers(NSOpenGLContext* OpenGLContext)
|
||||
{
|
||||
[OpenGLContext flushBuffer];
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#include <mach/mach_time.h>
|
||||
|
||||
static const uint64_t NANOS_PER_USEC = 1000ULL;
|
||||
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
|
||||
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
|
||||
|
||||
struct gsosx_time_info
|
||||
{
|
||||
uint64_t StartTimeAbsolute;
|
||||
mach_timebase_info_data_t MachTimeInfo;
|
||||
};
|
||||
|
||||
static gsosx_time_info
|
||||
gsosx_InitTime()
|
||||
{
|
||||
gsosx_time_info Result = {0};
|
||||
Result.StartTimeAbsolute = mach_absolute_time();
|
||||
mach_timebase_info(&Result.MachTimeInfo);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
gsosx_GetTime(gsosx_time_info TimeInfo)
|
||||
{
|
||||
uint64_t Result = mach_absolute_time() - TimeInfo.StartTimeAbsolute;
|
||||
return Result;
|
||||
}
|
||||
|
||||
static double
|
||||
gsosx_GetSecondsElapsed(uint64_t Start, uint64_t End, gsosx_time_info TimeInfo)
|
||||
{
|
||||
double Result = 0;
|
||||
double Elapsed = (double)(End - Start);
|
||||
Result = Elapsed * (double)(TimeInfo.MachTimeInfo.numer) / (double)NANOS_PER_SEC / (double)TimeInfo.MachTimeInfo.denom;
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void
|
||||
gsosx_Sleep(double Seconds, gsosx_time_info TimeInfo)
|
||||
{
|
||||
// NOTE(Peter): This isn't entirely precise, and can vary by up to 500 microseconds. We could implement a sleep combined witha busy wait
|
||||
// to get more precise sleeping. Do this if necessary
|
||||
uint64_t WaitTimeAbs = Seconds / (double)TimeInfo.MachTimeInfo.numer * (double)NANOS_PER_SEC * (double)TimeInfo.MachTimeInfo.denom;
|
||||
uint64_t NowAbs = mach_absolute_time();
|
||||
mach_wait_until(NowAbs + WaitTimeAbs);
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
@interface gsosx_ApplicationDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> @end
|
||||
|
||||
@implementation gsosx_ApplicationDelegate : NSObject
|
||||
- (void)
|
||||
applicationDidFinishLaunching: (NSNotification *)notification
|
||||
{
|
||||
[NSApp stop: nil];
|
||||
NSAutoreleasePool* Pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
[Pool drain];
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)
|
||||
applicationShouldTerminate: (NSApplication*) sender
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)
|
||||
dealloc
|
||||
{
|
||||
[super dealloc];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface gsosx_WindowDelegate: NSObject<NSWindowDelegate> @end
|
||||
@implementation gsosx_WindowDelegate : NSObject
|
||||
- (BOOL)
|
||||
windowShouldClose: (id)sender
|
||||
{
|
||||
// TODO(Peter): Stop Application Running?
|
||||
NSLog(@"Close button pressed");
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)
|
||||
windowDidBecomeKey: (NSNotification*)notification
|
||||
{
|
||||
// TODO: ???
|
||||
}
|
||||
|
||||
- (void)
|
||||
windowDisResignKey: (NSNotification*)notification
|
||||
{
|
||||
// TODO: ???
|
||||
}
|
||||
@end
|
||||
|
||||
static NSWindow*
|
||||
gsosx_CreateWindow(NSApplication* App, int Width, int Height, id Title)
|
||||
{
|
||||
int WindowStyleMask = NSWindowStyleMaskClosable;
|
||||
NSRect WindowRect = NSMakeRect(0, 0, Width, Height);
|
||||
|
||||
NSWindow* Window = [[NSWindow alloc] initWithContentRect: WindowRect styleMask: WindowStyleMask backing: NSBackingStoreBuffered defer: YES];
|
||||
Window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
|
||||
|
||||
gsosx_WindowDelegate* WindowDelegate = [[gsosx_WindowDelegate alloc] init];
|
||||
|
||||
[Window setOpaque: YES];
|
||||
[Window setDelegate: WindowDelegate];
|
||||
[Window setTitle: Title];
|
||||
|
||||
NSMenu* MenuBar = [NSMenu alloc];
|
||||
NSMenuItem* AppMenuItem = [NSMenuItem alloc];
|
||||
[MenuBar addItem: AppMenuItem];
|
||||
[App setMainMenu: MenuBar];
|
||||
|
||||
NSMenu* AppMenu = [NSMenu alloc];
|
||||
id QuitTitle = [@"Quit " gs_stringByAppendinggs_string: Title];
|
||||
id QuitMenuItem = [[NSMenuItem alloc] initWithTitle: QuitTitle action: @selector(terminate:) keyEquivalent: @"q"];
|
||||
[AppMenu addItem: QuitMenuItem];
|
||||
[AppMenuItem setSubmenu: AppMenu];
|
||||
|
||||
[App activateIgnoringOtherApps: YES];
|
||||
|
||||
return Window;
|
||||
}
|
|
@ -1,786 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-01-01
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_CPP
|
||||
|
||||
#include <Winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <intrin.h>
|
||||
#include <windowsx.h>
|
||||
#include <gl/gl.h>
|
||||
|
||||
#include "../../meta/gs_meta_include.h"
|
||||
#include "../foldhaus_platform.h"
|
||||
|
||||
#include "../../gs_libs/gs_win32.cpp"
|
||||
#include "win32_foldhaus_utils.h"
|
||||
#include "win32_foldhaus_memory.h"
|
||||
#include "win32_foldhaus_fileio.h"
|
||||
#include "win32_foldhaus_dll.h"
|
||||
#include "win32_foldhaus_timing.h"
|
||||
#include "win32_foldhaus_work_queue.h"
|
||||
#include "win32_foldhaus_serial.h"
|
||||
#include "win32_foldhaus_socket.h"
|
||||
#include "win32_foldhaus_mouse.h"
|
||||
|
||||
#include "../foldhaus_renderer.cpp"
|
||||
|
||||
#include "win32_test_code.cpp"
|
||||
|
||||
global b32 Running = false;
|
||||
global b32 WindowIsActive = false;
|
||||
|
||||
char DLLName[] = "foldhaus.dll";
|
||||
char WorkingDLLName[] = "foldhaus_temp.dll";
|
||||
char DLLLockFileName[] = "lock.tmp";
|
||||
|
||||
window MainWindow;
|
||||
|
||||
PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle)
|
||||
{
|
||||
s32 Handle = SubmitTexture(Memory, Width, Height);
|
||||
return Handle;
|
||||
}
|
||||
|
||||
HDC FontDrawingDC;
|
||||
HBITMAP FontBitmap;
|
||||
HFONT CurrentFont;
|
||||
|
||||
GET_FONT_INFO(Win32GetFontInfo)
|
||||
{
|
||||
platform_font_info Result = {};
|
||||
|
||||
FontDrawingDC = CreateCompatibleDC(NULL);
|
||||
SetBkColor(FontDrawingDC, RGB(0, 0, 0));
|
||||
SetTextColor(FontDrawingDC, RGB(255, 255, 255));
|
||||
FontBitmap = CreateCompatibleBitmap(FontDrawingDC, PixelHeight * 2, PixelHeight * 2);
|
||||
HGDIOBJ SelectObjectResult = SelectObject(FontDrawingDC, FontBitmap);
|
||||
|
||||
CurrentFont = CreateFont(PixelHeight, 0, 0, 0,
|
||||
FontWeight,
|
||||
Italic,
|
||||
Underline,
|
||||
Strikeout,
|
||||
ANSI_CHARSET,
|
||||
OUT_OUTLINE_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS,
|
||||
PROOF_QUALITY,
|
||||
FIXED_PITCH,
|
||||
FontName);
|
||||
SelectFont(FontDrawingDC, CurrentFont);
|
||||
|
||||
TEXTMETRIC WindowsFontMetrics = {};
|
||||
if (GetTextMetrics(FontDrawingDC, &WindowsFontMetrics))
|
||||
{
|
||||
Result.PixelHeight = WindowsFontMetrics.tmHeight;
|
||||
Result.Ascent = WindowsFontMetrics.tmAscent;
|
||||
Result.Descent = WindowsFontMetrics.tmDescent;
|
||||
Result.Leading = WindowsFontMetrics.tmExternalLeading;
|
||||
Result.MaxCharWidth = WindowsFontMetrics.tmMaxCharWidth;
|
||||
Result.CodepointStart = WindowsFontMetrics.tmFirstChar;
|
||||
Result.CodepointOnePastLast = WindowsFontMetrics.tmLastChar + 1;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
DRAW_FONT_CODEPOINT(Win32DrawFontCodepoint)
|
||||
{
|
||||
SIZE CodepointSize = {};
|
||||
if (GetTextExtentPoint32(FontDrawingDC, &Codepoint, 1, &CodepointSize))
|
||||
{
|
||||
*OutWidth = CodepointSize.cx;
|
||||
*OutHeight = CodepointSize.cy;
|
||||
|
||||
RECT TextRect = {};
|
||||
TextRect.left = 0;
|
||||
TextRect.right = *OutWidth;
|
||||
TextRect.top = 0;
|
||||
TextRect.bottom = *OutHeight;
|
||||
|
||||
int Error = DrawText(FontDrawingDC, &Codepoint, 1, &TextRect, DT_LEFT | DT_NOCLIP | DT_TOP);
|
||||
|
||||
u8* Row = DestBuffer + (YOffset * (DestBufferWidth * 4));
|
||||
COLORREF PixelColor;
|
||||
for (u32 Y = 0; Y < *OutHeight; Y++)
|
||||
{
|
||||
// NOTE(Peter): XOffset * 4 b/c its 4 bytes per pixel.
|
||||
u8* Channel = (u8*)Row + (XOffset * 4);
|
||||
for (u32 X = 0; X < *OutWidth; X++)
|
||||
{
|
||||
PixelColor = GetPixel(FontDrawingDC, X + TextRect.left, TextRect.bottom - Y);
|
||||
Assert(PixelColor != CLR_INVALID);
|
||||
u8 RValue = GetRValue(PixelColor);
|
||||
*Channel++ = RValue;
|
||||
*Channel++ = RValue;
|
||||
*Channel++ = RValue;
|
||||
*Channel++ = RValue;
|
||||
}
|
||||
Row += DestBufferWidth * 4;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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, window* Window, input_queue* InputQueue, mouse_state* Mouse)
|
||||
{
|
||||
switch (Message.message)
|
||||
{
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
Mouse->Scroll = GET_WHEEL_DELTA_WPARAM(Message.wParam);
|
||||
}break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
{
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
|
||||
Mouse->LeftButtonState |= KeyState_IsDown;
|
||||
Mouse->DownPos = Mouse->Pos;
|
||||
|
||||
// :Win32MouseEventCapture
|
||||
// NOTE(Peter): We capture events when the mouse goes down so that
|
||||
// if the user drags outside the window, we still get the mouse up
|
||||
// event and can process it. Otherwise, we can get into cases where
|
||||
// an event was started, didn't end, but the user can click again and
|
||||
// try to start the event again.
|
||||
// We relase event capture on mouse up.
|
||||
SetCapture(Window->Handle);
|
||||
}break;
|
||||
|
||||
case WM_MBUTTONDOWN:
|
||||
{
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, false, true,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
Mouse->MiddleButtonState = KeyState_IsDown & ~KeyState_WasDown;
|
||||
|
||||
// :Win32MouseEventCapture
|
||||
SetCapture(Window->Handle);
|
||||
}break;
|
||||
|
||||
case WM_RBUTTONDOWN:
|
||||
{
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, false, true,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
Mouse->RightButtonState = KeyState_IsDown & ~KeyState_WasDown;
|
||||
Mouse->DownPos = Mouse->Pos;
|
||||
|
||||
// :Win32MouseEventCapture
|
||||
SetCapture(Window->Handle);
|
||||
}break;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
{
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
Mouse->LeftButtonState &= ~KeyState_IsDown;
|
||||
|
||||
// :Win32MouseEventCapture
|
||||
ReleaseCapture();
|
||||
}break;
|
||||
|
||||
case WM_MBUTTONUP:
|
||||
{
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
AddInputEventEntry(InputQueue, KeyCode_MouseMiddleButton, true, false,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
Mouse->MiddleButtonState = ~KeyState_IsDown & KeyState_WasDown;
|
||||
|
||||
// :Win32MouseEventCapture
|
||||
ReleaseCapture();
|
||||
}break;
|
||||
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
AddInputEventEntry(InputQueue, KeyCode_MouseRightButton, true, false,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
Mouse->RightButtonState = ~KeyState_IsDown & KeyState_WasDown;
|
||||
|
||||
// :Win32MouseEventCapture
|
||||
ReleaseCapture();
|
||||
}break;
|
||||
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYDOWN:
|
||||
case WM_KEYUP:
|
||||
{
|
||||
#if 0
|
||||
int VirtualKey = (int)Message.wParam;
|
||||
key_code Key = Win32GetKeyCode(VirtualKey, true, false);
|
||||
s32 KeyIndex = (int)Key;
|
||||
|
||||
b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0;
|
||||
b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0;
|
||||
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
// New Input Queue
|
||||
AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
#endif
|
||||
TranslateMessage(&Message);
|
||||
DispatchMessage(&Message);
|
||||
}break;
|
||||
|
||||
case WM_CHAR:
|
||||
{
|
||||
char VirtualKey = (char)Message.wParam;
|
||||
key_code Key = CharToKeyCode(VirtualKey);
|
||||
s32 KeyIndex = (int)Key;
|
||||
|
||||
b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0;
|
||||
b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0;
|
||||
|
||||
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
|
||||
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
|
||||
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
|
||||
|
||||
// New Input Queue
|
||||
AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown,
|
||||
ShiftDown, AltDown, CtrlDown, false);
|
||||
}break;
|
||||
|
||||
default:
|
||||
{
|
||||
TranslateMessage(&Message);
|
||||
DispatchMessage(&Message);
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DebugPrint (char* Format, ...)
|
||||
{
|
||||
char Buffer[256];
|
||||
gs_string StringBuffer = MakeString(Buffer, 256);
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args);
|
||||
va_end(Args);
|
||||
}
|
||||
|
||||
internal void
|
||||
SetApplicationLinks (context* Context, win32_dll_refresh DLL, gs_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;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* BufferAt)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
u32 BuffersSent = 0;
|
||||
u32 DataSizeSent = 0;
|
||||
|
||||
switch(BufferAt->AddressType)
|
||||
{
|
||||
case AddressType_NetworkIP:
|
||||
{
|
||||
Win32Socket_SendTo(BufferAt->SendSocket,
|
||||
BufferAt->V4SendAddress,
|
||||
BufferAt->SendPort,
|
||||
(const char*)BufferAt->Memory,
|
||||
BufferAt->MemorySize,
|
||||
0);
|
||||
}break;
|
||||
|
||||
case AddressType_ComPort:
|
||||
{
|
||||
if (BufferAt->ComPort.Length > 0)
|
||||
{
|
||||
HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient);
|
||||
if (SerialPort != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (Win32SerialPort_Write(SerialPort, BufferAt->Data))
|
||||
{
|
||||
BuffersSent += 1;
|
||||
DataSizeSent += BufferAt->Data.Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
Win32SerialArray_Close(BufferAt->ComPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
Log_Message(GlobalLogBuffer,
|
||||
"Skipping data buffer because its COM Port isn't set");
|
||||
#endif
|
||||
}
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32_SendAddressedDataBuffer_Job(gs_thread_context Context, gs_data Arg)
|
||||
{
|
||||
addressed_data_buffer* OutputData = (addressed_data_buffer*)Arg.Memory;
|
||||
Win32_SendAddressedDataBuffer(Context, OutputData);
|
||||
}
|
||||
|
||||
internal bool
|
||||
ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError, bool AppReady)
|
||||
{
|
||||
bool Success = false;
|
||||
if (HotLoadDLL(DLL))
|
||||
{
|
||||
SetApplicationLinks(Context, *DLL, WorkQueue);
|
||||
Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady);
|
||||
Success = true;
|
||||
Log_Message(GlobalLogBuffer, "Reloaded DLL\n");
|
||||
}
|
||||
else if(ShouldError)
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n");
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal gs_const_string
|
||||
GetExePath(HINSTANCE HInstance, gs_thread_context ThreadContext)
|
||||
{
|
||||
gs_const_string Result = {};
|
||||
|
||||
u32 Error = 0;
|
||||
u32 PathSize = MAX_PATH;
|
||||
char* Path = PushArray(ThreadContext.Transient, char, PathSize);
|
||||
DWORD Length = GetModuleFileNameA(HInstance, Path, PathSize);
|
||||
|
||||
if (Length)
|
||||
{
|
||||
Error = GetLastError();
|
||||
if (Error == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// PathSize wasn't long enough
|
||||
// TODO(pjs): handle this case
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
Result.Str = Path;
|
||||
Result.Length = (u64)Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext)
|
||||
{
|
||||
bool Result = false;
|
||||
|
||||
gs_const_string ExePath = GetExePath(HInstance, ThreadContext);
|
||||
gs_string ScratchPath = PushString(ThreadContext.Transient, ExePath.Length + 128);
|
||||
gs_string WorkingDirectory = PushString(ThreadContext.Transient, ExePath.Length + 128);
|
||||
|
||||
while (WorkingDirectory.Length == 0)
|
||||
{
|
||||
s64 LastSlash = FindLastFromSet(ExePath, "\\/");
|
||||
if (LastSlash < 0) break;
|
||||
|
||||
ExePath = Substring(ExePath, 0, LastSlash);
|
||||
PrintF(&ScratchPath, "%S\\data", ExePath);
|
||||
NullTerminate(&ScratchPath);
|
||||
|
||||
gs_file_info PathInfo = GetFileInfo(ThreadContext.FileHandler, ScratchPath.ConstString);
|
||||
if (PathInfo.Path.Length > 0 &&
|
||||
PathInfo.IsDirectory) {
|
||||
PrintF(&WorkingDirectory, "%S", ExePath);
|
||||
NullTerminate(&WorkingDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
if (WorkingDirectory.Length > 0)
|
||||
{
|
||||
Log_Message(GlobalLogBuffer,
|
||||
"Setting Working Directory \n%S \n",
|
||||
WorkingDirectory.ConstString);
|
||||
Result = SetCurrentDirectory(WorkingDirectory.Str);
|
||||
if (!Result)
|
||||
{
|
||||
u32 Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Error, no data folder found\n");
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#include "../../gs_libs/gs_path.h"
|
||||
|
||||
internal void
|
||||
Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list OutputData)
|
||||
{
|
||||
bool Multithread = true;
|
||||
if (Multithread)
|
||||
{
|
||||
for (addressed_data_buffer* At = OutputData.Root;
|
||||
At != 0;
|
||||
At = At->Next)
|
||||
{
|
||||
gs_data ProcArg = {};
|
||||
ProcArg.Memory = (u8*)At;
|
||||
ProcArg.Size = sizeof(addressed_data_buffer);
|
||||
Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (addressed_data_buffer* At = OutputData.Root;
|
||||
At != 0;
|
||||
At = At->Next)
|
||||
{
|
||||
gs_data ProcArg = {};
|
||||
ProcArg.Memory = (u8*)At;
|
||||
ProcArg.Size = sizeof(addressed_data_buffer);
|
||||
Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Time
|
||||
internal system_time
|
||||
Win32GetSystemTime()
|
||||
{
|
||||
system_time Result = {};
|
||||
|
||||
SYSTEMTIME WinLocalTime;
|
||||
GetLocalTime(&WinLocalTime);
|
||||
|
||||
SYSTEMTIME WinSysTime;
|
||||
FILETIME WinSysFileTime;
|
||||
GetSystemTime(&WinSysTime);
|
||||
if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime))
|
||||
{
|
||||
ULARGE_INTEGER SysTime = {};
|
||||
SysTime.LowPart = WinSysFileTime.dwLowDateTime;
|
||||
SysTime.HighPart = WinSysFileTime.dwHighDateTime;
|
||||
|
||||
Result.NanosSinceEpoch = SysTime.QuadPart;
|
||||
Result.Year = WinLocalTime.wYear;
|
||||
Result.Month = WinLocalTime.wMonth;
|
||||
Result.Day = WinLocalTime.wDay;
|
||||
Result.Hour = WinLocalTime.wHour;
|
||||
Result.Minute = WinLocalTime.wMinute;
|
||||
Result.Second = WinLocalTime.wSecond;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int WINAPI
|
||||
WinMain (
|
||||
HINSTANCE HInstance,
|
||||
HINSTANCE HPrevInstance,
|
||||
PSTR CmdLineArgs,
|
||||
INT NCmdShow
|
||||
)
|
||||
{
|
||||
gs_thread_context ThreadContext = Win32CreateThreadContext();
|
||||
|
||||
gs_memory_arena PlatformPermanent = MemoryArenaCreate(MB(4),
|
||||
Bytes(8), ThreadContext.Allocator,
|
||||
0,
|
||||
0,
|
||||
"Platform Memory");
|
||||
|
||||
GlobalLogBuffer = AllocStruct(ThreadContext.Allocator, log_buffer, "Global Log Buffer");
|
||||
*GlobalLogBuffer = Log_Init(&PlatformPermanent, 32);
|
||||
|
||||
if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1;
|
||||
|
||||
context Context = {};
|
||||
Context.ThreadContext = ThreadContext;
|
||||
Context.SystemTime_Current = Win32GetSystemTime();
|
||||
|
||||
gs_const_string Args = ConstString((char*)CmdLineArgs);
|
||||
if (StringsEqual(Args, ConstString("-headless")))
|
||||
{
|
||||
Log_Message(GlobalLogBuffer, "Running Headless\n");
|
||||
Context.Headless = true;
|
||||
}
|
||||
|
||||
if (!Context.Headless)
|
||||
{
|
||||
MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents);
|
||||
Win32UpdateWindowDimension(&MainWindow);
|
||||
|
||||
win32_opengl_window_info OpenGLWindowInfo = {};
|
||||
OpenGLWindowInfo.ColorBits = 32;
|
||||
OpenGLWindowInfo.AlphaBits = 8;
|
||||
OpenGLWindowInfo.DepthBits = 0;
|
||||
CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow);
|
||||
}
|
||||
|
||||
s64 PerformanceCountFrequency = GetPerformanceFrequency();
|
||||
s64 LastFrameEnd = GetWallClock();
|
||||
r32 TargetSecondsPerFrame = 1 / 30.0f;
|
||||
r32 LastFrameSecondsElapsed = 0.0f;
|
||||
|
||||
GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services);
|
||||
#if DEBUG
|
||||
InitDebugServices_DebugMode(GlobalDebugServices,
|
||||
PerformanceCountFrequency,
|
||||
GetWallClock,
|
||||
Win32GetThreadId,
|
||||
Context.ThreadContext,
|
||||
PLATFORM_THREAD_COUNT + 1);
|
||||
#else
|
||||
InitDebugServices_OffMode(GlobalDebugServices,
|
||||
PerformanceCountFrequency,
|
||||
GetWallClock,
|
||||
Win32GetThreadId,
|
||||
Context.ThreadContext,
|
||||
PLATFORM_THREAD_COUNT + 1);
|
||||
#endif
|
||||
|
||||
input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32);
|
||||
|
||||
Win32WorkQueue_Init(&PlatformPermanent, PLATFORM_THREAD_COUNT);
|
||||
|
||||
// Platform functions
|
||||
Context.GeneralWorkQueue = &Win32WorkQueue.WorkQueue;
|
||||
Context.PlatformGetGPUTextureHandle = Win32GetGPUTextureHandle;
|
||||
Context.PlatformGetSocketHandle = Win32GetSocketHandle;
|
||||
Context.PlatformGetFontInfo = Win32GetFontInfo;
|
||||
Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint;
|
||||
|
||||
Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager);
|
||||
*Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread);
|
||||
|
||||
Context.SocketManager = PushStruct(&PlatformPermanent, platform_socket_manager);
|
||||
*Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend);
|
||||
|
||||
win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName);
|
||||
if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; }
|
||||
|
||||
Mouse_Init();
|
||||
|
||||
Win32SocketSystem_Init(&PlatformPermanent);
|
||||
|
||||
Win32SerialArray_Create(&PlatformPermanent);
|
||||
|
||||
render_command_buffer RenderBuffer = {};
|
||||
if (!Context.Headless)
|
||||
{
|
||||
RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext);
|
||||
}
|
||||
|
||||
addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext);
|
||||
|
||||
Context.InitializeApplication(&Context);
|
||||
|
||||
system_time StartTime = Win32GetSystemTime();
|
||||
|
||||
Running = true;
|
||||
Context.WindowIsVisible = true;
|
||||
while (Running)
|
||||
{
|
||||
if (GlobalDebugServices->RecordFrames)
|
||||
{
|
||||
EndDebugFrame(GlobalDebugServices);
|
||||
}
|
||||
|
||||
DEBUG_TRACK_SCOPE(MainLoop);
|
||||
|
||||
{
|
||||
Context.SystemTime_Last = Context.SystemTime_Current;
|
||||
Context.SystemTime_Current = Win32GetSystemTime();
|
||||
|
||||
#define PRINT_SYSTEM_TIME 0
|
||||
#if PRINT_SYSTEM_TIME
|
||||
gs_string T = PushStringF(Context.ThreadContext.Transient,
|
||||
256,
|
||||
"%d %d %d - %lld\n",
|
||||
Context.SystemTime_Current.Hour,
|
||||
Context.SystemTime_Current.Minute,
|
||||
Context.SystemTime_Current.Second,
|
||||
Context.SystemTime_Current.NanosSinceEpoch);
|
||||
|
||||
u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch;
|
||||
r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds;
|
||||
|
||||
Log_Message(GlobalLogBuffer,
|
||||
"%lld %f Seconds \n",
|
||||
NanosElapsed,
|
||||
SecondsElapsed);
|
||||
#endif
|
||||
}
|
||||
|
||||
ResetInputQueue(&InputQueue);
|
||||
|
||||
ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true);
|
||||
|
||||
AddressedDataBufferList_Clear(&OutputData);
|
||||
|
||||
if (!Context.Headless)
|
||||
{
|
||||
Mouse_Update(MainWindow, &Context);
|
||||
MSG Message;
|
||||
while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE))
|
||||
{
|
||||
DEBUG_TRACK_SCOPE(PeekWindowsMessages);
|
||||
HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse);
|
||||
}
|
||||
|
||||
Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}};
|
||||
RenderBuffer.ViewWidth = MainWindow.Width;
|
||||
RenderBuffer.ViewHeight = MainWindow.Height;
|
||||
}
|
||||
|
||||
Context.DeltaTime = LastFrameSecondsElapsed;
|
||||
Context.TotalTime += (r64)Context.DeltaTime;
|
||||
|
||||
Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData);
|
||||
|
||||
Win32_SendOutputData(ThreadContext, OutputData);
|
||||
|
||||
if (!Context.Headless)
|
||||
{
|
||||
RenderCommandBuffer(RenderBuffer);
|
||||
ClearRenderBuffer(&RenderBuffer);
|
||||
|
||||
Mouse_Advance(&Context);
|
||||
|
||||
HDC DeviceContext = GetDC(MainWindow.Handle);
|
||||
SwapBuffers(DeviceContext);
|
||||
ReleaseDC(MainWindow.Handle, DeviceContext);
|
||||
}
|
||||
|
||||
Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext);
|
||||
|
||||
s64 FinishedWorkTime = GetWallClock();
|
||||
r32 SecondsElapsed = GetSecondsElapsed(LastFrameEnd, FinishedWorkTime, PerformanceCountFrequency);
|
||||
|
||||
while (SecondsElapsed < TargetSecondsPerFrame)
|
||||
{
|
||||
DEBUG_TRACK_SCOPE(UnusedTime);
|
||||
u32 SleepTime = 1000.0f * (TargetSecondsPerFrame - SecondsElapsed);
|
||||
Sleep(SleepTime);
|
||||
SecondsElapsed = GetSecondsElapsed(LastFrameEnd, GetWallClock(), PerformanceCountFrequency);
|
||||
}
|
||||
|
||||
LastFrameSecondsElapsed = SecondsElapsed;
|
||||
LastFrameEnd = GetWallClock();
|
||||
}
|
||||
|
||||
Context.CleanupApplication(Context, &OutputData);
|
||||
Win32_SendOutputData(ThreadContext, OutputData);
|
||||
Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext);
|
||||
|
||||
Win32WorkQueue_Cleanup();
|
||||
Win32SocketSystem_Cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_CPP
|
||||
#endif // WIN32_FOLDHAUS_CPP
|
|
@ -1,162 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus_dll.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-02-04
|
||||
//
|
||||
//
|
||||
// NOTE: Relies on having imported foldhaus_platform.h prior to this file
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_DLL_H
|
||||
|
||||
// DLL
|
||||
struct win32_dll_refresh
|
||||
{
|
||||
FILETIME LastWriteTime;
|
||||
HMODULE DLL;
|
||||
|
||||
bool IsValid;
|
||||
|
||||
char SourceDLLPath[MAX_PATH];
|
||||
char WorkingDLLPath[MAX_PATH];
|
||||
char LockFilePath[MAX_PATH];
|
||||
};
|
||||
|
||||
internal int
|
||||
Win32DLLgs_stringLength(char* gs_string)
|
||||
{
|
||||
char* At = gs_string;
|
||||
while (*At) { At++; };
|
||||
return At - gs_string;
|
||||
}
|
||||
|
||||
internal int
|
||||
Win32DLLConcatgs_strings(int ALength, char* A, int BLength, char* B, int DestLength, char* Dest)
|
||||
{
|
||||
char* Dst = Dest;
|
||||
char* AAt = A;
|
||||
int ALengthToCopy = ALength < DestLength ? ALength : DestLength;
|
||||
for (s32 a = 0; a < ALength; a++)
|
||||
{
|
||||
*Dst++ = *AAt++;
|
||||
}
|
||||
char* BAt = B;
|
||||
int DestLengthRemaining = DestLength - (Dst - Dest);
|
||||
int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength;
|
||||
for (s32 b = 0; b < BLengthToCopy; b++)
|
||||
{
|
||||
*Dst++ = *BAt++;
|
||||
}
|
||||
int DestLengthOut = Dst - Dest;
|
||||
int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength;
|
||||
Dest[NullTermIndex] = 0;
|
||||
return DestLengthOut;
|
||||
}
|
||||
|
||||
internal void
|
||||
GetApplicationPath(system_path* Result)
|
||||
{
|
||||
Assert(Result->Path);
|
||||
Result->PathLength = GetModuleFileNameA(0, Result->Path, Result->PathLength);
|
||||
|
||||
u32 CharactersScanned = 0;
|
||||
u32 IndexOfLastSlash = 0;
|
||||
char *Scan = Result->Path;
|
||||
while(*Scan)
|
||||
{
|
||||
if (*Scan == '\\')
|
||||
{
|
||||
Result->IndexOfLastSlash = CharactersScanned + 1;
|
||||
}
|
||||
Scan++;
|
||||
CharactersScanned++;
|
||||
}
|
||||
}
|
||||
|
||||
internal b32
|
||||
LoadApplicationDLL(char* DLLName, win32_dll_refresh* DLLResult)
|
||||
{
|
||||
b32 Success = false;
|
||||
Assert(DLLResult->DLL == 0);
|
||||
|
||||
DLLResult->DLL = LoadLibraryA(DLLName);
|
||||
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;
|
||||
|
||||
system_path ExePath = {};
|
||||
ExePath.PathLength = MAX_PATH;
|
||||
ExePath.Path = (char*)VirtualAlloc(NULL, ExePath.PathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
GetApplicationPath(&ExePath);
|
||||
|
||||
Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
|
||||
Win32DLLgs_stringLength(SourceDLLName), SourceDLLName,
|
||||
MAX_PATH, Result.SourceDLLPath);
|
||||
Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
|
||||
Win32DLLgs_stringLength(WorkingDLLFileName), WorkingDLLFileName,
|
||||
MAX_PATH, Result.WorkingDLLPath);
|
||||
Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
|
||||
Win32DLLgs_stringLength(LockFileName), LockFileName,
|
||||
MAX_PATH, Result.LockFilePath);
|
||||
|
||||
Win32Free((u8*)ExePath.Path, ExePath.PathLength, 0);
|
||||
return Result;
|
||||
|
||||
}
|
||||
|
||||
internal b32
|
||||
HotLoadDLL(win32_dll_refresh* DLL)
|
||||
{
|
||||
b32 DidReload = false;
|
||||
|
||||
FILETIME UpdatedLastWriteTime = {};
|
||||
WIN32_FIND_DATA FindData = {};
|
||||
HANDLE FileHandle = FindFirstFileA(DLL->SourceDLLPath, &FindData);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
UpdatedLastWriteTime = FindData.ftLastWriteTime;
|
||||
FindClose(FileHandle);
|
||||
}
|
||||
|
||||
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 WIN32_FOLDHAUS_DLL_H
|
||||
#endif // WIN32_FOLDHAUS_DLL_H
|
|
@ -1,283 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus_fileio.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-02-04
|
||||
//
|
||||
//
|
||||
// NOTE: Relies on having imported foldhaus_platform.h prior to this file
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_FILEIO_H
|
||||
|
||||
internal u64
|
||||
Win32HighLowToU64(u32 LowPart, u32 HighPart)
|
||||
{
|
||||
ULARGE_INTEGER Time = {};
|
||||
Time.LowPart = LowPart;
|
||||
Time.HighPart = HighPart;
|
||||
u64 Result = Time.QuadPart;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u64
|
||||
Win32FileTimeToU64(FILETIME FileTime)
|
||||
{
|
||||
u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime);
|
||||
return Result;
|
||||
}
|
||||
|
||||
GET_FILE_INFO(Win32GetFileInfo)
|
||||
{
|
||||
Assert(IsNullTerminated(Path));
|
||||
gs_file_info Result = {};
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result.Path = Path;
|
||||
Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1;
|
||||
FILETIME CreationTime, LastWriteTime;
|
||||
if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime))
|
||||
{
|
||||
Result.CreationTime = Win32FileTimeToU64(CreationTime);
|
||||
Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD FileAttr = GetFileAttributes(Path.Str);
|
||||
if (FileAttr != INVALID_FILE_ATTRIBUTES &&
|
||||
(FileAttr & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
Result.Path = Path;
|
||||
Result.IsDirectory = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Path is not a file or directory
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
READ_ENTIRE_FILE(Win32ReadEntireFile)
|
||||
{
|
||||
Assert(DataIsNonEmpty(Memory));
|
||||
Assert(IsNullTerminated(Path));
|
||||
|
||||
gs_file Result = {0};
|
||||
u32 Error = 0;
|
||||
Result.FileInfo.Path = Path;
|
||||
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesRead = 0;
|
||||
if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL))
|
||||
{
|
||||
Memory.Memory[Memory.Size - 1] = 0;
|
||||
Result.Data = Memory;
|
||||
|
||||
gs_string AbsolutePath = PushString(FileHandler.Transient, 512);
|
||||
AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL);
|
||||
if (AbsolutePath.Length == 0)
|
||||
{
|
||||
Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
Result.FileInfo.AbsolutePath = AbsolutePath.ConstString;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(Peter): If we get to this error case, it means that the file exists,
|
||||
// and was successfully opened, but we can't read from it. I'm choosing to
|
||||
// treat this as a legitimate error at this point.
|
||||
gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path);
|
||||
if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK)
|
||||
{
|
||||
PostQuitMessage(-1);
|
||||
}
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
WRITE_ENTIRE_FILE(Win32WriteEntireFile)
|
||||
{
|
||||
Assert(DataIsNonEmpty(Data));
|
||||
Assert(IsNullTerminated(Path));
|
||||
|
||||
bool Success = false;
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
|
||||
{
|
||||
Success = (BytesWritten == Data.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path);
|
||||
MessageBox(NULL, Message.Str, "Error", MB_OK);
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
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): :ErrorLogging
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
struct temp_file_list_entry
|
||||
{
|
||||
gs_file_info Info;
|
||||
temp_file_list_entry* Next;
|
||||
};
|
||||
|
||||
struct temp_file_list
|
||||
{
|
||||
temp_file_list_entry* First;
|
||||
temp_file_list_entry* Last;
|
||||
};
|
||||
|
||||
internal void
|
||||
Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileData, gs_const_string SearchPath, gs_memory_arena* Storage)
|
||||
{
|
||||
u32 FileNameLength = CharArrayLength(FindFileData.cFileName);
|
||||
|
||||
Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh);
|
||||
Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime);
|
||||
Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime);
|
||||
Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
// NOTE(Peter): String Storage
|
||||
// Storing the string in the final storage means we don't have to copy the string later, and all
|
||||
// strings will be continguous in memory at the calling site, though they will be before the array
|
||||
gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 2);
|
||||
PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName);
|
||||
if (Info->IsDirectory)
|
||||
{
|
||||
AppendPrintF(&FileName, "\\");
|
||||
}
|
||||
NullTerminate(&FileName);
|
||||
|
||||
Info->Path = FileName.ConstString;
|
||||
}
|
||||
|
||||
internal u32
|
||||
Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags)
|
||||
{
|
||||
u32 FilesCount = 0;
|
||||
Assert(Path.Str[Path.Length - 1] != '\\' &&
|
||||
Path.Str[Path.Length - 1] != '/');
|
||||
gs_const_string SearchPath = Path;
|
||||
|
||||
gs_const_string SearchPathDir = SearchPath;
|
||||
s64 LastSlash = FindLastFromSet(SearchPath, "\\/");
|
||||
if (LastSlash >= 0)
|
||||
{
|
||||
SearchPathDir = Substring(SearchPath, 0, LastSlash + 1);
|
||||
}
|
||||
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData);
|
||||
if (SearchHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
b32 AddFile = true;
|
||||
|
||||
if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
gs_const_string SubDirName = ConstString(FindFileData.cFileName);
|
||||
bool IsNav = (StringsEqual(SubDirName, ConstString(".")) ||
|
||||
StringsEqual(SubDirName, ConstString("..")));
|
||||
|
||||
if (HasFlag(Flags, EnumerateDirectory_Recurse))
|
||||
{
|
||||
if (!IsNav)
|
||||
{
|
||||
gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3);
|
||||
PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName);
|
||||
FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString,
|
||||
Storage, Flags);
|
||||
}
|
||||
}
|
||||
|
||||
AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories);
|
||||
}
|
||||
|
||||
if (AddFile)
|
||||
{
|
||||
temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry);
|
||||
*File = {0};
|
||||
Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPathDir, Storage);
|
||||
SLLPushOrInit(TempList->First, TempList->Last, File);
|
||||
FilesCount += 1;
|
||||
}
|
||||
}while(FindNextFile(SearchHandle, &FindFileData));
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
|
||||
return FilesCount;
|
||||
}
|
||||
|
||||
ENUMERATE_DIRECTORY(Win32EnumerateDirectory)
|
||||
{
|
||||
Assert(IsNullTerminated(Path));
|
||||
gs_file_info_array Result = {};
|
||||
|
||||
temp_file_list TempList = {};
|
||||
Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags);
|
||||
|
||||
Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount);
|
||||
for (temp_file_list_entry* FileAt = TempList.First;
|
||||
FileAt != 0;
|
||||
FileAt = FileAt->Next)
|
||||
{
|
||||
// NOTE(Peter): We don't copy the file name here because its already in Storage.
|
||||
// See String Storage note above ^^
|
||||
Result.Values[Result.Count++] = FileAt->Info;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_FILEIO_H
|
||||
#endif // WIN32_FOLDHAUS_FILEIO_H
|
|
@ -1,26 +0,0 @@
|
|||
/* date = May 10th 2021 11:48 pm */
|
||||
|
||||
#ifndef GS_MEMORY_WIN32_H
|
||||
#define GS_MEMORY_WIN32_H
|
||||
|
||||
PLATFORM_ALLOC(Win32Alloc)
|
||||
{
|
||||
u8* Result = (u8*)VirtualAlloc(NULL, Size,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
if (ResultSize) *ResultSize = Size;
|
||||
return Result;
|
||||
}
|
||||
|
||||
PLATFORM_FREE(Win32Free)
|
||||
{
|
||||
VirtualFree(Base, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
internal gs_allocator
|
||||
CreatePlatformAllocator()
|
||||
{
|
||||
return AllocatorCreate(Win32Alloc, Win32Free, 0);
|
||||
}
|
||||
|
||||
#endif //GS_MEMORY_WIN32_H
|
|
@ -1,118 +0,0 @@
|
|||
//
|
||||
// File: win32_mouse.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-10
|
||||
//
|
||||
#ifndef WIN32_MOUSE_H
|
||||
|
||||
HCURSOR CursorArrow;
|
||||
HCURSOR CursorPointer;
|
||||
HCURSOR CursorLoading;
|
||||
HCURSOR CursorHArrows;
|
||||
HCURSOR CursorVArrows;
|
||||
HCURSOR CursorDTopLeftArrows;
|
||||
HCURSOR CursorDTopRightArrows;
|
||||
|
||||
HCURSOR CurrentCursor;
|
||||
|
||||
// NOTE(Peter): Only meant to take one of the values specified below:
|
||||
// IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM,
|
||||
// IDC_ICON, IDC_NO, IDC_SIZE, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE,
|
||||
// IDC_SIZEWE, IDC_UPARROW, IDC_WAIT
|
||||
internal HCURSOR
|
||||
Win32LoadSystemCursor(char* CursorIdentifier)
|
||||
{
|
||||
u32 Error = 0;
|
||||
HCURSOR Result = LoadCursorA(NULL, CursorIdentifier);
|
||||
if (Result == NULL)
|
||||
{
|
||||
Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Mouse_Init()
|
||||
{
|
||||
CursorArrow = Win32LoadSystemCursor(IDC_ARROW);
|
||||
CursorPointer = Win32LoadSystemCursor(IDC_HAND);
|
||||
CursorLoading = Win32LoadSystemCursor(IDC_WAIT);
|
||||
CursorHArrows = Win32LoadSystemCursor(IDC_SIZEWE);
|
||||
CursorVArrows = Win32LoadSystemCursor(IDC_SIZENS);
|
||||
CursorDTopLeftArrows = Win32LoadSystemCursor(IDC_SIZENWSE);
|
||||
CursorDTopRightArrows = Win32LoadSystemCursor(IDC_SIZENESW);
|
||||
}
|
||||
|
||||
internal void
|
||||
Mouse_Update(window Window, context* Context)
|
||||
{
|
||||
POINT Pos;
|
||||
GetCursorPos (&Pos);
|
||||
ScreenToClient(Window.Handle, &Pos);
|
||||
|
||||
Context->Mouse.Scroll = 0;
|
||||
Context->Mouse.OldPos = Context->Mouse.Pos;
|
||||
Context->Mouse.Pos = v2{(r32)Pos.x, (r32)Window.Height - Pos.y};
|
||||
Context->Mouse.DeltaPos = Context->Mouse.Pos - Context->Mouse.OldPos;
|
||||
|
||||
if (KeyIsDown(Context->Mouse.LeftButtonState))
|
||||
{
|
||||
SetKeyWasDown(Context->Mouse.LeftButtonState);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetKeyWasUp(Context->Mouse.LeftButtonState);
|
||||
}
|
||||
|
||||
if (KeyIsDown(Context->Mouse.MiddleButtonState))
|
||||
{
|
||||
SetKeyWasDown(Context->Mouse.MiddleButtonState);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetKeyWasUp(Context->Mouse.MiddleButtonState);
|
||||
}
|
||||
|
||||
|
||||
if (KeyIsDown(Context->Mouse.RightButtonState))
|
||||
{
|
||||
SetKeyWasDown(Context->Mouse.RightButtonState);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetKeyWasUp(Context->Mouse.RightButtonState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void
|
||||
Mouse_Advance(context* Context)
|
||||
{
|
||||
Context->Mouse.LeftButtonState = GetMouseButtonStateAdvanced(Context->Mouse.LeftButtonState);
|
||||
Context->Mouse.MiddleButtonState = GetMouseButtonStateAdvanced(Context->Mouse.MiddleButtonState);
|
||||
Context->Mouse.RightButtonState = GetMouseButtonStateAdvanced(Context->Mouse.RightButtonState);
|
||||
|
||||
HCURSOR NewCursor = 0;
|
||||
switch (Context->Mouse.CursorType)
|
||||
{
|
||||
case CursorType_Arrow: { NewCursor = CursorArrow; } break;
|
||||
case CursorType_Pointer: { NewCursor = CursorPointer; }break;
|
||||
case CursorType_Loading: { NewCursor = CursorLoading; }break;
|
||||
case CursorType_HArrows: { NewCursor = CursorHArrows; }break;
|
||||
case CursorType_VArrows: { NewCursor = CursorVArrows; }break;
|
||||
case CursorType_DTopLeftArrows: { NewCursor = CursorDTopLeftArrows; }break;
|
||||
case CursorType_DTopRightArrows: { NewCursor = CursorDTopRightArrows; }break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
if (NewCursor != CurrentCursor)
|
||||
{
|
||||
CurrentCursor = NewCursor;
|
||||
SetCursor(NewCursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define WIN32_MOUSE_H
|
||||
#endif // WIN32_MOUSE_H
|
|
@ -1,380 +0,0 @@
|
|||
//
|
||||
// File: win32_serial.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
#ifndef WIN32_SERIAL_H
|
||||
|
||||
global u32 Win32SerialHandlesCountMax;
|
||||
global HANDLE* Win32SerialHandles;
|
||||
global gs_string* Win32SerialPortNames;
|
||||
global s32* Win32SerialPortFilled;
|
||||
|
||||
DCB
|
||||
Win32SerialPort_GetState(HANDLE ComPortHandle)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
DCB ControlSettings = {0};
|
||||
ZeroStruct(&ControlSettings);
|
||||
ControlSettings.DCBlength = sizeof(ControlSettings);
|
||||
|
||||
bool Success = GetCommState(ComPortHandle, &ControlSettings);
|
||||
Assert(Success);
|
||||
|
||||
return ControlSettings;
|
||||
}
|
||||
|
||||
void
|
||||
Win32SerialPort_SetState(HANDLE ComPortHandle, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
DCB ControlSettings = Win32SerialPort_GetState(ComPortHandle);
|
||||
|
||||
// TODO(pjs): Validate BaudRate - There's only certain rates that are valid right?
|
||||
ControlSettings.BaudRate = BaudRate;
|
||||
|
||||
if (Parity == NOPARITY)
|
||||
{
|
||||
ControlSettings.Parity = Parity;
|
||||
ControlSettings.fParity = 0;
|
||||
}
|
||||
if (Parity == EVENPARITY || Parity == ODDPARITY)
|
||||
{
|
||||
ControlSettings.Parity = Parity;
|
||||
ControlSettings.fParity = 1;
|
||||
}
|
||||
|
||||
ControlSettings.StopBits = StopBits;
|
||||
ControlSettings.ByteSize = ByteSize;
|
||||
|
||||
ControlSettings.fBinary = true;
|
||||
|
||||
ControlSettings.fOutxCtsFlow = false;
|
||||
ControlSettings.fOutxDsrFlow = false;
|
||||
ControlSettings.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
ControlSettings.fDsrSensitivity = 0;
|
||||
ControlSettings.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
ControlSettings.fOutX = false;
|
||||
ControlSettings.fInX = false;
|
||||
|
||||
ControlSettings.fErrorChar = 0;
|
||||
ControlSettings.fNull = false;
|
||||
ControlSettings.fAbortOnError = false;
|
||||
ControlSettings.wReserved = false;
|
||||
ControlSettings.XonLim = 2;
|
||||
ControlSettings.XoffLim = 4;
|
||||
ControlSettings.XonChar = 0x13;
|
||||
ControlSettings.XoffChar = 0x19;
|
||||
ControlSettings.EvtChar = 0;
|
||||
|
||||
bool Success = SetCommState(ComPortHandle, &ControlSettings);
|
||||
}
|
||||
|
||||
gs_const_string_array
|
||||
Win32SerialPorts_List(gs_memory_arena* Arena, gs_memory_arena* Transient)
|
||||
{
|
||||
gs_const_string_array Result = {};
|
||||
|
||||
DWORD SizeNeeded0 = 0;
|
||||
DWORD CountReturned0 = 0;
|
||||
EnumPorts(NULL, 1, 0, 0, &SizeNeeded0, &CountReturned0);
|
||||
Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
||||
|
||||
DWORD SizeNeeded1 = 0;
|
||||
DWORD CountReturned1 = 0;
|
||||
PORT_INFO_1* PortsArray = (PORT_INFO_1*)PushSize(Transient, SizeNeeded0).Memory;
|
||||
if (EnumPorts(NULL,
|
||||
1,
|
||||
(u8*)PortsArray,
|
||||
SizeNeeded0,
|
||||
&SizeNeeded1,
|
||||
&CountReturned1))
|
||||
{
|
||||
Result.CountMax = (u64)CountReturned1;
|
||||
Result.Strings = PushArray(Arena, gs_const_string, Result.CountMax);
|
||||
|
||||
for (; Result.Count < Result.CountMax; Result.Count++)
|
||||
{
|
||||
u64 Index = Result.Count;
|
||||
u64 StrLen = CStringLength(PortsArray[Index].pName);
|
||||
gs_string Str = PushString(Arena, StrLen);
|
||||
PrintF(&Str, "%.*s", StrLen, PortsArray[Index].pName);
|
||||
Result.Strings[Result.Count] = Str.ConstString;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool
|
||||
Win32SerialPort_Exists(char* PortName, gs_memory_arena* Transient)
|
||||
{
|
||||
bool Result = false;
|
||||
if (PortName != 0)
|
||||
{
|
||||
gs_const_string PortIdent = ConstString(PortName);
|
||||
u32 IdentBegin = FindLast(PortIdent, '\\') + 1;
|
||||
PortIdent = Substring(PortIdent, IdentBegin, PortIdent.Length);
|
||||
|
||||
gs_const_string_array PortsAvailable = Win32SerialPorts_List(Transient, Transient);
|
||||
|
||||
for (u64 i = 0; i < PortsAvailable.Count; i++)
|
||||
{
|
||||
gs_const_string AvailablePortName = PortsAvailable.Strings[i];
|
||||
if (StringsEqualUpToLength(AvailablePortName, PortIdent, PortIdent.Length))
|
||||
{
|
||||
Result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
HANDLE
|
||||
Win32SerialPort_Open(char* PortName, gs_memory_arena* Transient)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
HANDLE ComPortHandle = INVALID_HANDLE_VALUE;;
|
||||
|
||||
if (Win32SerialPort_Exists(PortName, Transient))
|
||||
{
|
||||
|
||||
ComPortHandle = CreateFile(PortName,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, // Default Security Attr
|
||||
OPEN_EXISTING,
|
||||
0, // Not overlapped I/O
|
||||
NULL);
|
||||
|
||||
bool HasError = false;
|
||||
|
||||
if (ComPortHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
COMMTIMEOUTS Timeouts = { 0 };
|
||||
Timeouts.ReadIntervalTimeout = 0; // in milliseconds
|
||||
Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds
|
||||
Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds
|
||||
Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds
|
||||
Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds
|
||||
|
||||
HasError = !SetCommTimeouts(ComPortHandle, &Timeouts);
|
||||
}
|
||||
else
|
||||
{
|
||||
HasError = true;
|
||||
}
|
||||
|
||||
if (HasError)
|
||||
{
|
||||
// Error
|
||||
s32 Error = GetLastError();
|
||||
switch (Error)
|
||||
{
|
||||
case ERROR_INVALID_FUNCTION:
|
||||
case ERROR_NO_SUCH_DEVICE:
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
{
|
||||
// NOTE(PS): The outer scope should handle these cases
|
||||
ComPortHandle = INVALID_HANDLE_VALUE;
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ComPortHandle;
|
||||
}
|
||||
|
||||
void
|
||||
Win32SerialPort_Close(HANDLE PortHandle)
|
||||
{
|
||||
CloseHandle(PortHandle);
|
||||
}
|
||||
|
||||
bool
|
||||
Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
Assert(PortHandle != INVALID_HANDLE_VALUE);
|
||||
bool Success = false;
|
||||
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL))
|
||||
{
|
||||
Success = (BytesWritten == Buffer.Size);
|
||||
if (!Success)
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Error: Entire buffer not written.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Error: Unable to write to port\n");
|
||||
s32 Error = GetLastError();
|
||||
switch (Error)
|
||||
{
|
||||
case ERROR_OPERATION_ABORTED:
|
||||
case ERROR_GEN_FAILURE:
|
||||
{
|
||||
// NOTE(pjs): Probably means that the serial port became invalid
|
||||
// ie. the usb stick was removed
|
||||
}break;
|
||||
|
||||
case ERROR_ACCESS_DENIED:
|
||||
{
|
||||
// ??
|
||||
}break;
|
||||
|
||||
case ERROR_NO_SUCH_DEVICE:
|
||||
{
|
||||
}break;
|
||||
|
||||
case ERROR_INVALID_HANDLE:
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
bool
|
||||
Win32SerialPort_SetRead(HANDLE PortHandle)
|
||||
{
|
||||
bool Status = SetCommMask(PortHandle, EV_RXCHAR);
|
||||
return Status;
|
||||
}
|
||||
|
||||
u32
|
||||
Win32SerialPort_ReadMessageWhenReady(HANDLE PortHandle, gs_data Data)
|
||||
{
|
||||
u32 ReadSize = 0;
|
||||
|
||||
DWORD EventMask = 0;
|
||||
bool Status = WaitCommEvent(PortHandle, &EventMask, NULL);
|
||||
if (Status)
|
||||
{
|
||||
DWORD NoBytesRead = 0;
|
||||
do
|
||||
{
|
||||
u8 Byte = 0;
|
||||
Status = ReadFile(PortHandle, &Byte, sizeof(char), &NoBytesRead, NULL);
|
||||
Data.Memory[ReadSize] = Byte;
|
||||
ReadSize++;
|
||||
}
|
||||
while (NoBytesRead > 0 && ReadSize < Data.Size);
|
||||
}
|
||||
//Read data and store in a buffer
|
||||
|
||||
return ReadSize;
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Win32SerialArray
|
||||
|
||||
void
|
||||
Win32SerialArray_Create(gs_memory_arena* A)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
Win32SerialHandlesCountMax = 32;
|
||||
|
||||
Win32SerialHandles = PushArray(A, HANDLE, Win32SerialHandlesCountMax);
|
||||
Win32SerialPortNames = PushArray(A, gs_string, Win32SerialHandlesCountMax);
|
||||
Win32SerialPortFilled = PushArray(A, s32, Win32SerialHandlesCountMax);
|
||||
|
||||
u64 PortNameSize = 256;
|
||||
u64 PortNameBufferSize = PortNameSize * Win32SerialHandlesCountMax;
|
||||
char* PortNameBuffer = PushArray(A, char, PortNameBufferSize);
|
||||
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
||||
{
|
||||
char* NameBase = PortNameBuffer + (PortNameSize * i);
|
||||
Win32SerialPortNames[i] = MakeString(NameBase, 0, PortNameSize);
|
||||
Win32SerialPortFilled[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Win32SerialArray_Push(HANDLE SerialHandle, gs_const_string PortName)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
bool Found = false;
|
||||
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
||||
{
|
||||
bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0);
|
||||
if (!WasFilled)
|
||||
{
|
||||
Win32SerialHandles[i] = SerialHandle;
|
||||
PrintF(&Win32SerialPortNames[i], "%S", PortName);
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert(Found);
|
||||
}
|
||||
|
||||
void
|
||||
Win32SerialArray_Pop(u32 Index)
|
||||
{
|
||||
bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + Index, 0, 1);
|
||||
Assert(WasFilled);
|
||||
Win32SerialPortFilled[Index] = false;
|
||||
Win32SerialHandles[Index] = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
HANDLE
|
||||
Win32SerialArray_Get(gs_const_string PortName)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
HANDLE PortHandle = INVALID_HANDLE_VALUE;
|
||||
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
||||
{
|
||||
if (Win32SerialPortFilled[i] &&
|
||||
StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
|
||||
{
|
||||
PortHandle = Win32SerialHandles[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return PortHandle;
|
||||
}
|
||||
|
||||
HANDLE
|
||||
Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits, gs_memory_arena* Transient)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
HANDLE PortHandle = Win32SerialArray_Get(PortName);
|
||||
if (PortHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Assert(IsNullTerminated(PortName));
|
||||
PortHandle = Win32SerialPort_Open(PortName.Str, Transient);
|
||||
if (PortHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits);
|
||||
Win32SerialArray_Push(PortHandle, PortName);
|
||||
}
|
||||
}
|
||||
return PortHandle;
|
||||
}
|
||||
|
||||
void
|
||||
Win32SerialArray_Close(gs_const_string PortName)
|
||||
{
|
||||
for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
|
||||
{
|
||||
if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
|
||||
{
|
||||
Win32SerialPort_Close(Win32SerialHandles[i]);
|
||||
Win32SerialArray_Pop(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define WIN32_SERIAL_H
|
||||
#endif // WIN32_SERIAL_H
|
|
@ -1,539 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus_socket.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-03
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_SOCKET_H
|
||||
|
||||
struct win32_socket
|
||||
{
|
||||
SOCKET Socket;
|
||||
};
|
||||
|
||||
struct win32_socket_array
|
||||
{
|
||||
win32_socket* Values;
|
||||
s32 CountMax;
|
||||
s32 Count;
|
||||
};
|
||||
|
||||
global WSADATA WSAData;
|
||||
global win32_socket_array Win32Sockets;
|
||||
|
||||
//////////////////////
|
||||
//
|
||||
// Win32 Socket Array
|
||||
|
||||
internal win32_socket_array
|
||||
Win32SocketArray_Create(u32 CountMax, gs_memory_arena* Storage)
|
||||
{
|
||||
win32_socket_array Result = {};
|
||||
Result.CountMax = CountMax;
|
||||
Result.Values = PushArray(Storage, win32_socket, CountMax);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32SocketArray_Take(win32_socket_array* Array)
|
||||
{
|
||||
Assert(Array->Count < Array->CountMax);
|
||||
s32 Result = Array->Count++;
|
||||
win32_socket* Socket = Array->Values + Result;
|
||||
*Socket = {0};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal win32_socket*
|
||||
Win32SocketArray_Get(win32_socket_array Array, s32 Index)
|
||||
{
|
||||
Assert(Index < Array.Count);
|
||||
win32_socket* Result = Array.Values + Index;
|
||||
return Result;
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
//
|
||||
// Win32 Sockets
|
||||
|
||||
internal win32_socket
|
||||
Win32Socket_Create(s32 AddressFamily, s32 Type, s32 Protocol)
|
||||
{
|
||||
win32_socket Result = {0};
|
||||
Result.Socket = socket(AddressFamily, Type, Protocol);
|
||||
if (Result.Socket == INVALID_SOCKET)
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_Bind(win32_socket* Socket, s32 AddressFamily, char* Address, s32 Port)
|
||||
{
|
||||
sockaddr_in Service = {0};
|
||||
Service.sin_family = AddressFamily;
|
||||
Service.sin_addr.s_addr = inet_addr(Address);
|
||||
Service.sin_port = htons(Port);
|
||||
|
||||
s32 Result = bind(Socket->Socket, (SOCKADDR*)&Service, sizeof(Service));
|
||||
if (Result == SOCKET_ERROR)
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
|
||||
internal win32_socket
|
||||
Win32Socket_ConnectToAddress(char* Address, char* DefaultPort)
|
||||
{
|
||||
win32_socket Result = {};
|
||||
|
||||
addrinfo Hints = {0};
|
||||
Hints.ai_family = AF_UNSPEC;
|
||||
Hints.ai_socktype = SOCK_STREAM;
|
||||
Hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
addrinfo* PotentialConnections;
|
||||
s32 Error = getaddrinfo(Address, DefaultPort, &Hints, &PotentialConnections);
|
||||
if (Error == 0)
|
||||
{
|
||||
for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
|
||||
{
|
||||
win32_socket Socket = Win32Socket_Create(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
|
||||
if (Socket.Socket == INVALID_SOCKET)
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
Error = connect(Socket.Socket, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
|
||||
if (Error == SOCKET_ERROR)
|
||||
{
|
||||
closesocket(Socket.Socket);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = Socket;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
freeaddrinfo(PotentialConnections);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket)
|
||||
{
|
||||
bool Result = false;
|
||||
|
||||
addrinfo Hints = {0};
|
||||
Hints.ai_family = AF_UNSPEC;
|
||||
Hints.ai_socktype = SOCK_STREAM;
|
||||
Hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
addrinfo* PotentialConnections;
|
||||
s32 Error = getaddrinfo(Socket->Addr, Socket->Port, &Hints, &PotentialConnections);
|
||||
if (Error == 0)
|
||||
{
|
||||
for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
|
||||
{
|
||||
SOCKET SocketHandle = socket(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
|
||||
if (SocketHandle == INVALID_SOCKET)
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
// If iMode == 0, blocking is enabled
|
||||
// if iMode != 0, non-blocking mode is enabled
|
||||
u_long iMode = 0;
|
||||
Error = ioctlsocket(SocketHandle, FIONBIO, &iMode);
|
||||
if (Error != NO_ERROR)
|
||||
{
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
Error = connect(SocketHandle, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
|
||||
if (Error == SOCKET_ERROR)
|
||||
{
|
||||
u32 Status = WSAGetLastError();
|
||||
if (Status == WSAEWOULDBLOCK)
|
||||
{
|
||||
// Non-blocking sockets
|
||||
#if 0
|
||||
TIMEVAL Timeout = { 0, 500 };
|
||||
fd_set SocketSet = {};
|
||||
FD_ZERO(&SocketSet);
|
||||
FD_SET(SocketHandle, &SocketSet);
|
||||
Assert(FD_ISSET(SocketHandle, &SocketSet));
|
||||
Status = select(0, &SocketSet, 0, 0, (const TIMEVAL*)&Timeout);
|
||||
if (Status == SOCKET_ERROR)
|
||||
{
|
||||
|
||||
}
|
||||
else if (Status == 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
closesocket(SocketHandle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Socket->PlatformHandle = (u8*)Win32Alloc(sizeof(SOCKET), 0, 0);
|
||||
*(SOCKET*)Socket->PlatformHandle = SocketHandle;
|
||||
Result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
Assert(Socket->PlatformHandle == 0);
|
||||
}
|
||||
|
||||
freeaddrinfo(PotentialConnections);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Win32CloseSocket(platform_socket_manager* Manager, platform_socket* Socket)
|
||||
{
|
||||
SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
|
||||
closesocket(*Win32Sock);
|
||||
Win32Free((u8*)Socket->PlatformHandle, sizeof(SOCKET), 0);
|
||||
*Socket = {};
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool
|
||||
Win32SocketQueryStatus(platform_socket_manager* Manager, platform_socket* Socket)
|
||||
{
|
||||
SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
|
||||
bool Result = (*Win32Sock != INVALID_SOCKET);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u32
|
||||
Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket)
|
||||
{
|
||||
u32 Result = 0;
|
||||
s32 Flags = MSG_PEEK;
|
||||
SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
|
||||
char Temp[4];
|
||||
u32 TempSize = 4;
|
||||
|
||||
//Log_Message(GlobalLogBuffer, "Pre Peek...");
|
||||
//s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags);
|
||||
u_long BytesQueued = 0;
|
||||
ioctlsocket(*Win32Sock, FIONREAD, &BytesQueued);
|
||||
//Log_Message(GlobalLogBuffer, "Post Peek\n");
|
||||
|
||||
if (BytesQueued != SOCKET_ERROR)
|
||||
{
|
||||
Result = (u32)BytesQueued;
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
switch (Error)
|
||||
{
|
||||
case WSAEWOULDBLOCK:
|
||||
{
|
||||
// NOTE(PS): This case covers non-blocking sockets
|
||||
// if we peek and there's nothing there, it returns
|
||||
// this error code. MSDN says its a non-fatal error
|
||||
// and the operation should be retried later
|
||||
Result = 0;
|
||||
} break;
|
||||
|
||||
case WSAENOTCONN:
|
||||
case WSAECONNRESET:
|
||||
case WSAECONNABORTED:
|
||||
{
|
||||
CloseSocket(Manager, Socket);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
return (s32)Result;
|
||||
}
|
||||
|
||||
internal gs_data
|
||||
Win32SocketReceive(platform_socket_manager* Manager, platform_socket* Socket, gs_memory_arena* Storage)
|
||||
{
|
||||
// TODO(pjs): Test this first code path when you have data running - it should
|
||||
// get the actual size of the data packet being sent
|
||||
#if 0
|
||||
gs_data Result = {};
|
||||
s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
|
||||
if (BytesQueued > 0)
|
||||
{
|
||||
Result = PushSizeToData(Storage, BytesQueued);
|
||||
s32 Flags = 0;
|
||||
s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
|
||||
Assert(BytesReceived == BytesQueued);
|
||||
}
|
||||
return Result;
|
||||
#else
|
||||
gs_data Result = PushSize(Storage, 1024);
|
||||
s32 Flags = 0;
|
||||
SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
|
||||
s32 BytesReceived = recv(*Win32Sock, (char*)Result.Memory, Result.Size, Flags);
|
||||
if (BytesReceived == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
Result.Size = BytesReceived;
|
||||
return Result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
internal s32
|
||||
Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags)
|
||||
{
|
||||
SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
|
||||
|
||||
sockaddr_in SockAddress = {};
|
||||
SockAddress.sin_family = AF_INET;
|
||||
SockAddress.sin_port = HostToNetU16(Port);
|
||||
SockAddress.sin_addr.s_addr = HostToNetU32(Address);
|
||||
|
||||
s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
|
||||
|
||||
//Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: ");
|
||||
if (LengthSent == SOCKET_ERROR)
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
switch (Error)
|
||||
{
|
||||
case WSAEWOULDBLOCK:
|
||||
{
|
||||
// NOTE(PS): This covers non-blocking sockets
|
||||
// In this case the message should be tried again
|
||||
LengthSent = 0;
|
||||
//Log_Message(GlobalLogBuffer, "Not sent, buffered\n");
|
||||
}break;
|
||||
|
||||
case WSAECONNABORTED:
|
||||
case WSAENETUNREACH:
|
||||
case WSAECONNRESET:
|
||||
case WSAENOTCONN:
|
||||
{
|
||||
if (CloseSocket(Manager, Socket))
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Error: %d\n", Error);
|
||||
Error = 0;
|
||||
}
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_Message(GlobalLogBuffer, "Sent\n");
|
||||
}
|
||||
|
||||
return LengthSent;
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32Socket_SetOption(win32_socket* Socket, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength)
|
||||
{
|
||||
int Error = setsockopt(Socket->Socket, Level, Option, OptionValue, OptionLength);
|
||||
if (Error == SOCKET_ERROR)
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
// TODO(Peter): :ErrorLogging
|
||||
}
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32Socket_SetOption(platform_socket_handle SocketHandle, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength)
|
||||
{
|
||||
win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle);
|
||||
return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength);
|
||||
}
|
||||
|
||||
|
||||
internal s32
|
||||
Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, const char* Buffer, s32 BufferLength, s32 Flags)
|
||||
{
|
||||
win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle);
|
||||
|
||||
sockaddr_in SockAddress = {};
|
||||
SockAddress.sin_family = AF_INET;
|
||||
SockAddress.sin_port = HostToNetU16(Port);
|
||||
SockAddress.sin_addr.s_addr = HostToNetU32(Address);
|
||||
|
||||
s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
|
||||
|
||||
if (LengthSent == SOCKET_ERROR)
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
switch (Error)
|
||||
{
|
||||
case WSAENETUNREACH:
|
||||
{
|
||||
Log_Message(GlobalLogBuffer,
|
||||
"Non Critical Error: WSAENETUNREACH \n");
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Error: %d \n", Error);
|
||||
InvalidCodePath;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return LengthSent;
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_SetListening(win32_socket* Socket)
|
||||
{
|
||||
if (listen(Socket->Socket, SOMAXCONN) == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32Socket_PeekGetTotalSize(win32_socket* Socket)
|
||||
{
|
||||
s32 Flags = MSG_PEEK;
|
||||
char Temp[4];
|
||||
s32 BytesQueued = recv(Socket->Socket, Temp, 4, Flags);
|
||||
if (BytesQueued == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
BytesQueued = 0;
|
||||
}
|
||||
return BytesQueued;
|
||||
}
|
||||
|
||||
internal gs_data
|
||||
Win32Socket_Receive(win32_socket* Socket, gs_memory_arena* Storage)
|
||||
{
|
||||
#if 0
|
||||
gs_data Result = {};
|
||||
s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
|
||||
if (BytesQueued > 0)
|
||||
{
|
||||
Result = PushSizeToData(Storage, BytesQueued);
|
||||
s32 Flags = 0;
|
||||
s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
|
||||
Assert(BytesReceived == BytesQueued);
|
||||
}
|
||||
return Result;
|
||||
#else
|
||||
gs_data Result = PushSize(Storage, 1024);
|
||||
s32 Flags = 0;
|
||||
s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
|
||||
if (BytesReceived == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
switch (Error)
|
||||
{
|
||||
case WSAECONNABORTED:
|
||||
case WSANOTINITIALISED:
|
||||
break;
|
||||
|
||||
case WSAENOTCONN:
|
||||
{
|
||||
|
||||
}break;
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
Result.Size = BytesReceived;
|
||||
return Result;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_Close(win32_socket* Socket)
|
||||
{
|
||||
closesocket(Socket->Socket);
|
||||
Socket->Socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_CloseArray(win32_socket_array Array)
|
||||
{
|
||||
for (s32 i = 0; i < Array.Count; i++)
|
||||
{
|
||||
win32_socket* Socket = Array.Values + i;
|
||||
Win32Socket_Close(Socket);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
//
|
||||
// Win32 Socket System
|
||||
|
||||
internal void
|
||||
Win32SocketSystem_Init(gs_memory_arena* Arena)
|
||||
{
|
||||
WSAStartup(MAKEWORD(2, 2), &WSAData);
|
||||
Win32Sockets = Win32SocketArray_Create(16, Arena);
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32SocketSystem_Cleanup()
|
||||
{
|
||||
Win32Socket_CloseArray(Win32Sockets);
|
||||
|
||||
s32 CleanupResult = 0;
|
||||
do {
|
||||
CleanupResult = WSACleanup();
|
||||
}while(CleanupResult == SOCKET_ERROR);
|
||||
}
|
||||
|
||||
PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle)
|
||||
{
|
||||
s32 Result = Win32SocketArray_Take(&Win32Sockets);
|
||||
s32 Error = 0;
|
||||
win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result);
|
||||
*Socket = Win32Socket_Create(AF_INET, SOCK_DGRAM, 0);
|
||||
Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
(const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
|
||||
return (platform_socket_handle)Result;
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_SOCKET_H
|
||||
#endif // WIN32_FOLDHAUS_SOCKET_H
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus_timing.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-02-04
|
||||
//
|
||||
//
|
||||
// NOTE: Relies on having imported foldhaus_platform.h prior to this file
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_TIMING_H
|
||||
|
||||
internal s64
|
||||
GetPerformanceFrequency ()
|
||||
{
|
||||
LARGE_INTEGER Frequency;
|
||||
if (!QueryPerformanceFrequency(&Frequency))
|
||||
{
|
||||
s32 Error = GetLastError();
|
||||
// TODO(Peter): I'm waiting to see an error actually occur here
|
||||
// to know what it could possibly be.
|
||||
InvalidCodePath;
|
||||
}
|
||||
return (s64)Frequency.QuadPart;
|
||||
}
|
||||
|
||||
internal s64
|
||||
GetWallClock ()
|
||||
{
|
||||
#if 0
|
||||
s64 Result = __rdtsc();
|
||||
return Result;
|
||||
#else
|
||||
LARGE_INTEGER Time;
|
||||
if (!QueryPerformanceCounter(&Time))
|
||||
{
|
||||
s32 Error = GetLastError();
|
||||
// TODO(Peter): I'm waiting to see an error actually occur here
|
||||
// to know what it could possibly be.
|
||||
InvalidCodePath;
|
||||
}
|
||||
return (s64)Time.QuadPart;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define WIN32_FOLDHAUS_TIMING_H
|
||||
#endif // WIN32_FOLDHAUS_TIMING_H
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus_utils.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_UTILS_H
|
||||
|
||||
internal gs_string
|
||||
Win32DumpErrorAndPrepareMessageBoxString(gs_memory_arena* Arena, char* Format, ...)
|
||||
{
|
||||
s32 Error = GetLastError();
|
||||
gs_string ErrorDump = PushString(Arena, 4096);
|
||||
PrintF(&ErrorDump,
|
||||
R"FOO(Win32 Error: %s\n
|
||||
Error Code: %d\n
|
||||
)FOO",
|
||||
__FUNCTION__,
|
||||
Error);
|
||||
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
PrintFArgsList(&ErrorDump, Format, Args);
|
||||
va_end(Args);
|
||||
|
||||
gs_data ErrorDumpData = StringToData(ErrorDump);
|
||||
|
||||
HANDLE FileHandle = CreateFileA("./crashdump.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(FileHandle, ErrorDumpData.Memory, ErrorDumpData.Size, &BytesWritten, NULL))
|
||||
{
|
||||
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
|
||||
AppendPrintF(&ErrorDump, "Program will attempt to continue. See crashdump.txt for info");
|
||||
NullTerminate(&ErrorDump);
|
||||
|
||||
return ErrorDump;
|
||||
}
|
||||
|
||||
DEBUG_PRINT(Win32DebugPrint)
|
||||
{
|
||||
Log_Message(GlobalLogBuffer, "%S", Message);
|
||||
}
|
||||
|
||||
#define PrintLastError() PrintLastError_(__FILE__, __LINE__)
|
||||
internal void
|
||||
PrintLastError_(char* File, u32 Line)
|
||||
{
|
||||
char DebugStringData[256];
|
||||
gs_string DebugString = MakeString(DebugStringData, 0, 256);
|
||||
u32 Error = GetLastError();
|
||||
Log_Error(GlobalLogBuffer, "%s Line %d: Win32 Error %d\n\0", File, Line, Error);
|
||||
}
|
||||
|
||||
|
||||
internal HINSTANCE
|
||||
GetHInstance()
|
||||
{
|
||||
HINSTANCE Result = GetModuleHandle(NULL);
|
||||
if (Result == NULL)
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
#define WIN32_FOLDHAUS_UTILS_H
|
||||
#endif // WIN32_FOLDHAUS_UTILS_H
|
|
@ -1,250 +0,0 @@
|
|||
//
|
||||
// File: win32_foldhaus_work_queue.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_WORK_QUEUE_H
|
||||
|
||||
struct worker_thread_entry
|
||||
{
|
||||
b32 IsValid;
|
||||
u32 Index;
|
||||
};
|
||||
|
||||
struct worker_thread_info
|
||||
{
|
||||
gs_thread_context ThreadContext;
|
||||
HANDLE Handle;
|
||||
gs_work_queue* Queue;
|
||||
};
|
||||
|
||||
struct win32_work_queue
|
||||
{
|
||||
u32 ThreadCount;
|
||||
worker_thread_info* Threads;
|
||||
gs_work_queue WorkQueue;
|
||||
};
|
||||
|
||||
worker_thread_info* WorkerThreads;
|
||||
win32_work_queue Win32WorkQueue;
|
||||
|
||||
internal s32
|
||||
Win32GetThreadId()
|
||||
{
|
||||
s32 Result = GetCurrentThreadId();
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_thread_context
|
||||
Win32CreateThreadContext(gs_memory_arena* Transient = 0)
|
||||
{
|
||||
gs_thread_context Result = {0};
|
||||
Result.ThreadInfo.ThreadID = Win32GetThreadId();
|
||||
Result.Allocator = CreatePlatformAllocator();
|
||||
if (Transient != 0)
|
||||
{
|
||||
Result.Transient = Transient;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Transient = AllocStruct(Result.Allocator, gs_memory_arena, "Work Queue");
|
||||
*Result.Transient = MemoryArenaCreate(MB(4), Bytes(8), Result.Allocator, 0, 0, "Tctx Transient");
|
||||
}
|
||||
Result.FileHandler = CreateFileHandler(Win32GetFileInfo,
|
||||
Win32ReadEntireFile,
|
||||
Win32WriteEntireFile,
|
||||
Win32EnumerateDirectory,
|
||||
Result.Transient);
|
||||
|
||||
Result.DebugOutput.Print = Win32DebugPrint;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue)
|
||||
{
|
||||
#if DEBUG
|
||||
// NOTE(Peter): Just prints out the names of all the pending jobs if we end up
|
||||
// overflowing the buffer
|
||||
if (Queue->JobsCount >= Queue->JobsMax)
|
||||
{
|
||||
gs_string DebugString = MakeString((char*)malloc(256), 256);
|
||||
for (u32 i = 0; i < Queue->JobsCount; i++)
|
||||
{
|
||||
Log_Message(GlobalLogBuffer, "%d %s \n", i, Queue->Jobs[i].JobName);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Assert(Queue->JobsCount < Queue->JobsMax);
|
||||
|
||||
gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount;
|
||||
Job->WorkProc = WorkProc;
|
||||
Job->Data = Data;
|
||||
#if DEBUG
|
||||
Job->JobName = JobName;
|
||||
#endif
|
||||
|
||||
// Complete Past Writes before Future Writes
|
||||
_WriteBarrier();
|
||||
_mm_sfence();
|
||||
|
||||
++Queue->JobsCount;
|
||||
ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0);
|
||||
}
|
||||
|
||||
internal worker_thread_entry
|
||||
CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone)
|
||||
{
|
||||
worker_thread_entry Entry = {};
|
||||
Entry.IsValid = false;
|
||||
while (Queue->JobsCompleted < Queue->JobsCount)
|
||||
{
|
||||
Entry = CompleteAndTakeNextJob(Queue, Entry, Context);
|
||||
if (Entry.IsValid)
|
||||
{
|
||||
Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI
|
||||
WorkerThreadProc (LPVOID InputThreadInfo)
|
||||
{
|
||||
worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo;
|
||||
ThreadInfo->ThreadContext = Win32CreateThreadContext();
|
||||
|
||||
worker_thread_entry Entry = {};
|
||||
Entry.IsValid = false;
|
||||
while (true)
|
||||
{
|
||||
MemoryArenaClear(ThreadInfo->ThreadContext.Transient);
|
||||
Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext);
|
||||
if (Entry.IsValid)
|
||||
{
|
||||
ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext,
|
||||
ThreadInfo->Queue->Jobs[Entry.Index].Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI
|
||||
Win32ThreadProcWrapper(LPVOID ThreadInfo)
|
||||
{
|
||||
platform_thread* Thread = (platform_thread*)ThreadInfo;
|
||||
gs_thread_context Ctx = Win32CreateThreadContext();
|
||||
Thread->Proc(&Ctx, Thread->UserData);
|
||||
|
||||
// TODO(pjs): Destroy Thread Context
|
||||
// TODO(pjs): How do we notify the thread manager this thread belongs to that it is free?
|
||||
// Probaby put a pointer to the thread manager in the platform_thread struct
|
||||
// so we can update the tracking structure?
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CREATE_THREAD(Win32CreateThread)
|
||||
{
|
||||
Thread->Proc = Proc;
|
||||
Thread->UserData = UserData;
|
||||
|
||||
// TODO(pjs): ugh, allocation out in the middle of nowhere
|
||||
HANDLE* ThreadHandle = AllocStruct(Ctx.Allocator, HANDLE, "Create Thread");
|
||||
*ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0);
|
||||
// TODO(pjs): Error checking on the created thread
|
||||
|
||||
Thread->PlatformHandle = (u8*)ThreadHandle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
KILL_THREAD(Win32KillThread)
|
||||
{
|
||||
HANDLE* ThreadHandle = (HANDLE*)Thread->PlatformHandle;
|
||||
TerminateThread(ThreadHandle, 0);
|
||||
|
||||
// TODO(pjs): see allocation out in the middle of nowhere in Win32CreateThread
|
||||
Win32Free((void*)Thread->PlatformHandle, sizeof(HANDLE), 0);
|
||||
|
||||
// TODO(pjs): Error checking
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32WorkQueue_Init(gs_memory_arena* Arena, u32 ThreadCount)
|
||||
{
|
||||
if (ThreadCount > 0)
|
||||
{
|
||||
Win32WorkQueue.ThreadCount = ThreadCount;
|
||||
Win32WorkQueue.Threads = PushArray(Arena, worker_thread_info, ThreadCount);
|
||||
}
|
||||
|
||||
gs_work_queue WQ = {};
|
||||
WQ.SemaphoreHandle = CreateSemaphoreEx(0, 0, ThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);;
|
||||
WQ.JobsMax = 512;
|
||||
WQ.Jobs = PushArray(Arena, gs_threaded_job, WQ.JobsMax);
|
||||
WQ.NextJobIndex = 0;
|
||||
WQ.PushWorkOnQueue = Win32PushWorkOnQueue;
|
||||
WQ.CompleteQueueWork = Win32DoQueueWorkUntilDone;
|
||||
|
||||
Win32WorkQueue.WorkQueue = WQ;
|
||||
|
||||
// ID = 0 is reserved for this thread
|
||||
for (u32 i = 0; i < ThreadCount; i++)
|
||||
{
|
||||
worker_thread_info* T = Win32WorkQueue.Threads + i;
|
||||
T->Queue = &Win32WorkQueue.WorkQueue;
|
||||
T->Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)T, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32WorkQueue_Cleanup()
|
||||
{
|
||||
u32 Error = 0;
|
||||
for (u32 Thread = 0; Thread < Win32WorkQueue.ThreadCount; Thread++)
|
||||
{
|
||||
u32 Success = TerminateThread(Win32WorkQueue.Threads[Thread].Handle, 0);
|
||||
if (!Success)
|
||||
{
|
||||
Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_WORK_QUEUE_H
|
||||
#endif // WIN32_FOLDHAUS_WORK_QUEUE_H
|
|
@ -1,86 +0,0 @@
|
|||
//
|
||||
// File: win32_test_code.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-10
|
||||
//
|
||||
#ifndef WIN32_TEST_CODE_CPP
|
||||
|
||||
#if 0
|
||||
internal void
|
||||
Win32_TestCode_UART(gs_thread_context ThreadContext)
|
||||
{
|
||||
u32 LedCount = 48;
|
||||
u32 MessageBaseSize = sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer);
|
||||
MessageBaseSize += sizeof(u8) * 3 * LedCount;
|
||||
gs_data MessageBuffer = PushSizeToData(ThreadContext.Transient);
|
||||
|
||||
gs_memory_cursor WriteCursor = CreateMemoryCursor(MessageBuffer);
|
||||
|
||||
uart_header* Header = PushStructOnCursor(WriteCursor, uart_header);
|
||||
UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812);
|
||||
uart_channel* Channel = PushStructOnCursor(WriteCursor, uart_channel);
|
||||
*Channel = ChannelSettings;
|
||||
|
||||
for (u32 i = 0; i < LedCount; i++)
|
||||
{
|
||||
u8* OutputPixel = PushArrayOnCursor(WriteCursor, u8, 3);
|
||||
OutputPixel[Channel->RedIndex] = (u8)(i);
|
||||
OutputPixel[Channel->GreenIndex] = 0;
|
||||
OutputPixel[Channel->BlueIndex] = 0;
|
||||
}
|
||||
|
||||
uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer);
|
||||
UART_FillFooter(Footer, (u8*)Header);
|
||||
}
|
||||
#endif
|
||||
|
||||
win32_socket ListenSocket;
|
||||
HANDLE ListenThread;
|
||||
|
||||
DWORD WINAPI
|
||||
Win32_TestCode_ListenThreadProc(LPVOID ThreadData)
|
||||
{
|
||||
gs_thread_context Ctx = Win32CreateThreadContext();
|
||||
|
||||
temp_job_req* Req = (temp_job_req*)ThreadData;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Req->Proc(&Ctx, Req->Memory);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32_TestCode_SocketReading(gs_thread_context ThreadContext, temp_job_req* Req)
|
||||
{
|
||||
ListenSocket = Win32Socket_ConnectToAddress("127.0.0.1", "20185");
|
||||
u8* Arg = (u8*)Req;
|
||||
ListenThread = CreateThread(0, 0, &Win32_TestCode_ListenThreadProc, Arg, 0, 0);
|
||||
}
|
||||
|
||||
internal void
|
||||
BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData)
|
||||
{
|
||||
packet_ringbuffer* MicPacketBuffer = (packet_ringbuffer*)UserData;
|
||||
|
||||
gs_data Data = Win32Socket_Receive(&ListenSocket, Ctx->Transient);
|
||||
if (Data.Size > 0)
|
||||
{
|
||||
OutputDebugStringA("Listened");
|
||||
MicPacketBuffer->Values[MicPacketBuffer->WriteHead++] = Data;
|
||||
if (MicPacketBuffer->WriteHead >= PACKETS_MAX)
|
||||
{
|
||||
MicPacketBuffer->WriteHead = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32_TestCode_SocketReading_Cleanup()
|
||||
{
|
||||
TerminateThread(ListenThread, 0);
|
||||
Win32Socket_Close(&ListenSocket);
|
||||
}
|
||||
|
||||
#define WIN32_TEST_CODE_CPP
|
||||
#endif // WIN32_TEST_CODE_CPP
|
File diff suppressed because it is too large
Load Diff
|
@ -1,393 +0,0 @@
|
|||
//
|
||||
// File: blumen_lumen.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2021-01-15
|
||||
//
|
||||
#ifndef BLUMEN_LUMEN_H
|
||||
|
||||
#include "message_queue.h"
|
||||
|
||||
enum bl_debug_ui_mode
|
||||
{
|
||||
BlumenDebug_Motors,
|
||||
BlumenDebug_Leds,
|
||||
BlumenDebug_Awaken,
|
||||
|
||||
BlumenDebug_Count,
|
||||
};
|
||||
|
||||
char* BlDebugUiModeStrings[] = {
|
||||
"Motors",
|
||||
"Leds",
|
||||
"Awaken",
|
||||
};
|
||||
|
||||
enum bl_pattern_mode
|
||||
{
|
||||
BlumenPattern_Standard,
|
||||
BlumenPattern_VoiceCommand,
|
||||
BlumenPattern_NoControl,
|
||||
|
||||
BlumenPattern_Count,
|
||||
};
|
||||
|
||||
enum bl_python_packet_type
|
||||
{
|
||||
PacketType_Invalid = 0,
|
||||
PacketType_PatternCommand = 1,
|
||||
PacketType_MotorState = 2,
|
||||
PacketType_Temperature = 3,
|
||||
PacketType_LumenariumStatus = 4,
|
||||
};
|
||||
|
||||
enum bl_motor_state_value
|
||||
{
|
||||
MotorState_Invalid = 0,
|
||||
|
||||
MotorState_Closed = 1,
|
||||
MotorState_Open = 2,
|
||||
MotorState_HalfOpen = 3,
|
||||
MotorState_MostlyOpen = 4,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct motor_packet
|
||||
{
|
||||
u8 FlowerPositions[3];
|
||||
} motor_packet;
|
||||
|
||||
typedef struct motor_status_packet
|
||||
{
|
||||
motor_packet Pos;
|
||||
u8 MotorStatus[3];
|
||||
u16 Temperature;
|
||||
|
||||
} motor_status_packet;
|
||||
|
||||
typedef struct microphone_packet
|
||||
{
|
||||
b8 ChangeAnimation;
|
||||
char AnimationFileName[32];
|
||||
b8 SetLayer;
|
||||
char LayerName[32];
|
||||
r32 LayerOpacity;
|
||||
b8 SetLayerParamColor;
|
||||
char LayerParamColor[7];
|
||||
r32 OverrideDuration;
|
||||
} microphone_packet;
|
||||
|
||||
typedef struct temp_packet
|
||||
{
|
||||
s8 Temperature;
|
||||
} temp_packet;
|
||||
|
||||
enum motor_event_type
|
||||
{
|
||||
MotorEvent_Close = 0,
|
||||
MotorEvent_Open = 1,
|
||||
};
|
||||
|
||||
typedef struct status_packet
|
||||
{
|
||||
u8 NextMotorEventType;
|
||||
// u16 Padding;
|
||||
u32 NextEventTime;
|
||||
|
||||
char AnimFileName[32];
|
||||
} status_packet;
|
||||
|
||||
typedef struct blumen_packet
|
||||
{
|
||||
u8 Type;
|
||||
union
|
||||
{
|
||||
motor_packet MotorPacket;
|
||||
motor_status_packet MotorStatusPacket;
|
||||
microphone_packet MicPacket;
|
||||
temp_packet TempPacket;
|
||||
status_packet StatusPacket;
|
||||
};
|
||||
} blumen_packet;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// TODO(pjs): Refactor this -> blumen_network_job_state
|
||||
struct mic_listen_job_data
|
||||
{
|
||||
bool* Running;
|
||||
|
||||
platform_socket_manager* SocketManager;
|
||||
blumen_network_msg_queue* IncomingMsgQueue;
|
||||
|
||||
blumen_network_msg_queue* OutgoingMsgQueue;
|
||||
|
||||
// Status
|
||||
bool IsConnected;
|
||||
};
|
||||
|
||||
typedef struct time_range
|
||||
{
|
||||
s32 StartHour;
|
||||
s32 StartMinute;
|
||||
|
||||
s32 EndHour;
|
||||
s32 EndMinute;
|
||||
} time_range;
|
||||
|
||||
internal bool
|
||||
SystemTimeIsBeforeTime(system_time SysTime, s32 Hour, s32 Minute)
|
||||
{
|
||||
bool Result = false;
|
||||
if (SysTime.Hour == Hour) {
|
||||
Result = SysTime.Minute < Minute;
|
||||
} else {
|
||||
Result = SysTime.Hour < Hour;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
SystemTimeIsAfterTime(system_time SysTime, s32 Hour, s32 Minute)
|
||||
{
|
||||
bool Result = false;
|
||||
if (SysTime.Hour == Hour) {
|
||||
Result = SysTime.Minute >= Minute;
|
||||
} else {
|
||||
Result = SysTime.Hour > Hour;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
SystemTimeIsInTimeRange(system_time SysTime, time_range Range)
|
||||
{
|
||||
bool Result = false;
|
||||
|
||||
bool IsAfterStartTime = SystemTimeIsAfterTime(SysTime, Range.StartHour, Range.StartMinute);
|
||||
bool IsBeforeEndTime = SystemTimeIsBeforeTime(SysTime, Range.EndHour, Range.EndMinute);
|
||||
Result = IsAfterStartTime && IsBeforeEndTime;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
SystemTimeIsInTimeRangeList(system_time SysTime, time_range* Ranges, u32 RangesCount, time_range* RangeOut = 0)
|
||||
{
|
||||
bool Result = false;
|
||||
for (u32 i = 0; i < RangesCount; i++)
|
||||
{
|
||||
time_range Range = Ranges[i];
|
||||
bool CurrTimeInRange = SystemTimeIsInTimeRange(SysTime, Range);
|
||||
if (CurrTimeInRange)
|
||||
{
|
||||
Result = true;
|
||||
if (RangeOut != 0) {
|
||||
*RangeOut = Range;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
#include "blumen_lumen_settings.h"
|
||||
|
||||
struct blumen_lumen_state
|
||||
{
|
||||
bool Running;
|
||||
|
||||
blumen_network_msg_queue IncomingMsgQueue;
|
||||
blumen_network_msg_queue OutgoingMsgQueue;
|
||||
|
||||
temp_job_req JobReq;
|
||||
|
||||
led_strip_list StemStrips[BL_FLOWER_COUNT];
|
||||
|
||||
platform_thread_handle MicListenThread;
|
||||
mic_listen_job_data MicListenJobData;
|
||||
|
||||
motor_packet LastKnownMotorState;
|
||||
u64 LastTimeMotorStateChanged[BL_FLOWER_COUNT];
|
||||
b8 ShouldDimUpperLeds[BL_FLOWER_COUNT];
|
||||
|
||||
// NOTE(pjs): Based on temperature data from weatherman
|
||||
// dim the leds.
|
||||
r32 BrightnessPercent;
|
||||
s8 LastTemperatureReceived;
|
||||
system_time LastStatusUpdateTime;
|
||||
|
||||
system_time LastSendTime;
|
||||
bl_motor_state_value LastSendState;
|
||||
|
||||
phrase_hue StandardPatternHues;
|
||||
r32 AssemblyColorsTransitionTimeLeft[BL_FLOWER_COUNT];
|
||||
phrase_hue NextAssemblyColors[BL_FLOWER_COUNT];
|
||||
phrase_hue AssemblyColors[BL_FLOWER_COUNT];
|
||||
u32 LastAssemblyColorSet;
|
||||
|
||||
// The indices of this array are the index the clear core uses to
|
||||
// represent a motor.
|
||||
// The values of the array are the names Lumenarium uses to
|
||||
// represent assemblies.
|
||||
//
|
||||
u32 AssemblyNameToClearCoreMapCount;
|
||||
u64* AssemblyNameToClearCore_Names;
|
||||
|
||||
bl_pattern_mode PatternMode;
|
||||
animation_handle_array ModeAnimations[BlumenPattern_Count];
|
||||
animation_handle OffAnimHandle;
|
||||
animation_handle AwakenHandle;
|
||||
|
||||
phrase_hue_map PhraseHueMap;
|
||||
|
||||
bool InPhraseReceptionMode;
|
||||
phrase_hue NextHotHue;
|
||||
system_time TimePhraseReceptionBegan;
|
||||
system_time TimeLastPhraseReceived;
|
||||
|
||||
system_time TimeLastSetToVoiceMode;
|
||||
phrase_hue LastHuePhrase;
|
||||
|
||||
r32 PatternSpeed;
|
||||
|
||||
// Debug
|
||||
bl_debug_ui_mode DebugMode;
|
||||
|
||||
motor_packet DEBUG_PendingMotorPacket;
|
||||
bool DEBUG_IgnoreWeatherDimmingLeds;
|
||||
|
||||
bool ShouldUpdateLog;
|
||||
bool IgnoreTimeOfDay_LedDimming;
|
||||
bool IgnoreTimeOfDay_MotorState;
|
||||
|
||||
phrase_hue PendingPhrase;
|
||||
|
||||
bool DebugOverrideHue;
|
||||
phrase_hue DebugHue;
|
||||
};
|
||||
|
||||
internal bool
|
||||
Blumen_TempShouldDimLeds(blumen_lumen_state* BLState)
|
||||
{
|
||||
bool Result = BLState->LastTemperatureReceived > MinHighTemperature;
|
||||
return Result;
|
||||
}
|
||||
|
||||
#include "message_queue.cpp"
|
||||
|
||||
internal void
|
||||
BlumenLumen_SetNextHue(blumen_lumen_state* BLState, u32 AssemblyIndex, phrase_hue Hue)
|
||||
{
|
||||
#if 1
|
||||
BLState->NextAssemblyColors[AssemblyIndex] = Hue;
|
||||
BLState->AssemblyColorsTransitionTimeLeft[AssemblyIndex] = PhraseHueFadeInDuration;
|
||||
#else
|
||||
BLState->AssemblyColors[AssemblyIndex] = Hue;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void
|
||||
BlumenLumen_AdvanceHueFade(blumen_lumen_state* BLState, context Context)
|
||||
{
|
||||
for (u32 i = 0; i < BL_FLOWER_COUNT; i++)
|
||||
{
|
||||
r32 T = BLState->AssemblyColorsTransitionTimeLeft[i];
|
||||
if (T > 0)
|
||||
{
|
||||
T -= Context.DeltaTime;
|
||||
if (T <= 0)
|
||||
{
|
||||
BLState->AssemblyColors[i] = BLState->NextAssemblyColors[i];
|
||||
}
|
||||
BLState->AssemblyColorsTransitionTimeLeft[i] = T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal phrase_hue
|
||||
BlumenLumen_GetCurrentHue(blumen_lumen_state* BLState, assembly Assembly)
|
||||
{
|
||||
phrase_hue Result = {};
|
||||
|
||||
switch (BLState->PatternMode)
|
||||
{
|
||||
case BlumenPattern_NoControl:
|
||||
case BlumenPattern_Standard:
|
||||
{
|
||||
Result = BLState->StandardPatternHues;
|
||||
}break;
|
||||
|
||||
case BlumenPattern_VoiceCommand:
|
||||
{
|
||||
u32 i = Assembly.AssemblyIndex % 3;
|
||||
r32 T = BLState->AssemblyColorsTransitionTimeLeft[i];
|
||||
if (T > 0)
|
||||
{
|
||||
T = Clamp(T / PhraseHueFadeInDuration, 0, 1);
|
||||
Result = LerpPhraseHue(T, BLState->NextAssemblyColors[i], BLState->AssemblyColors[i]);
|
||||
} else {
|
||||
Result = BLState->AssemblyColors[i];
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// If you change anything, exit lumenarium if its running
|
||||
// then in this application hit f1 to compile then
|
||||
// go to remedybg (the debugger) and hit f5
|
||||
|
||||
|
||||
// don't touch this
|
||||
u8 LastPosition = 1;
|
||||
|
||||
u8 ClosedValue = 1;
|
||||
u8 OpenValue = 2;
|
||||
|
||||
|
||||
r64 MotorTimeElapsed = 0;
|
||||
r64 OpenClosePeriod = 15.0f;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define BLUMEN_LUMEN_H
|
||||
#endif // BLUMEN_LUMEN_H
|
|
@ -1,135 +0,0 @@
|
|||
/* date = March 27th 2021 2:50 pm */
|
||||
|
||||
#ifndef BLUMEN_LUMEN_SETTINGS_H
|
||||
#define BLUMEN_LUMEN_SETTINGS_H
|
||||
|
||||
// Hey you never know, might need to change this some day lololol
|
||||
// The number of flowers in the sculpture. Used to size all sorts of
|
||||
// arrays. Maybe don't touch this unless you really know what you're doing?
|
||||
#define BL_FLOWER_COUNT 3
|
||||
|
||||
// The path to the three flower assembly files
|
||||
// PS is 90% sure you don't need to touch these ever
|
||||
gs_const_string Flower0AssemblyPath = ConstString("data/ss_blumen_one.fold");
|
||||
gs_const_string Flower1AssemblyPath = ConstString("data/ss_blumen_two.fold");
|
||||
gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold");
|
||||
|
||||
// The path to the phrase map CSV. Can be an absolute path, or relative
|
||||
// to the app_run_tree folder
|
||||
gs_const_string PhraseMapCSVPath = ConstString("C:/projects/flowers-sound/flower_codes.tsv");
|
||||
char PhraseMapCSVSeparator = '\t';
|
||||
|
||||
// Search Strings for which folders to find ambient animation files and
|
||||
// voice animation files in.
|
||||
// these search patterns should always end in *.foldanim so they only
|
||||
// return valid animation files
|
||||
gs_const_string AmbientPatternFolder = ConstString("data/blumen_animations/ambient_patterns/*.foldanim");
|
||||
gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_responses/*.foldanim");
|
||||
|
||||
// The times of day when the motors should be open.
|
||||
//
|
||||
// @TimeFormat: documentation follows
|
||||
// these are in the format { Start_Hour, Start_Minute, End_Hour, End_Minute }
|
||||
// Hours are in the range 0-23 inclusive
|
||||
// Minutes are in the range 0-59 inclusive
|
||||
//
|
||||
// NOTE: There is no need to modify the MotorOpenTimesCount variable -
|
||||
// it is a compile time constant that gets calculated automatically
|
||||
global time_range MotorOpenTimes[] = {
|
||||
{ 8, 00, 12, 00 }, // 8:00am to 12:00pm
|
||||
{ 12, 30, 18, 00 }, // 12:30pm to 06:00pm
|
||||
{ 18, 30, 22, 00 }, // 6:30pm to 10:00pm
|
||||
};
|
||||
global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit
|
||||
|
||||
// Lumenarium repeatedly resends the current motor state to the python
|
||||
// server. This variable determines how much time elapses between each
|
||||
// message.
|
||||
global r32 MotorResendStatePeriod = 90.0f; // seconds
|
||||
|
||||
// The times of day when the leds should be on
|
||||
// Search for @TimeFormat to find documentation
|
||||
global time_range LedOnTimes[] = {
|
||||
{ 17, 00, 23, 59 },
|
||||
{ 00, 00, 06, 30 },
|
||||
};
|
||||
global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit
|
||||
global b8 DEBUGIgnoreLedOnTimeRange = false;
|
||||
|
||||
// How long it takes to fade from the default pattern to the
|
||||
// voice activated pattern
|
||||
r32 VoiceCommandFadeDuration = 0.5f; // in seconds
|
||||
|
||||
// How long the voice activated pattern will remain active
|
||||
// without additional voice commands, before fading back to
|
||||
// default behaviour.
|
||||
// ie.
|
||||
// if this is set to 30 seconds, upon receiving a voice command
|
||||
// lumenarium will fade to the requested pattern/color palette
|
||||
// and then wait 30 seconds before fading back to the original
|
||||
// pattern. If, in that 30 second window, another voice command
|
||||
// is issued, lumenarium will reset the 30 second counter.
|
||||
r64 VoiceCommandSustainDuration = 120.0; // in seconds
|
||||
|
||||
// When we send a Motor Close command, we don't want the upper leds to
|
||||
// immediately turn off. Instead, we want to wait until the flower is
|
||||
// at least some of the way closed. This variable dictates how long
|
||||
// we wait for.
|
||||
// For example:
|
||||
// 1. We send a 'motor close' command to the clear core
|
||||
// 2. the clear core sends back a 'motor closed' state packet
|
||||
// 3. We begin a timer
|
||||
// 4. When the timer reaches the value set in this variable,
|
||||
// we turn the upper leds off.
|
||||
//
|
||||
// NOTE: This is not a symmetric operation. When we send a 'motor open'
|
||||
// command, we want to immediately turn the upper leds on so they appear
|
||||
// to have been on the whole time.
|
||||
r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 120.0; // in seconds
|
||||
|
||||
|
||||
// NOTE: Temperature & Time of Day Based Led Brightness Settings
|
||||
|
||||
// The temperature above which we dim the leds to
|
||||
// HighTemperatureBrightnessPercent (below)
|
||||
//
|
||||
// NOTE: this is an 8bit signed integer so its range is
|
||||
// -128 to 127, not that we should need either of those extremes for
|
||||
// this, but just a note that you can't just put anything in here.
|
||||
s8 MinHighTemperature = 26;
|
||||
|
||||
// The percent brightness we set leds to during high temperatures.
|
||||
// A value in the range 0:1 inclusive
|
||||
// This is multiplied by each pixels R, G, & B channels before being
|
||||
// sent. So if it is set to .1f, then the maximum brightness value sent
|
||||
// to any channel of any pixel will be 25 (255 * .1 = 25).
|
||||
r32 HighTemperatureBrightnessPercent = 0.25f;
|
||||
|
||||
// The percent brightness we set leds to when no other conditions apply
|
||||
// A value in the range 0:1 inclusive.
|
||||
// Probably wants to be something high like 1 but we might want to
|
||||
// lower it for heat reasons?
|
||||
r32 FullBrightnessPercent = 0.50f;
|
||||
|
||||
// A global modifier so Joerg can just slow all the patterns right down
|
||||
// XD
|
||||
// This is a percent - so 1 is full speed, 0.1f is 1/10 full speed and
|
||||
// 2 is 2x as fast.
|
||||
r32 GlobalAnimSpeed = 1.0f;
|
||||
|
||||
// How long it takes to fade from one animation to the next.
|
||||
// This is used both for transitioning between animation files
|
||||
// as well as transitioning from Standard pattern mode to voice
|
||||
// activated mode
|
||||
r32 GlobalAnimTransitionSpeed = 0.0f;
|
||||
|
||||
// how long it takes to fade from the old voice hue to the new one
|
||||
r32 PhraseHueFadeInDuration = 0.01f; // seconds
|
||||
|
||||
r64 PhrasePriorityMessageGroupingTime = 0.1f;
|
||||
|
||||
// How often should Lumenarium send its status to the python server?
|
||||
//
|
||||
#define STATUS_PACKET_FREQ_SECONDS 10 // in seconds
|
||||
|
||||
#endif //BLUMEN_LUMEN_SETTINGS_H
|
|
@ -1,461 +0,0 @@
|
|||
/* date = March 31st 2021 2:19 am */
|
||||
|
||||
#ifndef GFX_MATH_H
|
||||
#define GFX_MATH_H
|
||||
|
||||
internal r32
|
||||
Smoothstep(r32 T)
|
||||
{
|
||||
r32 Result = (T * T * (3 - (2 * T)));
|
||||
return Result;
|
||||
}
|
||||
internal r32
|
||||
Smoothstep(r32 T, r32 A, r32 B)
|
||||
{
|
||||
return LerpR32(Smoothstep(T), A, B);
|
||||
}
|
||||
internal v3
|
||||
Smoothstep(v3 P)
|
||||
{
|
||||
v3 R = {};
|
||||
R.x = Smoothstep(P.x);
|
||||
R.y = Smoothstep(P.y);
|
||||
R.z = Smoothstep(P.z);
|
||||
return R;
|
||||
}
|
||||
|
||||
internal v3
|
||||
AbsV3(v3 P)
|
||||
{
|
||||
v3 Result = {};
|
||||
Result.x = Abs(P.x);
|
||||
Result.y = Abs(P.y);
|
||||
Result.z = Abs(P.z);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal v2
|
||||
FloorV2(v2 P)
|
||||
{
|
||||
v2 Result = {};
|
||||
Result.x = FloorR32(P.x);
|
||||
Result.y = FloorR32(P.y);
|
||||
return Result;
|
||||
}
|
||||
internal v3
|
||||
FloorV3(v3 P)
|
||||
{
|
||||
v3 Result = {};
|
||||
Result.x = FloorR32(P.x);
|
||||
Result.y = FloorR32(P.y);
|
||||
Result.z = FloorR32(P.z);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal v2
|
||||
FractV2(v2 P)
|
||||
{
|
||||
v2 Result = {};
|
||||
Result.x = FractR32(P.x);
|
||||
Result.y = FractR32(P.y);
|
||||
return Result;
|
||||
}
|
||||
internal v3
|
||||
FractV3(v3 P)
|
||||
{
|
||||
v3 Result = {};
|
||||
Result.x = FractR32(P.x);
|
||||
Result.y = FractR32(P.y);
|
||||
Result.z = FractR32(P.z);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal v2
|
||||
SinV2(v2 P)
|
||||
{
|
||||
v2 Result = {};
|
||||
Result.x = SinR32(P.x);
|
||||
Result.y = SinR32(P.y);
|
||||
return Result;
|
||||
}
|
||||
internal v3
|
||||
SinV3(v3 P)
|
||||
{
|
||||
v3 Result = {};
|
||||
Result.x = SinR32(P.x);
|
||||
Result.y = SinR32(P.y);
|
||||
Result.y = SinR32(P.z);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Hash1(v2 P)
|
||||
{
|
||||
v2 Result = FractV2( P * 0.3183099f ) * 50.f;
|
||||
return FractR32(P.x * P.y * (P.x + P.y));
|
||||
}
|
||||
|
||||
internal r32
|
||||
Hash1(r32 N)
|
||||
{
|
||||
return FractR32(N * 17.0f * FractR32(N * 0.3183099f));
|
||||
}
|
||||
|
||||
internal v2
|
||||
Hash2(r32 N)
|
||||
{
|
||||
v2 P = V2MultiplyPairwise(SinV2(v2{N,N+1.0f}), v2{43758.5453123f,22578.1459123f});
|
||||
return FractV2(P);
|
||||
}
|
||||
|
||||
internal v2
|
||||
Hash2(v2 P)
|
||||
{
|
||||
v2 K = v2{ 0.3183099f, 0.3678794f };
|
||||
v2 Kp = v2{K.y, K.x};
|
||||
v2 R = V2MultiplyPairwise(P, K) + Kp;
|
||||
return FractV2( K * 16.0f * FractR32( P.x * P.y * (P.x + P.y)));
|
||||
}
|
||||
|
||||
internal v3
|
||||
Hash3(v2 P)
|
||||
{
|
||||
v3 Q = v3{};
|
||||
Q.x = V2Dot(P, v2{127.1f, 311.7f});
|
||||
Q.y = V2Dot(P, v2{267.5f, 183.3f});
|
||||
Q.z = V2Dot(P, v2{419.2f, 371.9f});
|
||||
return FractV3(SinV3(Q) * 43758.5453f);
|
||||
}
|
||||
|
||||
internal r32
|
||||
HashV3ToR32(v3 P)
|
||||
{
|
||||
v3 Pp = FractV3(P * 0.3183099f + v3{0.1f, 0.1f, 0.1f});
|
||||
Pp *= 17.0f;
|
||||
r32 Result = FractR32(Pp.x * Pp.y * Pp.z * (Pp.x + Pp.y + Pp.z));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Random(v2 N)
|
||||
{
|
||||
v2 V = v2{12.9898f, 4.1414f};
|
||||
return FractR32(SinR32(V2Dot(N, V)) * 43758.5453);
|
||||
}
|
||||
|
||||
internal r32
|
||||
Noise2D(v2 P)
|
||||
{
|
||||
v2 IP = FloorV2(P);
|
||||
v2 U = FractV2(P);
|
||||
U = V2MultiplyPairwise(U, U);
|
||||
U = V2MultiplyPairwise(U, ((U * 2.0f) + v2{-3, -3}));
|
||||
|
||||
r32 A = LerpR32(U.x, Random(IP), Random(IP + v2{1.0f, 0}));
|
||||
r32 B = LerpR32(U.x, Random(IP + v2{0, 1}), Random(IP + v2{1, 1}));
|
||||
r32 Res = LerpR32(U.y, A, B);
|
||||
|
||||
return Res * Res;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Noise3D(v3 P)
|
||||
{
|
||||
P = AbsV3(P);
|
||||
v3 PFloor = FloorV3(P);
|
||||
v3 PFract = FractV3(P);
|
||||
v3 F = Smoothstep(PFract);
|
||||
|
||||
r32 Result = LerpR32(F.z,
|
||||
LerpR32(F.y,
|
||||
LerpR32(F.x,
|
||||
HashV3ToR32(PFloor + v3{0, 0, 0}),
|
||||
HashV3ToR32(PFloor + v3{1, 0, 0})),
|
||||
LerpR32(F.x,
|
||||
HashV3ToR32(PFloor + v3{0, 1, 0}),
|
||||
HashV3ToR32(PFloor + v3{1, 1, 0}))),
|
||||
LerpR32(F.y,
|
||||
LerpR32(F.x,
|
||||
HashV3ToR32(PFloor + v3{0, 0, 1}),
|
||||
HashV3ToR32(PFloor + v3{1, 0, 1})),
|
||||
LerpR32(F.x,
|
||||
HashV3ToR32(PFloor + v3{0, 1, 1}),
|
||||
HashV3ToR32(PFloor + v3{1, 1, 1}))));
|
||||
|
||||
Assert(Result >= 0 && Result <= 1);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Noise3D_(v3 Pp)
|
||||
{
|
||||
v3 P = FloorV3(Pp);
|
||||
v3 W = FractV3(Pp);
|
||||
|
||||
//v3 U = W * W * W * (W * (W * 6.0f - 15.0f) + 10.0f);
|
||||
v3 U = V3MultiplyPairwise(W, W * 6.0f - v3{15, 15, 15});
|
||||
U = U + v3{10, 10, 10};
|
||||
U = V3MultiplyPairwise(U, W);
|
||||
U = V3MultiplyPairwise(U, W);
|
||||
U = V3MultiplyPairwise(U, W);
|
||||
|
||||
r32 N = P.x + 317.0f * P.y + 157.0f * P.z;
|
||||
|
||||
r32 A = Hash1(N + 0.0f);
|
||||
r32 B = Hash1(N + 1.0f);
|
||||
r32 C = Hash1(N + 317.0f);
|
||||
r32 D = Hash1(N + 317.0f);
|
||||
r32 E = Hash1(N + 157.0f);
|
||||
r32 F = Hash1(N + 158.0f);
|
||||
r32 G = Hash1(N + 474.0f);
|
||||
r32 H = Hash1(N + 475.0f);
|
||||
|
||||
r32 K0 = A;
|
||||
r32 K1 = B - A;
|
||||
r32 K2 = C - A;
|
||||
r32 K3 = E - A;
|
||||
r32 K4 = A - B - C + D;
|
||||
r32 K5 = A - C - E + G;
|
||||
r32 K6 = A - B - E + F;
|
||||
r32 K7 = A + B + C - D + E - F - G + H;
|
||||
|
||||
return -1.0f + 2.0f * (K0 +
|
||||
K1 * U.x +
|
||||
K2 * U.y +
|
||||
K3 * U.z +
|
||||
K4 * U.x * U.y +
|
||||
K5 * U.y + U.z +
|
||||
K6 * U.z * U.x +
|
||||
K7 * U.x * U.y * U.z);
|
||||
}
|
||||
|
||||
internal r32
|
||||
Fbm2D(v2 P)
|
||||
{
|
||||
r32 R = 0;
|
||||
r32 Amp = 1.0;
|
||||
r32 Freq = 1.0;
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
R += Amp * Noise2D(P * Freq);
|
||||
Amp *= 0.5f;
|
||||
Freq *= 1.0f / 0.5f;
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
global m44 M3 = m44{
|
||||
0.00f, 0.80f, 0.60f, 0,
|
||||
-0.80f, 0.36f, -0.48f, 0,
|
||||
-0.60f, -0.48f, 0.64f, 0,
|
||||
0, 0, 0, 1
|
||||
};
|
||||
|
||||
internal r32
|
||||
Fbm3D(v3 P)
|
||||
{
|
||||
v3 X = P;
|
||||
r32 F = 2.0f;
|
||||
r32 S = 0.5f;
|
||||
r32 A = 0.0f;
|
||||
r32 B = 0.5f;
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
r32 N = Noise3D(X);
|
||||
A += B * N;
|
||||
B *= S;
|
||||
v4 Xp = M3 * ToV4Point(X);
|
||||
X = Xp.xyz * F;
|
||||
}
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Fbm3D(v3 P, r32 T)
|
||||
{
|
||||
v3 Tt = v3{T, T, T};
|
||||
r32 SinT = SinR32(T);
|
||||
v3 Tv = v3{SinT, SinT, SinT};
|
||||
v3 Pp = P;
|
||||
r32 F = 0.0;
|
||||
|
||||
F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02;
|
||||
//F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01;
|
||||
F += 0.300000f * Noise3D(Pp - Tt); Pp = Pp * 2.03;
|
||||
F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01;
|
||||
F += 0.062500f * Noise3D(Pp + Tt); Pp = Pp * 2.04;
|
||||
//F += 0.015625f * Noise3D(Pp + Tv);
|
||||
r32 D = 0.9875f;
|
||||
|
||||
F = F / D;
|
||||
return F;
|
||||
}
|
||||
|
||||
internal r32
|
||||
Voronoise(v2 P, r32 U, r32 V)
|
||||
{
|
||||
r32 K = 1.0f + 63.0f + PowR32(1.0f - V, 6.0f);
|
||||
|
||||
v2 I = FloorV2(P);
|
||||
v2 F = FractV2(P);
|
||||
|
||||
v2 A = v2{0, 0};
|
||||
for (s32 y = -2; y <= 2; y++)
|
||||
{
|
||||
for (s32 x = -2; x <= 2; x++)
|
||||
{
|
||||
v2 G = v2{(r32)x, (r32)y};
|
||||
v3 O = V3MultiplyPairwise(Hash3(I + G), v3{U, U, 1.0f});
|
||||
v2 D = G - F + O.xy;
|
||||
r32 W = PowR32(1.0f - Smoothstep(V2Mag(D), 0.0f, 1.414f), K);
|
||||
A += v2{O.z * W, W};
|
||||
}
|
||||
}
|
||||
|
||||
return A.x / A.y;
|
||||
}
|
||||
|
||||
|
||||
v4 RGBToHSV(v4 In)
|
||||
{
|
||||
v4 Result = {};
|
||||
|
||||
r32 Min = Min(In.r, Min(In.g, In.b));
|
||||
r32 Max = Max(In.r, Max(In.g, In.b));
|
||||
|
||||
r32 V = Max;
|
||||
r32 Delta = Max - Min;
|
||||
r32 S = 0;
|
||||
r32 H = 0;
|
||||
if( Max != 0 )
|
||||
{
|
||||
S = Delta / Max;
|
||||
|
||||
if( In.r == Max )
|
||||
{
|
||||
H = ( In.g - In.b ) / Delta; // between yellow & magenta
|
||||
}
|
||||
else if( In.g == Max )
|
||||
{
|
||||
H = 2 + ( In.b - In.r ) / Delta; // between cyan & yellow
|
||||
}
|
||||
else
|
||||
{
|
||||
H = 4 + ( In.r - In.g ) / Delta; // between magenta & cyan
|
||||
}
|
||||
H *= 60; // degrees
|
||||
if( H < 0 )
|
||||
H += 360;
|
||||
Result = v4{H, S, V, 1};
|
||||
}
|
||||
else
|
||||
{
|
||||
// r = g = b = 0
|
||||
// s = 0, v is undefined
|
||||
S = 0;
|
||||
H = -1;
|
||||
Result = v4{H, S, 1, 1};
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
v4 HSVToRGB (v4 In)
|
||||
{
|
||||
float Hue = In.x;
|
||||
/*
|
||||
while (Hue > 360.0f) { Hue -= 360.0f; }
|
||||
while (Hue < 0.0f) { Hue += 360.0f; }
|
||||
*/
|
||||
Hue = ModR32(Hue, 360.0f);
|
||||
if (Hue < 0) { Hue += 360.0f; }
|
||||
if (Hue == MinR32) { Hue = 0; }
|
||||
if (Hue == MaxR32) { Hue = 360; }
|
||||
Assert(Hue >= 0 && Hue < 360);
|
||||
|
||||
float Sat = In.y;
|
||||
float Value = In.z;
|
||||
|
||||
float hh, p, q, t, ff;
|
||||
long i;
|
||||
v4 Result = {};
|
||||
Result.a = In.a;
|
||||
|
||||
if(Sat <= 0.0f) { // < is bogus, just shuts up warnings
|
||||
Result.r = Value;
|
||||
Result.g = Value;
|
||||
Result.b = Value;
|
||||
return Result;
|
||||
}
|
||||
hh = Hue;
|
||||
if(hh >= 360.0f) hh = 0.0f;
|
||||
hh /= 60.0f;
|
||||
i = (long)hh;
|
||||
ff = hh - i;
|
||||
p = Value * (1.0f - Sat);
|
||||
q = Value * (1.0f - (Sat * ff));
|
||||
t = Value * (1.0f - (Sat * (1.0f - ff)));
|
||||
|
||||
switch(i) {
|
||||
case 0:
|
||||
{Result.r = Value;
|
||||
Result.g = t;
|
||||
Result.b = p;
|
||||
}break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
Result.r = q;
|
||||
Result.g = Value;
|
||||
Result.b = p;
|
||||
}break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
Result.r = p;
|
||||
Result.g = Value;
|
||||
Result.b = t;
|
||||
}break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
Result.r = p;
|
||||
Result.g = q;
|
||||
Result.b = Value;
|
||||
}break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
Result.r = t;
|
||||
Result.g = p;
|
||||
Result.b = Value;
|
||||
}break;
|
||||
|
||||
case 5:
|
||||
default:
|
||||
{
|
||||
Result.r = Value;
|
||||
Result.g = p;
|
||||
Result.b = q;
|
||||
}break;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
V4ToRGBPixel(v4 C)
|
||||
{
|
||||
C.x = Clamp01(C.x);
|
||||
C.y = Clamp01(C.y);
|
||||
C.z = Clamp01(C.z);
|
||||
|
||||
pixel Result = {};
|
||||
Result.R = (u8)(C.x * 255);
|
||||
Result.G = (u8)(C.y * 255);
|
||||
Result.B = (u8)(C.z * 255);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif //GFX_MATH_H
|
|
@ -1,83 +0,0 @@
|
|||
|
||||
internal void
|
||||
MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena)
|
||||
{
|
||||
gs_data MessageQueueData = PushSize(Arena, DEFAULT_QUEUE_ENTRY_SIZE * BLUMEN_MESSAGE_QUEUE_COUNT);
|
||||
gs_memory_cursor C = MemoryCursorCreateFromData(MessageQueueData);
|
||||
|
||||
for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++)
|
||||
{
|
||||
Queue->Buffers[i] = MemoryCursorPushSize(&C, DEFAULT_QUEUE_ENTRY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool
|
||||
MessageQueue_CanWrite(blumen_network_msg_queue Queue)
|
||||
{
|
||||
bool Result = ((Queue.WriteHead >= Queue.ReadHead) ||
|
||||
(Queue.WriteHead < Queue.ReadHead));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal bool
|
||||
MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg)
|
||||
{
|
||||
Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE);
|
||||
|
||||
u32 Index = Queue->WriteHead;
|
||||
Assert(Index >= 0 &&
|
||||
Index < BLUMEN_MESSAGE_QUEUE_COUNT);
|
||||
|
||||
gs_data* Dest = Queue->Buffers + Index;
|
||||
CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size);
|
||||
Dest->Size = Msg.Size;
|
||||
|
||||
// NOTE(pjs): We increment write head at the end of writing so that
|
||||
// a reader thread doesn't pull the message off before we've finished
|
||||
// filling it out
|
||||
Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool
|
||||
MessageQueue_CanRead(blumen_network_msg_queue Queue)
|
||||
{
|
||||
bool Result = (Queue.ReadHead != Queue.WriteHead);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_data
|
||||
MessageQueue_Peek(blumen_network_msg_queue* Queue)
|
||||
{
|
||||
gs_data Result = {};
|
||||
u32 ReadIndex = Queue->ReadHead;
|
||||
if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT)
|
||||
{
|
||||
ReadIndex = 0;
|
||||
}
|
||||
Result = Queue->Buffers[ReadIndex];
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_data
|
||||
MessageQueue_Read(blumen_network_msg_queue* Queue)
|
||||
{
|
||||
gs_data Result = {};
|
||||
u32 ReadIndex = Queue->ReadHead++;
|
||||
if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT)
|
||||
{
|
||||
Queue->ReadHead = 0;
|
||||
ReadIndex = 0;
|
||||
}
|
||||
Result = Queue->Buffers[ReadIndex];
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
MessageQueue_Clear(blumen_network_msg_queue* Queue)
|
||||
{
|
||||
while (MessageQueue_CanRead(*Queue))
|
||||
{
|
||||
MessageQueue_Read(Queue);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/* date = March 27th 2021 3:07 pm */
|
||||
|
||||
#ifndef MESSAGE_QUEUE_H
|
||||
#define MESSAGE_QUEUE_H
|
||||
|
||||
#define BLUMEN_MESSAGE_QUEUE_COUNT 32
|
||||
typedef struct blumen_network_msg_queue
|
||||
{
|
||||
gs_data Buffers[BLUMEN_MESSAGE_QUEUE_COUNT];
|
||||
u32 WriteHead;
|
||||
u32 ReadHead;
|
||||
} blumen_network_msg_queue;
|
||||
|
||||
// KB(1) is just bigger than any packet we send. Good for now
|
||||
#define DEFAULT_QUEUE_ENTRY_SIZE KB(1)
|
||||
|
||||
#endif //MESSAGE_QUEUE_H
|
|
@ -1,283 +0,0 @@
|
|||
/* date = March 27th 2021 1:55 pm */
|
||||
|
||||
#ifndef PHRASE_HUE_MAP_H
|
||||
#define PHRASE_HUE_MAP_H
|
||||
|
||||
enum p_hue_flag
|
||||
{
|
||||
Hue_Value = 0,
|
||||
Hue_White = 1,
|
||||
Hue_Black = 2,
|
||||
};
|
||||
|
||||
enum p_hue_pattern
|
||||
{
|
||||
HuePattern_Patchy,
|
||||
HuePattern_Wavy,
|
||||
|
||||
HuePattern_Count,
|
||||
};
|
||||
|
||||
enum p_hue_add_in
|
||||
{
|
||||
AddIn_None,
|
||||
AddIn_Waves,
|
||||
AddIn_Rotary,
|
||||
|
||||
AddIn_Count,
|
||||
};
|
||||
|
||||
typedef struct p_hue
|
||||
{
|
||||
v4 HSV;
|
||||
} p_hue;
|
||||
|
||||
typedef struct phrase_hue_map
|
||||
{
|
||||
u64 CountMax;
|
||||
u64 Count;
|
||||
gs_const_string* Phrases;
|
||||
u64* PhraseHashes;
|
||||
p_hue* Hue0;
|
||||
p_hue* Hue1;
|
||||
p_hue* Hue2;
|
||||
u32* Gran; // granularity
|
||||
r32* Speed;
|
||||
u8* Pattern;
|
||||
u8* AddIn;
|
||||
bool* OverrideAll;
|
||||
} phrase_hue_map;
|
||||
|
||||
typedef struct phrase_hue
|
||||
{
|
||||
gs_const_string Phrase;
|
||||
u64 PhraseHash;
|
||||
p_hue Hue0;
|
||||
p_hue Hue1;
|
||||
p_hue Hue2;
|
||||
u32 Granularity;
|
||||
r32 Speed;
|
||||
u8 Pattern;
|
||||
u8 AddIn;
|
||||
bool OverrideAll;
|
||||
} phrase_hue;
|
||||
|
||||
internal p_hue
|
||||
LerpPHue(r32 T, p_hue A, p_hue B)
|
||||
{
|
||||
p_hue Result = {};
|
||||
|
||||
if (Abs(A.HSV.x - B.HSV.x) < 180.0f)
|
||||
{
|
||||
Result.HSV.x = LerpR64(T, A.HSV.x, B.HSV.x);
|
||||
}
|
||||
else if (B.HSV.x > A.HSV.x)
|
||||
{
|
||||
Result.HSV.x = LerpR64(T, A.HSV.x, B.HSV.x - 360.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.HSV.x = LerpR64(T, A.HSV.x - 360.0f, B.HSV.x);
|
||||
}
|
||||
if (Result.HSV.x < 360) Result.HSV.x += 360;
|
||||
if (Result.HSV.x > 360) Result.HSV.x -= 360;
|
||||
Result.HSV.x = Clamp(0, Result.HSV.x, 360);
|
||||
Result.HSV.y = LerpR32(T, A.HSV.y, B.HSV.y);
|
||||
Result.HSV.z = LerpR32(T, A.HSV.z, B.HSV.z);
|
||||
Result.HSV.w = LerpR32(T, A.HSV.w, B.HSV.w);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal phrase_hue
|
||||
LerpPhraseHue(r32 T, phrase_hue A, phrase_hue B)
|
||||
{
|
||||
phrase_hue Result = {};
|
||||
Result.Hue0 = LerpPHue(T, A.Hue0, B.Hue0);
|
||||
Result.Hue1 = LerpPHue(T, A.Hue1, B.Hue1);
|
||||
Result.Hue2 = LerpPHue(T, A.Hue2, B.Hue2);
|
||||
Result.Granularity = (u32)LerpR32(T, (r32)A.Granularity, (r32)B.Granularity);
|
||||
Result.Speed = LerpR32(T, A.Speed, B.Speed);
|
||||
|
||||
if (T < .5f) {
|
||||
Result.Phrase = A.Phrase;
|
||||
Result.PhraseHash = A.PhraseHash;
|
||||
Result.Pattern = A.Pattern;
|
||||
Result.AddIn = A.AddIn;
|
||||
}
|
||||
else {
|
||||
Result.Phrase = B.Phrase;
|
||||
Result.PhraseHash = B.PhraseHash;
|
||||
Result.Pattern = B.Pattern;
|
||||
Result.AddIn = B.AddIn;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal p_hue
|
||||
CreateHueFromString(gs_const_string Str)
|
||||
{
|
||||
p_hue Result = {};
|
||||
if (Str.Str[0] == 'b') {
|
||||
Result.HSV = v4{0, 0, 0, 1 };
|
||||
} else if (Str.Str[0] == 'w') {
|
||||
Result.HSV = v4{0, 0, 1, 1 };;
|
||||
} else {
|
||||
parse_float_result Parsed = ValidateAndParseFloat(Str);
|
||||
if (!Parsed.Success)
|
||||
{
|
||||
Log_Error(GlobalLogBuffer, "Failed to Parse CSV Float\n");
|
||||
Parsed.Value = 0.0;
|
||||
}
|
||||
Result.HSV = v4{ (r32)Parsed.Value, 1, 1, 1 };
|
||||
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal v4
|
||||
RGBFromPhraseHue (p_hue H)
|
||||
{
|
||||
v4 Result = H.HSV;
|
||||
Result = HSVToRGB(Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal phrase_hue_map
|
||||
PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena)
|
||||
{
|
||||
phrase_hue_map Result = {};
|
||||
if (Sheet.RowCount == 0) return Result;
|
||||
|
||||
Result.CountMax = Sheet.RowCount - 1; // we don't include the header row
|
||||
Result.Phrases = PushArray(Arena, gs_const_string, Result.CountMax);
|
||||
Result.PhraseHashes = PushArray(Arena, u64, Result.CountMax);
|
||||
Result.Hue0 = PushArray(Arena, p_hue, Result.CountMax);
|
||||
Result.Hue1 = PushArray(Arena, p_hue, Result.CountMax);
|
||||
Result.Hue2 = PushArray(Arena, p_hue, Result.CountMax);
|
||||
Result.Gran = PushArray(Arena, u32, Result.CountMax);
|
||||
Result.Pattern = PushArray(Arena, u8, Result.CountMax);
|
||||
Result.Speed = PushArray(Arena, r32, Result.CountMax);
|
||||
Result.AddIn = PushArray(Arena, u8, Result.CountMax);
|
||||
Result.OverrideAll = PushArray(Arena, bool, Result.CountMax);
|
||||
|
||||
// this lets us tightly pack phrase_hues even if there is a
|
||||
// row in the csv that is empty or invalid
|
||||
s32 DestOffset = 0;
|
||||
for (u32 Row = 1; Row < Sheet.RowCount; Row++)
|
||||
{
|
||||
s32 Index = (Row - 1) - DestOffset;
|
||||
Assert(Index >= 0 && Index < Result.CountMax);
|
||||
|
||||
gs_const_string Phrase = CSVSheet_GetCell(Sheet,
|
||||
0, Row);
|
||||
gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row);
|
||||
gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row);
|
||||
gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, 3, Row);
|
||||
gs_const_string Homonyms = CSVSheet_GetCell(Sheet, 4, Row);
|
||||
gs_const_string Granularity = CSVSheet_GetCell(Sheet, 5, Row);
|
||||
gs_const_string Speed = CSVSheet_GetCell(Sheet, 6, Row);
|
||||
gs_const_string Pattern = CSVSheet_GetCell(Sheet, 7, Row);
|
||||
gs_const_string AddIn = CSVSheet_GetCell(Sheet, 8, Row);
|
||||
gs_const_string OverrideAll = CSVSheet_GetCell(Sheet, 9, Row);
|
||||
|
||||
// essential parameters
|
||||
if (Phrase.Length == 0 ||
|
||||
Hue0Str.Length == 0 ||
|
||||
Hue1Str.Length == 0 ||
|
||||
Hue2Str.Length == 0)
|
||||
{
|
||||
DestOffset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Result.Phrases[Index] = PushStringF(Arena, Phrase.Length, "%S", Phrase).ConstString;
|
||||
for (u64 i = 0; i < Result.Phrases[Index].Length; i++)
|
||||
{
|
||||
char C = Result.Phrases[Index].Str[i];
|
||||
if (IsAlpha(C))
|
||||
{
|
||||
Result.Phrases[Index].Str[i] = ToLower(C);
|
||||
}
|
||||
}
|
||||
|
||||
u64 PhraseHash = HashDJB2ToU32(StringExpand(Result.Phrases[Index]));
|
||||
|
||||
Result.PhraseHashes[Index] = PhraseHash;
|
||||
Result.Hue0[Index] = CreateHueFromString(Hue0Str);
|
||||
Result.Hue1[Index] = CreateHueFromString(Hue1Str);
|
||||
Result.Hue2[Index] = CreateHueFromString(Hue2Str);
|
||||
|
||||
parse_float_result ParsedSpeed = ValidateAndParseFloat(Speed);
|
||||
if (!ParsedSpeed.Success)
|
||||
{
|
||||
ParsedSpeed.Value = 1.0;
|
||||
}
|
||||
Result.Speed[Index] = ParsedSpeed.Value;
|
||||
|
||||
if (StringsEqual(Pattern, ConstString("wavy")))
|
||||
{
|
||||
Result.Pattern[Index] = HuePattern_Wavy;
|
||||
} else {
|
||||
Result.Pattern[Index] = HuePattern_Patchy;
|
||||
}
|
||||
|
||||
if (StringsEqual(AddIn, ConstString("waves")))
|
||||
{
|
||||
Result.AddIn[Index] = AddIn_Waves;
|
||||
} else if (StringsEqual(AddIn, ConstString("rotary"))) {
|
||||
Result.AddIn[Index] = AddIn_Rotary;
|
||||
} else {
|
||||
Result.AddIn[Index] = AddIn_None;
|
||||
}
|
||||
|
||||
parse_uint_result ParsedGranularity = ValidateAndParseUInt(Granularity);
|
||||
if (!ParsedGranularity.Success)
|
||||
{
|
||||
ParsedGranularity.Value = 1;
|
||||
}
|
||||
Result.Gran[Index] = ParsedGranularity.Value;
|
||||
|
||||
Result.OverrideAll[Index] = StringsEqualUpToLength(OverrideAll, ConstString("yes"), 3);
|
||||
}
|
||||
|
||||
Result.Count = Result.CountMax + DestOffset;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal phrase_hue
|
||||
PhraseHueMap_Get(phrase_hue_map Map, u32 Index)
|
||||
{
|
||||
Assert(Index < Map.Count);
|
||||
phrase_hue Result = {};
|
||||
Result.Phrase = Map.Phrases[Index];
|
||||
Result.PhraseHash = Map.PhraseHashes[Index];
|
||||
Result.Hue0 = Map.Hue0[Index];
|
||||
Result.Hue1 = Map.Hue1[Index];
|
||||
Result.Hue2 = Map.Hue2[Index];
|
||||
Result.Granularity = Map.Gran[Index];
|
||||
Result.Speed = Map.Speed[Index];
|
||||
Result.AddIn = Map.AddIn[Index];
|
||||
Result.Pattern = Map.Pattern[Index];
|
||||
Result.OverrideAll = Map.OverrideAll[Index];
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal phrase_hue
|
||||
PhraseHueMap_Find(phrase_hue_map Map, u64 PhraseHash)
|
||||
{
|
||||
phrase_hue Result = {};
|
||||
for (u32 i = 0; i < Map.Count; i++)
|
||||
{
|
||||
if (Map.PhraseHashes[i] == PhraseHash)
|
||||
{
|
||||
Result = PhraseHueMap_Get(Map, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif //PHRASE_HUE_MAP_H
|
|
@ -1,20 +0,0 @@
|
|||
/* date = March 29th 2021 10:41 pm */
|
||||
|
||||
#ifndef SDF_H
|
||||
#define SDF_H
|
||||
|
||||
internal r32
|
||||
SDF_Sphere(v3 Point, v3 Center, r32 Radius)
|
||||
{
|
||||
r32 Result = V3Mag(Point - Center) - Radius;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal r32
|
||||
SDF_SphereNormalized(v3 Point, v3 Center, r32 Radius)
|
||||
{
|
||||
r32 Result = SDF_Sphere(Point, Center, Radius) / Radius;
|
||||
return Result;
|
||||
}
|
||||
|
||||
#endif //SDF_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue