support both for building for Windows and via MSVC

This commit is contained in:
Zack Buhman 2026-03-22 16:14:29 -07:00
parent fa3ca59059
commit 8d33b39730
25 changed files with 231 additions and 51 deletions

6
.gitignore vendored
View File

@ -1,3 +1,9 @@
*.dll
*.exp
*.lib
*.pdb
*.idb
*.def
.~* .~*
.\#* .\#*
\#* \#*

View File

@ -14,14 +14,12 @@ CFLAGS += -fpic
CFLAGS += -I./include CFLAGS += -I./include
CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -Wno-error=unused-but-set-variable CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -Wno-error=unused-but-set-variable
CFLAGS += -Wno-error=unknown-pragmas -Wno-unknown-pragmas CFLAGS += -Wno-error=unknown-pragmas -Wno-unknown-pragmas
CFLAGS += $(shell pkg-config --cflags glfw3)
CFLAGS += -fno-strict-aliasing CFLAGS += -fno-strict-aliasing
ifdef READ_PACK_FILE ifdef READ_PACK_FILE
CFLAGS += -DREAD_PACK_FILE CFLAGS += -DREAD_PACK_FILE
endif endif
LDFLAGS += -lm LDFLAGS += -lm
LDFLAGS += $(shell pkg-config --libs glfw3)
MINECRAFT_OBJS = \ MINECRAFT_OBJS = \
minecraft/love2dworld/inthash.o \ minecraft/love2dworld/inthash.o \
@ -83,6 +81,9 @@ test.pack.o: test.pack
test.so: $(OBJS) test.so: $(OBJS)
$(CC) $(ARCH) $(OPT) -Wl,-z noexecstack -shared -g $^ -o $@ -lSDL3 $(CC) $(ARCH) $(OPT) -Wl,-z noexecstack -shared -g $^ -o $@ -lSDL3
test.dll: $(OBJS)
$(CXX) $(ARCH) $(OPT) -mthreads -static -mdll -static-libstdc++ -static-libgcc -g $^ -o $@ -L. -lSDL3 $(WINDOWS)
main: $(OBJS) src/main.o main: $(OBJS) src/main.o
$(CC) $(ARCH) $(LDFLAGS) $(OPT) -g $^ -o $@ $(CC) $(ARCH) $(LDFLAGS) $(OPT) -g $^ -o $@

View File

@ -6,13 +6,13 @@
namespace collada::instance_types { namespace collada::instance_types {
struct __attribute__((aligned(16))) lookat { struct XM_ALIGNED_DATA(16) lookat {
XMVECTOR eye; XMVECTOR eye;
XMVECTOR at; XMVECTOR at;
XMVECTOR up; XMVECTOR up;
}; };
struct __attribute__((aligned(16))) transform { struct XM_ALIGNED_DATA(16) transform {
union { union {
instance_types::lookat lookat; instance_types::lookat lookat;
XMMATRIX matrix; XMMATRIX matrix;

9
include/declarations.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define DECL __cdecl
#else
#define EXPORT __attribute__((dllexport))
#define DECL __attribute__((__cdecl__))
#endif

View File

@ -9,8 +9,10 @@
#pragma once #pragma once
#ifdef __GNUC__
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif
#ifndef __cplusplus #ifndef __cplusplus
#error DirectX Math requires C++ #error DirectX Math requires C++
@ -2218,4 +2220,6 @@ namespace DirectX
using namespace DirectX; using namespace DirectX;
#ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif

View File

@ -10,7 +10,7 @@ namespace font::outline::types {
int32_t horiBearingX; int32_t horiBearingX;
int32_t horiBearingY; int32_t horiBearingY;
int32_t horiAdvance; int32_t horiAdvance;
} __attribute__ ((packed)); };
static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 3)); static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 3));
@ -19,14 +19,14 @@ namespace font::outline::types {
uint16_t y; uint16_t y;
uint16_t width; uint16_t width;
uint16_t height; uint16_t height;
} __attribute__ ((packed)); };
static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint16_t)) * 4)); static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint16_t)) * 4));
struct glyph { struct glyph {
struct glyph_bitmap bitmap; struct glyph_bitmap bitmap;
struct glyph_metrics metrics; struct glyph_metrics metrics;
} __attribute__ ((packed)); };
static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_bitmap)) + (sizeof (struct glyph_metrics)))); static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_bitmap)) + (sizeof (struct glyph_metrics))));
@ -42,7 +42,7 @@ namespace font::outline::types {
uint16_t texture_width; uint16_t texture_width;
uint16_t texture_height; uint16_t texture_height;
uint32_t max_z_curve_ix; uint32_t max_z_curve_ix;
} __attribute__ ((packed)); };
static_assert((sizeof (struct font)) == ((sizeof (uint32_t)) * 7)); static_assert((sizeof (struct font)) == ((sizeof (uint32_t)) * 7));

