svg support; reworking of sounds; steam integration and building

This commit is contained in:
John Alanbrook 2023-11-20 21:57:23 +00:00
parent ec8b3c97c4
commit 051d8653fb
25 changed files with 4809 additions and 132 deletions

View file

@ -5,6 +5,12 @@ MAKEDIR != pwd
# Options
# DBG --- build with debugging symbols and logging
CXX:=$(CC)
ifeq ($(CXX), tcc)
CXX=clang
endif
ifeq ($(CC),cc)
CC=clang
endif
@ -36,6 +42,14 @@ ifdef NMP3
CFLAGS += -DNMP3
endif
ifdef NSVG
CFLAGS += -DNSVG
endif
ifdef NQOA
CFLAGS += -DNQOA
endif
ifeq ($(DBG),1)
CFLAGS += -g
INFO += _dbg
@ -66,7 +80,6 @@ endif
CFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DTINYSPLINE_FLOAT_PRECISION -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) -MD $(WARNING_FLAGS) -I. -DVER=\"$(VER)\" -DINFO=\"$(INFO)\"
PKGCMD = tar --directory $(BIN) --exclude="./*.a" --exclude="./obj" -czf $(DISTDIR)/$(DIST) .
ZIP = .tar.gz
UNZIP = cp $(DISTDIR)/$(DIST) $(DESTDIR) && tar xzf $(DESTDIR)/$(DIST) -C $(DESTDIR) && rm $(DESTDIR)/$(DIST)
@ -75,6 +88,8 @@ ifeq ($(ARCH),)
ARCH != uname -m
endif
STEAMPATH = steam/sdk/redistributable_bin
ifeq ($(OS), Windows_NT)
LDFLAGS += -mwin32 -static -g
CFLAGS += -mwin32 -g
@ -85,6 +100,11 @@ ifeq ($(OS), Windows_NT)
ZIP = .zip
UNZIP = unzip -o -q $(DISTDIR)/$(DIST) -d $(DESTDIR)
ifdef STEAM
LDPATHS += $(STEAMPATH)/win64
LDLIBS += steam_api64
endif
else ifeq ($(OS), ios)
TTARGET = arm64-apple-ios13.1
SYSRT := /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
@ -99,30 +119,43 @@ else ifeq ($(CC), emcc)
CC = emcc
EXT = .html
else
else
UNAME != uname -s
ifeq ($(UNAME), Linux)
OS := Linux
LDFLAGS += -pthread -rdynamic
LDLIBS += GL pthread c m dl X11 Xi Xcursor EGL asound
ifdef STEAM
LDLIBS += steam_api
LDPATHS += $(STEAMPATH)/linux64
endif
endif
ifeq ($(UNAME), Darwin)
OS := macos
CFLAGS += -arch $(ARCH) -x objective-c
LDFLAGS += -arch $(ARCH) -framework Cocoa -framework QuartzCore -framework AudioToolbox -framework Metal -framework MetalKit
ifdef STEAM
LDPATHS += $(STEAMPATH)/osx
LDLIBS += steam_api
endif
endif
endif
BIN = bin/$(OS)/$(ARCH)$(INFO)
ifdef STEAM
BIN := $(addsuffix /steam, $(BIN))
endif
OBJDIR = $(BIN)/obj
# All other sources
OBJS != find source/engine -type f -name '*.c'
#OBJS += $(shell find source/engine -type f -name '*.cpp')
OBJS += $(shell find source/engine -type f -name '*.cpp')
OBJS += $(shell find source/engine -type f -name '*.m')
#OBJS := $(patsubst %.cpp, %.o, $(OBJS))
OBJS := $(patsubst %.cpp, %.o, $(OBJS))
OBJS := $(patsubst %.c, %.o,$(OBJS))
OBJS := $(patsubst %.m, %.o, $(OBJS))
OBJS := $(addprefix $(BIN)/obj/, $(OBJS))
@ -134,10 +167,9 @@ includeflag := $(addprefix -I, $(includeflag))
# Adding different SDKs
ifdef STEAM
LDLIBS += steam_api
LDPATHS += steam/sdk/redistributable_bin/osx
includeflag += -Isteam/sdk/public
CFLAGS += -DSTEAM
# BIN += /steam
endif
WARNING_FLAGS = -Wno-incompatible-function-pointer-types -Wno-incompatible-pointer-types -Wno-unused-function -Wno-unused-const-variable -Wno-address-of-temporary
@ -229,8 +261,8 @@ $(OBJDIR)/%.o: %.c
$(OBJDIR)/%.o: %.cpp
@mkdir -p $(@D)
@echo Making C++ object $@
@$(CC) $(CFLAGS) -c $< -o $@
@echo Making C++ object $@ with $(CXX)
@$(CXX) $(CFLAGS) -c $< -o $@
$(OBJDIR)/%.o: %.m
@mkdir -p $(@D)