View File

@ -2,6 +2,14 @@
#include <stdlib.h> #include <stdlib.h>
#ifdef aligned_alloc
#undef aligned_alloc
#endif
#ifdef _WIN32
#define aligned_alloc(alignment, size) _aligned_malloc(size, alignment)
#endif
template <typename T> template <typename T>
T * New(int elements) T * New(int elements)
{ {

6
include/popcount.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
#define __builtin_popcount __popcnt
#endif

View File

@ -1,25 +1,27 @@
#pragma once #pragma once
#include "declarations.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void load(const char * source_path); EXPORT void DECL load(const char * source_path);
void draw(); EXPORT void DECL draw();
void love2d_state_load(); EXPORT void DECL love2d_state_load();
void love2d_state_restore(); EXPORT void DECL love2d_state_restore();
void update_keyboard(int up, int down, int left, int right, EXPORT void DECL update_keyboard(int up, int down, int left, int right,
int w, int s, int a, int d, int w, int s, int a, int d,
int t, int g, int f, int h, int t, int g, int f, int h,
int i, int k, int j, int l); int i, int k, int j, int l);
void update_mouse(int x, int y); EXPORT void DECL update_mouse(int x, int y);
void update_joystick(int joystick_index, EXPORT void DECL update_joystick(int joystick_index,
float lx, float ly, float rx, float ry, float tl, float tr, float lx, float ly, float rx, float ry, float tl, float tr,
int up, int down, int left, int right, int up, int down, int left, int right,
int a, int b, int x, int y, int a, int b, int x, int y,
int leftshoulder, int rightshoulder, int leftshoulder, int rightshoulder,
int start); int start);
void update(float time); EXPORT void DECL update(float time);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "declarations.h"
namespace window { namespace window {
extern float width; extern float width;
extern float height; extern float height;
@ -8,7 +10,7 @@ namespace window {
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void update_window(int width, int height); EXPORT void DECL update_window(int width, int height);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -37,6 +37,8 @@ void update(float time);
local is_zip = source_path:sub(-#".zip") == ".zip" local is_zip = source_path:sub(-#".zip") == ".zip"
local is_love = source_path:sub(-#".love") == ".love" local is_love = source_path:sub(-#".love") == ".love"
local platform = love.system.getOS()
if is_zip or is_love then if is_zip or is_love then
if love.filesystem.isFused() then if love.filesystem.isFused() then
local archive = love.filesystem.getSourceBaseDirectory() local archive = love.filesystem.getSourceBaseDirectory()
@ -49,9 +51,21 @@ void update(float time);
-- the love2d "filesystem" API is the worst possible design in the -- the love2d "filesystem" API is the worst possible design in the
-- entire history of computing -- entire history of computing
if platform == "Linux" then
test = ffi.load(app_data .. "/love/love-demo2/test.so") test = ffi.load(app_data .. "/love/love-demo2/test.so")
elseif platform == "Windows" then
test = ffi.load(app_data .. "/love/love-demo2/test.dll")
else else
assert(false, "unsupported platform: " .. platform)
end
else
if platform == "Linux" then
test = ffi.load("./test.so") test = ffi.load("./test.so")
elseif platform == "Windows" then
test = ffi.load("./test.dll")
else
assert(false, "unsupported platform: " .. platform)
end
end end
test.load(source_path) test.load(source_path)
end end

71
msvc/Makefile_windows Normal file
View File

@ -0,0 +1,71 @@
#PREFIX = x86_64-w64-mingw32-
CC=$(PREFIX)gcc
CXX=$(PREFIX)g++
OBJCOPY=$(PREFIX)objcopy
OBJARCH = elf64-x86-64
OPT = -O0 -march=x86-64-v3
CSTD = -std=gnu23
CXXSTD = -std=gnu++23
CFLAGS += -g
CFLAGS += -fpic
CFLAGS += -I./include
CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -Wno-error=unused-but-set-variable
CFLAGS += -Wno-error=unknown-pragmas -Wno-unknown-pragmas
CFLAGS += -fno-strict-aliasing
CFLAGS += -no-pthread
ifdef READ_PACK_FILE
CFLAGS += -DREAD_PACK_FILE
endif
LDFLAGS += -lm
MINECRAFT_OBJS = \
minecraft/love2dworld/inthash.o \
minecraft/grandlecturn/inthash.o \
minecraft/midnightmeadow/inthash.o \
src/minecraft.o \
src/world/world.o \
src/world/entry_table.o
OBJS = \
src/gl.o \
src/opengl.o \
src/test.o \
src/font/bitmap.o \
src/font/outline.o \
src/window.o \
src/bresenham.o \
src/file.o \
src/non_block.o \
src/view.o \
src/hud.o \
src/lighting.o \
src/collision_scene.o \
src/line_art.o \
src/boids.o \
src/boids_scene.o \
src/dds_validate.o \
src/collada/scene.o \
src/collada/effect.o \
src/collada/node_state.o \
src/collada/animate.o \
src/lua_api.o \
src/pixel_line_art.o \
src/flame.o \
$(MINECRAFT_OBJS)
%.o: %.c
cl.exe /Fo"$@" @"compile_debug.rsp" $<
%.o: %.cpp
cl.exe /Fo"$@" @"compile_debug.rsp" $<
#link.exe /OUT:"$@" @"link_release.rsp" $(OBJS) /NOLOGO /ERRORREPORT:PROMPT
test.dll: $(OBJS)
cl.exe /DEBUG /D_USRDLL /D_WINDLL $(OBJS) /link /DLL /OUT:$@ /LIBPATH:../SDL/Debug SDL3.lib /PDB:"test.pdb"
all: $(OBJS)

18
msvc/compile_debug.rsp Normal file
View File

@ -0,0 +1,18 @@
/Od
/D "WIN32"
/D "_DEBUG"
/D "_WINDOWS"
/D "_UNICODE"
/D "UNICODE"
/D "_WIN32_WINNT=0x0501"
/I ".\include"
/I "..\SDL\include"
/EHsc
/RTC1
/MDd
/W3
/c
/Z7
/nologo
/errorReport:prompt
/std:c++20

19
msvc/compile_release.rsp Normal file
View File

@ -0,0 +1,19 @@
/O2
/GL
/D "WIN32"
/D "NDEBUG"
/D "_WINDOWS"
/D "_UNICODE"
/D "UNICODE"
/D "_WIN32_WINNT=0x0501"
/I ".\include"
/I "..\SDL\include"
/FD
/EHsc
/MT
/W3
/c
/Zi
/nologo
/errorReport:prompt
/std:c++20

7
msvc/link_release.rsp Normal file
View File

@ -0,0 +1,7 @@
/MANIFEST:NO
/INCREMENTAL:NO
/OPT:REF
/OPT:ICF
/LTCG
/MACHINE:X64
SDL3.lib

View File

@ -26,7 +26,7 @@ namespace boids {
forces.separation.force = XMVectorZero(); forces.separation.force = XMVectorZero();
} }
inline constexpr bool vector_equal(XMVECTOR V1, XMVECTOR V2) static inline bool vector_equal(XMVECTOR V1, XMVECTOR V2)
{ {
uint32_t CR; uint32_t CR;
XMVectorEqualR(&CR, V1, V2); XMVectorEqualR(&CR, V1, V2);

View File

@ -121,7 +121,7 @@ namespace collada::animate {
float value) float value)
{ {
switch (transform.type) { switch (transform.type) {
case types::transform_type::TRANSLATE: __attribute__((fallthrough)); case types::transform_type::TRANSLATE: [[fallthrough]];
case types::transform_type::SCALE: case types::transform_type::SCALE:
switch (channel_target_attribute) { switch (channel_target_attribute) {
case types::target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return; case types::target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
@ -164,7 +164,7 @@ namespace collada::animate {
int target_attributes_count = 1; int target_attributes_count = 1;
if (channel.target_attribute == types::target_attribute::ALL) { if (channel.target_attribute == types::target_attribute::ALL) {
switch (transform.type) { switch (transform.type) {
case types::transform_type::TRANSLATE: __attribute__((fallthrough)); case types::transform_type::TRANSLATE: [[fallthrough]];
case types::transform_type::SCALE: case types::transform_type::SCALE:
target_attributes = translate_scale_target_attributes; target_attributes = translate_scale_target_attributes;
target_attributes_count = 3; target_attributes_count = 3;

View File

@ -446,14 +446,16 @@ namespace collada::scene {
void state::draw_instance_controllers(types::instance_controller const * const instance_controllers, void state::draw_instance_controllers(types::instance_controller const * const instance_controllers,
int const instance_controllers_count) int const instance_controllers_count)
{ {
static int const max_joints = 64;
static XMFLOAT4X4 joints[max_joints];
for (int i = 0; i < instance_controllers_count; i++) { for (int i = 0; i < instance_controllers_count; i++) {
types::instance_controller const &instance_controller = instance_controllers[i]; types::instance_controller const &instance_controller = instance_controllers[i];
types::skin const &skin = instance_controller.controller->skin; types::skin const &skin = instance_controller.controller->skin;
XMMATRIX bsm = XMLoadFloat4x4((XMFLOAT4X4*)&skin.bind_shape_matrix); XMMATRIX bsm = XMLoadFloat4x4((XMFLOAT4X4*)&skin.bind_shape_matrix);
XMFLOAT4X4 joints[instance_controller.joint_count]; assert(instance_controller.joint_count < max_joints);
int joints_size = (sizeof (joints));
for (int joint_index = 0; joint_index < instance_controller.joint_count; joint_index++) { for (int joint_index = 0; joint_index < instance_controller.joint_count; joint_index++) {
XMMATRIX ibm = XMLoadFloat4x4((XMFLOAT4X4*)&skin.inverse_bind_matrices[joint_index]); XMMATRIX ibm = XMLoadFloat4x4((XMFLOAT4X4*)&skin.inverse_bind_matrices[joint_index]);
int node_index = instance_controller.joint_node_indices[joint_index]; int node_index = instance_controller.joint_node_indices[joint_index];
@ -461,6 +463,7 @@ namespace collada::scene {
XMStoreFloat4x4(&joints[joint_index], bsm * ibm * node_instance.world); XMStoreFloat4x4(&joints[joint_index], bsm * ibm * node_instance.world);
} }
int joints_size = (sizeof (XMFLOAT4X4)) * instance_controller.joint_count;
glBindBuffer(GL_UNIFORM_BUFFER, joint_uniform_buffer); glBindBuffer(GL_UNIFORM_BUFFER, joint_uniform_buffer);
glBufferData(GL_UNIFORM_BUFFER, joints_size, (void *)&joints[0], GL_DYNAMIC_DRAW); glBufferData(GL_UNIFORM_BUFFER, joints_size, (void *)&joints[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0); glBindBuffer(GL_UNIFORM_BUFFER, 0);

View File

@ -41,7 +41,7 @@ static inline dds_size_levels dds_mip_total_size(uintptr_t data,
width /= 2; width /= 2;
} }
return (dds_size_levels){mip_total_size, mip_levels}; return {mip_total_size, mip_levels};
} }
DDS_FILE const * dds_validate(void const * data, unsigned int size, void ** out_data, int * out_size) DDS_FILE const * dds_validate(void const * data, unsigned int size, void ** out_data, int * out_size)

View File

@ -69,7 +69,7 @@ namespace font::bitmap {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
return (font){ return {
.desc = &desc, .desc = &desc,
.texture = texture, .texture = texture,
.stride = desc.texture_width / desc.glyph_width, .stride = desc.texture_width / desc.glyph_width,

View File

@ -74,7 +74,7 @@ namespace font::outline {
// font_data intentionally not free'd // font_data intentionally not free'd
return (outline::font){ return {
.texture = texture, .texture = texture,
.font = font, .font = font,
.glyphs = glyphs, .glyphs = glyphs,

View File

@ -11,7 +11,7 @@
extern unsigned int empty_vertex_array_object; extern unsigned int empty_vertex_array_object;
extern unsigned int quad_index_buffer; extern unsigned int quad_index_buffer;
extern geometry_buffer<3> geometry_buffer_pnc; extern geometry_buffer<4> geometry_buffer_pnc;
namespace lighting { namespace lighting {

View File

@ -11,6 +11,7 @@
#include "minecraft.h" #include "minecraft.h"
#include "minecraft_data.inc" #include "minecraft_data.inc"
#include "new.h" #include "new.h"
#include "popcount.h"
#include "world/world.h" #include "world/world.h"
namespace minecraft { namespace minecraft {
@ -205,7 +206,10 @@ namespace minecraft {
static void load_regions(world::descriptor const * const descriptor, world::region * region) static void load_regions(world::descriptor const * const descriptor, world::region * region)
{ {
unsigned int per_instance_vertex_buffers[descriptor->region_count]; static int const max_region_count = 128;
static unsigned int per_instance_vertex_buffers[max_region_count];
assert(descriptor->region_count < max_region_count);
glGenBuffers(descriptor->region_count, per_instance_vertex_buffers); glGenBuffers(descriptor->region_count, per_instance_vertex_buffers);
for (int i = 0; i < descriptor->region_count; i++) { for (int i = 0; i < descriptor->region_count; i++) {
// vtx // vtx

View File

@ -28,6 +28,8 @@
#include "pixel_line_art.h" #include "pixel_line_art.h"
#include "flame.h" #include "flame.h"
#include "new.h" #include "new.h"
#include "popcount.h"
#include "declarations.h"
#include "world/entry_table.h" #include "world/entry_table.h"
#include "world/world.h" #include "world/world.h"
@ -74,10 +76,14 @@ font::outline::font * uncial_antiqua_fonts;
geometry_buffer<4> geometry_buffer_pnc = {}; geometry_buffer<4> geometry_buffer_pnc = {};
static target_type const geometry_buffer_pnc_types[4] = { static target_type const geometry_buffer_pnc_types[4] = {
[target_name::POSITION] = { GL_RGBA16F, GL_COLOR_ATTACHMENT0 }, //[target_name::POSITION] =
[target_name::NORMAL] = { GL_RGBA16F, GL_COLOR_ATTACHMENT1 }, { GL_RGBA16F, GL_COLOR_ATTACHMENT0 },
[target_name::COLOR] = { GL_RGBA8, GL_COLOR_ATTACHMENT2 }, //[target_name::NORMAL] =
[target_name::BLOCK] = { GL_RGBA16F, GL_COLOR_ATTACHMENT3 }, { GL_RGBA16F, GL_COLOR_ATTACHMENT1 },
//[target_name::COLOR] =
{ GL_RGBA8, GL_COLOR_ATTACHMENT2 },
//[target_name::BLOCK] =
{ GL_RGBA16F, GL_COLOR_ATTACHMENT3 },
}; };
collada::instance_types::node * node_eye; collada::instance_types::node * node_eye;
@ -110,9 +116,7 @@ void load_quad_program()
quad_program = program; quad_program = program;
} }
extern "C" { extern "C" void * DECL SDL_GL_GetProcAddress(const char *proc);
void * SDL_GL_GetProcAddress(const char *proc);
}
void load(const char * source_path) void load(const char * source_path)
{ {
@ -194,10 +198,10 @@ void load(const char * source_path)
// collada // collada
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
collada::effect::load_effects(); //collada::effect::load_effects();
scene_state.load_scene(&book::descriptor); //scene_state.load_scene(&book::descriptor);
node_eye = scene_state.find_node_by_name("Camera"); //node_eye = scene_state.find_node_by_name("Camera");
assert(node_eye != nullptr); //assert(node_eye != nullptr);
//view::state.eye = XMVector3Transform(XMVectorZero(), node_eye->world); //view::state.eye = XMVector3Transform(XMVectorZero(), node_eye->world);
//node_at = scene_state.find_node_by_name("Camera001.Target"); //node_at = scene_state.find_node_by_name("Camera001.Target");
@ -349,7 +353,7 @@ void update(float time)
{ {
current_time = time; current_time = time;
scene_state.update(time); //scene_state.update(time);
/* /*
view::state.eye = XMVector3Transform(XMVectorZero(), node_eye->world); view::state.eye = XMVector3Transform(XMVectorZero(), node_eye->world);
if (node_at == nullptr) if (node_at == nullptr)

View File

@ -17,14 +17,16 @@ namespace world {
}; };
descriptor const descriptors[] = { descriptor const descriptors[] = {
[world_id::GRANDLECTURN] = { //[world_id::GRANDLECTURN] =
{
.region_count = 4, .region_count = 4,
.vertex_paths = grandlecturn_vertex_paths, .vertex_paths = grandlecturn_vertex_paths,
.entry_table_path = "minecraft/grandlecturn/global.dump", .entry_table_path = "minecraft/grandlecturn/global.dump",
.lights_path = "minecraft/grandlecturn/global.lights.vtx", .lights_path = "minecraft/grandlecturn/global.lights.vtx",
.hash_func = grandlecturn_hash, .hash_func = grandlecturn_hash,
}, },
[world_id::MIDNIGHTMEADOW] = { //[world_id::MIDNIGHTMEADOW] =
{
.region_count = 4, .region_count = 4,
.vertex_paths = midnightmeadow_vertex_paths, .vertex_paths = midnightmeadow_vertex_paths,
.entry_table_path = "minecraft/midnightmeadow/global.dump", .entry_table_path = "minecraft/midnightmeadow/global.dump",