38
docs/building.md Normal file
View file

@ -0,0 +1,38 @@
# Building Primum
Building is done via a basic makefile with flags. Type "make" to build for the platform you are on. Targets include:
|crossmac|On an ARM mac, build a bundled intel and arm mac version|
|crosswin|On any POSIX computer with a GCC win32 toolchain, build for windows platforms|
|clean|clean up|
|tags|build tags file|
|cdb|build the cdb tool to inspect primum pak files|
|shaders|rebuild all shader headers|
|packer|tool for building primum pak files|
|input.md|editor input documentation|
|api.md|scripting API|
|switch|build for the nintendo switch|
|ps5|build for the ps5|
|playdate|build for the playdate|
Basic boolean flags, set to 0 or 1, to enable or disable features in the build
|DBG|debugging info in binary|
|NFLAC|true to not build flac ability|
|NMP3|true to not build mp3|
|NSVG|true to build without SVG|
|NEDITOR|true to not include editor|
|STEAM|true to build steam support|
|EGS|true to build with EGS support|
|GOG|true to build with GOG support|
## OPT
-0 No optimization
-1 Full optimization
-small Optimize for smallest binary
## Building for Steam
-Get the steam SDK
-Unpack it into a folder named 'steam' at the top level directory
-Make with STEAM=1
Steam uses a C++ based SDK, so a C-only compiler like TCC will not work.

5
docs/entities.md Normal file
View file

@ -0,0 +1,5 @@
# Entities
Entities are defined by creating a .jso script in your game directory. Variants of the original entity can be created by defining a .json file, typically done via the provided editor. The original entity is known as its ur-type. If you create a player.jso file in your game directory, a player can be spawned by saying Primum.spawn(ur.player).
An entity which differs from its ur will have an asterisk * next to its name.

19
docs/gui.md Normal file
View file

@ -0,0 +1,19 @@
# GUI & Drawing
Register functions with built-in drawing callbacks to have code execute at particular points in time. Some drawing functions take world coordinates, while others take screen coordinates.
register_gui
Called before nk_gui. Screen space.
register_nk_gui
Called during the Nuklear debug pass. Use Nuklear functions only.
register_draw
Called every frame. World space.
register_debug
Called if drawing physics. World space.
# Mum
Mum is the GUI compositing system here. When drawing a mum, the bottom left corner is [0,0]. Change the anchor property of any Mum to change where it is drawn.

View file

@ -28,10 +28,6 @@ ifeq ($(shell uname -s),Darwin)
endif
endif
$(info $$CFLAGS is [${CFLAGS}])
$(info HOST_CC is $(HOST_CC))
$(info shell is $(shell uname -s))
# Windows cross compilation from Linux
#CONFIG_WIN32=y
# use link time optimization (smaller and faster executables but slower build)

20
scripts/achievements.js Normal file
View file

@ -0,0 +1,20 @@
var achievement = {
api: "",
name: "",
description: "",
hidden: false,
icon: "",
licon: "",
max: 1,
};
achievement.doc = {
doc: "Generic achivement.",
api: "String used to access the achievement via APIs.",
name: "Displayed name of the achievement.",
description: "Description of the achievement.",
hidden: "True if the player shouldn't see this achievement.",
icon: "Path to an unlocked icon.",
licon: "Path to icon for not achieved.",
max: "Value needed to reach to unlock the achievement."
};

View file

@ -216,6 +216,16 @@ var editor = {
load("debug.js");
},
start_play() {
Primum.clear();
load("config.js");
Game.play();
Player.players[0].uncontrol(this);
Player.players[0].control(limited_editor);
Register.unregister_obj(this);
load("game.js");
},
enter_editor() {
Game.pause();
Player.players[0].control(this);
@ -831,6 +841,13 @@ editor.inputs.f5 = function()
editor.inputs.f5.doc = "Start game from 'debug' if it exists; otherwise, from 'game'.";
editor.inputs.f6 = function()
{
editor.start_play();
}
editor.inputs.f6.doc = "Start game as if the player started it.";
editor.inputs['M-p'] = function() {
if (Game.playing())
Game.pause();
@ -2041,7 +2058,6 @@ limited_editor.inputs['M-p'] = function()
limited_editor.inputs['C-q'] = function()
{
Sound.killall();
Primum.clear();
load("editorconfig.js");
load("dbgret.js");

View file

@ -467,7 +467,7 @@ var gameobject = {
kill() {
if (this.body === -1) {
Log.warn(`Object is already dead!`);
// Log.warn(`Object is already dead!`);
return;
}
@ -518,11 +518,9 @@ var gameobject = {
var obj = Object.create(this);
obj.make = undefined;
obj.level = level;
// obj.toJSON = obj.transform_obj;
if (this.instances)
this.instances.push(obj);
// Log.warn(`Made an object from ${this.toString()}`);
// Log.warn(this.instances.length);
obj.body = make_gameobject();
obj.components = {};
obj.objects = {};

View file

@ -2,24 +2,6 @@ var Audio = {
};
var Music = {
play(path) {
Log.info("Playing " + path);
cmd(87,path);
},
stop() {
cmd(89);
},
pause() {
cmd(88);
},
set volume(x) {
},
};
var Sound = {
sounds: [], /* array of loaded sound files */
play(file) {
@ -50,17 +32,8 @@ var Sound = {
/* Between 0 and 100 */
set volume(x) { cmd(19, x); },
get volume() { 0; },
killall() {
Music.stop();
this.musicstop();
/* TODO: Kill all sound effects that may still be running */
},
};
Sound.play.doc = "Play the given file once.";
Sound.music.doc = "Play a midi track with a given soundfont.";
Sound.musicstop.doc = "Stop playing music.";
Sound.doc = {};
Sound.doc.volume = "Set the master volume. 0 is no sound and 100 is loudest.";
Sound.killall.doc = "Kill any playing sounds and music in the game world.";

View file

@ -35,3 +35,7 @@
#define PL_MPEG_IMPLEMENTATION
#include <pl_mpeg.h>
#define NANOSVG_IMPLEMENTATION
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg.h"
#include "nanosvgrast.h"

View file

@ -546,7 +546,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 14:
str = JS_ToCString(js, argv[1]);
ret = ptr2js(play_sound(make_sound(str)));
//ret = ptr2js(play_sound(make_sound(str)));
play_oneshot(make_sound(str));
break;
case 15:

3098
source/engine/nanosvg.h Normal file

File diff suppressed because it is too large Load diff

1459
source/engine/nanosvgrast.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -161,6 +161,7 @@ struct dsp_filter dsp_filter(void *data, void (*filter)(void *data, soundbyte *o
new.data = data;
new.filter = filter;
new.inputs = 0;
new.bus = NULL;
return new;
}

View file

@ -20,6 +20,7 @@ struct dsp_filter {
int inputs;
struct dsp_filter *in[6];
struct bus *bus;
soundbyte cache[CHANNELS*BUF_FRAMES];
int dirty;

View file

@ -36,6 +36,21 @@ void mixer_init() {
bus[BUS_N-1].next = -1;
}
void filter_to_bus(struct dsp_filter *f)
{
struct bus *b = first_free_bus(*f);
if (b)
f->bus = b;
}
void unplug_filter(struct dsp_filter *f)
{
if (!f->bus) return;
bus_free(f->bus);
f->bus = NULL;
}
struct bus *first_free_bus(struct dsp_filter in) {
for (int i = 0; i < 255; i++)
if (!bus[i].on) {

View file

@ -19,6 +19,8 @@ extern soundbyte *mastermix;
void mixer_init();
struct bus *first_free_bus(struct dsp_filter in);
void filter_to_bus(struct dsp_filter *f);
void unplug_filter(struct dsp_filter *f);
void bus_fill_buffers(soundbyte *master, int n);
/* Set volume between 0 and 100% */

View file

@ -12,6 +12,7 @@
#define TSF_BLOCK 32
struct dsp_midi_song gsong;
struct dsp_filter songfil;
float music_pan = 0.f;
@ -52,8 +53,6 @@ void dsp_midi_fillbuf(struct dsp_midi_song *song, void *out, int n)
}
song->midi = midi;
// dsp_pan(&music_pan, out, n);
}
struct bus *musicbus;
@ -88,43 +87,12 @@ void play_song(const char *midi, const char *sf)
// Preset on 10th MIDI channel to use percussion sound bank if possible
tsf_channel_set_bank_preset(gsong.sf, 9, 128, 0);
struct dsp_filter cursong;
cursong.data = &gsong;
cursong.filter = dsp_midi_fillbuf;
musicbus = first_free_bus(cursong);
}
void music_play()
{
songfil.data = &gsong;
songfil.filter = dsp_midi_fillbuf;
filter_to_bus(&songfil);
}
void music_stop()
{
bus_free(musicbus);
}
void music_volume()
{
}
void music_resume()
{
}
void music_paused()
{
}
void music_pause()
{
}
void sound_play()
{
unplug_filter(&songfil);
}

View file

@ -11,10 +11,7 @@ struct dsp_midi_song {
tml_message *midi;
};
extern float music_pan;
void play_song(const char *midi, const char *sf);
void music_stop();
void dsp_midi_fillbuf(struct dsp_midi_song *song, void *out, int n);
#endif

View file

@ -31,7 +31,6 @@
#include "dr_wav.h"
#ifndef NFLAC
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO
#include "dr_flac.h"
@ -43,9 +42,11 @@
#include "dr_mp3.h"
#endif
#ifndef NQOA
#define QOA_NO_STDIO
#define QOA_IMPLEMENTATION
#include "qoa.h"
#endif
static struct {
char *key;
@ -139,7 +140,8 @@ void sound_init() {
struct wav *make_sound(const char *wav) {
int index = shgeti(wavhash, wav);
if (index != -1) return wavhash[index].value;
if (index != -1)
return wavhash[index].value;
char *ext = strrchr(wav, '.')+1;
@ -158,19 +160,26 @@ struct wav *make_sound(const char *wav) {
if (!strcmp(ext, "wav"))
mwav.data = drwav_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav.ch, &mwav.samplerate, &mwav.frames, NULL);
#ifndef NFLAC
else if (!strcmp(ext, "flac"))
else if (!strcmp(ext, "flac")) {
#ifndef NFLAC
mwav.data = drflac_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav.ch, &mwav.samplerate, &mwav.frames, NULL);
#else
YughWarn("Could not load %s because Primum was built without FLAC support.", wav);
#endif
#ifndef NMP3
}
else if (!strcmp(ext, "mp3")) {
#ifndef NMP3
drmp3_config cnf;
mwav.data = drmp3_open_memory_and_read_pcm_frames_f32(raw, rawlen, &cnf, &mwav.frames, NULL);
mwav.ch = cnf.channels;
mwav.samplerate = cnf.sampleRate;
}
#else
YughWarn("Could not load %s because Primum was built without MP3 support.", wav);
#endif
}
else if (!strcmp(ext, "qoa")) {
#ifndef NQOA
qoa_desc qoa;
short *qoa_data = qoa_decode(raw, rawlen, &qoa);
mwav.ch = qoa.channels;
@ -179,8 +188,11 @@ struct wav *make_sound(const char *wav) {
mwav.data = malloc(sizeof(soundbyte) * mwav.frames * mwav.ch);
src_short_to_float_array(qoa_data, mwav.data, mwav.frames*mwav.ch);
free(qoa_data);
#else
YughWarn("Could not load %s because Primum was built without QOA support.", wav);
#endif
} else {
YughWarn("Cannot process file type '%s'.", ext);
YughWarn("File with unknown type '%s'.", wav);
free (raw);
return NULL;
}
@ -221,24 +233,24 @@ void kill_oneshot(struct sound *s) {
}
void play_oneshot(struct wav *wav) {
struct sound *new = malloc(sizeof(*new));
new->data = wav;
new->bus = first_free_bus(dsp_filter(new, sound_fillbuf));
new->playing = 1;
new->loop = 0;
new->frame = 0;
new->endcb = kill_oneshot;
struct sound *self = malloc(sizeof(*self));
self->data = wav;
self->bus = first_free_bus(dsp_filter(self, sound_fillbuf));
self->playing = 1;
self->loop = 0;
self->frame = 0;
self->endcb = kill_oneshot;
}
struct sound *play_sound(struct wav *wav) {
struct sound *new = calloc(1, sizeof(*new));
new->data = wav;
new->bus = first_free_bus(dsp_filter(new, sound_fillbuf));
new->playing = 1;
new->loop = 0;
new->frame = 0;
new->endcb = kill_oneshot;
return new;
struct sound *self = calloc(1, sizeof(*self));
self->data = wav;
self->bus = first_free_bus(dsp_filter(self, sound_fillbuf));
self->playing = 1;
self->loop = 0;
self->frame = 0;
self->endcb = kill_oneshot;
return self;
}
int sound_playing(const struct sound *s) {

View file

@ -5,20 +5,6 @@
typedef float soundbyte;
struct Mix_Chunk {
int i;
};
struct Mix_Music {
int i;
};
enum MUS {
MUS_STOP,
MUS_PLAY,
MUS_PAUSE
};
struct soundstream {
struct circbuf *buf;
};
@ -38,16 +24,16 @@ struct sound {
void (*endcb)(struct sound*);
};
/* Represents a sound file */
/* Represents a sound file source, fulled loaded*/
struct wav {
unsigned int ch;
unsigned int samplerate;
unsigned long long frames;
float gain; /* In dB */
soundbyte *data;
};
/* Represents a sound file stream */
struct mp3 {
};
@ -58,12 +44,6 @@ void audio_close();
void sound_fillbuf(struct sound *s, soundbyte *buf, int n);
void mini_sound(char *path);
void mini_master(float v);
void mini_music_play(char *path);
void mini_music_pause();
void mini_music_stop();
struct wav *make_sound(const char *wav);
void free_sound(const char *wav);
void wav_norm_gain(struct wav *w, double lv);

View file

@ -0,0 +1,9 @@
#include "steamffi.h"
#ifdef STEAM
#include "steam/steam_api_flat.h"
void steaminit()
{
SteamAPI_Init();
}
#endif

13
source/engine/steamffi.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef STEAMFFI
#define STEAMFFI
#ifdef __cplusplus
extern "C"{
#endif
void steaminit();
#ifdef __cplusplus
}
#endif
#endif

View file

@ -17,6 +17,10 @@
#define QOI_IMPLEMENTATION
#include "qoi.h"
#ifndef NSVG
#include "nanosvgrast.h"
#endif
struct glrect ST_UNIT = {0.f, 1.f, 0.f, 1.f};
static struct {
@ -111,6 +115,23 @@ struct Texture *texture_pullfromfile(const char *path) {
int *delays;
data = stbi_load_gif_from_memory(raw, rawlen, &delays, &tex->width, &tex->height, &tex->frames, &n, 4);
tex->height *= tex->frames;
} else if (!strcmp(ext, ".svg")) {
#ifndef NSVG
NSVGimage *svg = nsvgParse(raw, "px", 96);
struct NSVGrasterizer *rast = nsvgCreateRasterizer();
n=4;
tex->width=100;
tex->height=100;
float scale = tex->width/svg->width;
data = malloc(tex->width*tex->height*n);
nsvgRasterize(rast, svg, 0, 0, scale, data, tex->width, tex->height, tex->width*n);
free(svg);
free(rast);
#else
YughWarn("Primum was built without SVG capabilities.");
return;
#endif
} else {
data = stbi_load_from_memory(raw, rawlen, &tex->width, &tex->height, &n, 4);
}

View file

@ -33,8 +33,7 @@
#include <time.h>
#ifdef STEAM
#include "steam/steam_api_flat.h"
//#include "steam/steam_api.h"
#include "steamffi.h"
#endif
#include "string.h"
@ -349,7 +348,7 @@ int main(int argc, char **argv) {
#endif
#ifdef STEAM
SteamAPI_Init();
steaminit();
#endif
stm_setup(); /* time */