Compare commits

..

48 commits

Author SHA1 Message Date
John Alanbrook 7e04937c8b ld55 fixes 2024-04-14 14:53:41 -05:00
John Alanbrook c05cb3c6d2 Change physics collisions and masks 2024-04-12 13:53:00 -05:00
John Alanbrook 17ad44e5a5 Physics fixes; editor fixes 2024-04-11 17:17:49 -05:00
John Alanbrook 0b4231c0b6 Add ur chaining 2024-04-11 07:38:28 -05:00
John Alanbrook 33ac36ae5c Fix render bug 2024-04-10 16:21:46 -05:00
John Alanbrook 941106ced5 fonts managed by javascript 2024-04-09 16:48:15 -05:00
John Alanbrook 5051d11005 Fix scaling and rotating controls 2024-04-09 08:01:54 -05:00
John Alanbrook 53f2addeec Init undefined variables in function definitions 2024-04-07 13:16:54 -05:00
John Alanbrook 33d450189c Physics callbacks called without registering 2024-04-06 19:41:14 -05:00
John Alanbrook 9106ebcc48 Fixed sound effects; fix invisible chars 2024-04-05 23:03:10 -05:00
John Alanbrook 403771d7f8 many fixes to editor 2024-04-04 17:28:11 -05:00
John Alanbrook 32268fc25d editor fix 2024-04-03 17:17:32 -05:00
John Alanbrook ccdcfafdf2 Reduce exports from input.js 2024-04-03 08:37:29 -05:00
John Alanbrook 033b7c5109 Add guid; tags; physics point checking 2024-04-03 00:44:08 -05:00
John Alanbrook 3f73a808d8 Fix animations from not deleting gameobjects 2024-04-02 07:41:46 -05:00
John Alanbrook 32333c32ad make more like misty 2024-04-01 17:58:29 -05:00
John Alanbrook 3b9a932c85 Add back in ai and tween, and fix 2024-04-01 08:13:57 -05:00
John Alanbrook 1195e6c73d makefile doesn't rebuild everything on one script change 2024-03-29 12:38:14 -05:00
John Alanbrook 59ad64bbba Replace tinycdb with miniz; remove unnecessary files; use sokol_main 2024-03-28 17:40:14 -05:00
John Alanbrook c27e1a3071 Update tinycdb to 0.81 2024-03-27 17:54:58 -05:00
John Alanbrook fe3250752d Make render path simpler 2024-03-27 15:00:59 -05:00
John Alanbrook 204aa19c50 Add phys2d raycasting 2024-03-26 18:31:20 -05:00
John Alanbrook c3aa598300 Move jsffi defines to header 2024-03-26 14:57:35 -05:00
John Alanbrook fa5398474b Add steam quickjs 2024-03-26 13:05:25 -05:00
John Alanbrook 459ef00330 Fix sprite animation crash; move text/image to render 2024-03-26 07:53:36 -05:00
John Alanbrook b17e5d3917 Merge branch 'origin/test' 2024-03-24 12:44:38 -05:00
John Alanbrook 3023a12184 Add system command; correct makefile 2024-03-24 12:44:35 -05:00
John Alanbrook 79c06098b3 Add windows icon and resource files to build 2024-03-24 10:32:51 -05:00
John Alanbrook ecb2882e7e fix makefile; add ios 2024-03-23 09:56:38 -05:00
John Alanbrook 5e3fb73398 move infos to spam 2024-03-22 13:40:56 -05:00
John Alanbrook 845fc5d4b8 Clean up render; fix delay kills 2024-03-22 09:02:10 -05:00
John Alanbrook 1540da8392 Move texture caching/handling to javascript 2024-03-21 21:07:24 -05:00
John Alanbrook f5d610cbed 'use' now how to load extra programs; cached 2024-03-21 11:33:36 -05:00
John Alanbrook 51f3b5abeb removed ffi stop [memleak] 2024-03-20 17:12:36 -05:00
John Alanbrook 5a52afc2fd simplify camera 2024-03-20 16:48:03 -05:00
John Alanbrook d3db5ca61e unseparated body from gameobject; now 'entity' 2024-03-20 16:07:23 -05:00
John Alanbrook 23afa7b266 macro compression 2024-03-20 14:32:35 -05:00
John Alanbrook 7e6fc1ffbe Moved all component ffi into global objects 2024-03-20 09:04:28 -05:00
John Alanbrook 046b7c6f44 Made debug object consistent 2024-03-19 23:01:31 -05:00
John Alanbrook 18c07e9f82 actor delays; fix sound playing 2024-03-19 17:00:49 -05:00
John Alanbrook 62a34c1e7c Remove generic cmd from jsffi 2024-03-19 14:39:19 -05:00
John Alanbrook 9730f8defb --amend 2024-03-19 09:06:28 -05:00
John Alanbrook 261b373a75 refactor jsffi 2024-03-18 12:38:32 -05:00
John Alanbrook 088dd4d4fd Move main process into javascript 2024-03-15 13:04:56 -05:00
John Alanbrook 32b0cc7377 quickjs log to its own file; cleanup script.c 2024-03-15 10:51:04 -05:00
John Alanbrook bad225b965 add dump option for qjs, clean up makefile 2024-03-14 16:48:05 -05:00
John Alanbrook 188fe7526c Move console and time to c; consoleout and colors 2024-03-14 14:10:06 -05:00
John Alanbrook 55ab159a85 Add testing harness 2024-03-14 10:21:44 -05:00
663 changed files with 13638 additions and 205506 deletions

14
.gitignore vendored
View file

@ -12,13 +12,21 @@ Jenkinsfile
*.gz
*.tar
.nova/
packer
packer*
primum
sokol-shdc
sokol-shdc*
source/shaders/*.h
core.cdb
primum.exe
core.cdb.h
jsc
.DS_Store
primum.html
*.html
.vscode
primum*
Prosperon*
prosperon*
*.icns
game.cdb
icon.ico
steam/

16
Info.plist Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Prosperon</string>
<key>CFBundleIdentifier</key>
<string>pockle.world.prosperon</string>
<key>CFBundleName</key>
<string>Prosperon</string>
<key>CFBundleVersion</key>
<string>0.5</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024 Pockle World. All rights reserved.</string>
</dict>
</plist>

323
Makefile
View file

@ -1,30 +1,25 @@
MAKEFLAGS = --jobs=4
MAKEFLAGS = --jobs=8
UNAME != uname
MAKEDIR != pwd
# Options
# DBG --- build with debugging symbols and logging
# NDEBUG --- build with debugging symbols and logging
CXX:=$(CC)
ifeq ($(CXX), tcc)
CXX=clang
endif
ifeq ($(CC),cc)
CC=clang
endif
# Temp to strip long emcc paths to just emcc
CC := $(notdir $(CC))
DBG ?= 1
OPT ?= 0
LEAK ?= 0
INFO :=
LD = $(CC)
ifeq ($(CC), x86_64-w64-mingw32-gcc)
AR = x86_64-w64-mingw32-ar
STEAM = steam/sdk
STEAMAPI = steam_api
CCC != $(CC) -v
ifneq ($(findstring clangcc , $(CCC)),)
LDFLAGS += -Wl,-rpath=./
endif
ifdef NEDITOR
@ -47,56 +42,49 @@ ifdef NQOA
CPPFLAGS += -DNQOA
endif
CPPFLAGS += -ffast-math
ifeq ($(CC), emcc)
LDFLAGS += #--closure 1
LDFLAGS += #--closure 1 --emrun
CPPFLAGS += -O0
OPT = 0
DBG = 0
NDEBUG = 1
AR = emar
endif
ifeq ($(DBG),1)
CPPFLAGS += -g
INFO += _dbg
else
ifdef NDEBUG
CPPFLAGS += -DNDEBUG
LDFLAGS += -s
else
CPPFLAGS += -g -DDUMP
INFO :=$(INFO)_dbg
endif
ifeq ($(LEAK),1)
CPPFLAGS += -fsanitize=address
ifdef LEAK
CPPFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -DLEAK
endif
CPPFLAGS += -DLEAK=$(LEAK)
ifeq ($(OPT),small)
CPPFLAGS += -Oz -flto -fno-ident -fno-asynchronous-unwind-tables
CPPFLAGS += -Oz -flto -fno-ident -fno-asynchronous-unwind-tables -ffunction-sections -fdata-sections
LDFLAGS += -flto
ifeq ($(CC), emcc)
LDFLAGS += --closure 1
endif
INFO := $(addsuffix _small,$(INFO))
INFO :=$(INFO)_small
else ifeq ($(OPT), 1)
CPPFLAGS += -O2 -flto
INFO :=$(INFO)_opt
else
ifeq ($(OPT), 1)
CPPFLAGS += -O2 -flto
INFO := $(addsuffix _opt,$(INFO))
else
CPPFLAGS += -O0
endif
CPPFLAGS += -O2
endif
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) -MD $(WARNING_FLAGS) -I. -DVER=\"$(VER)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
CPPFLAGS += -D_FILE_OFFSET_BITS=64 # for tinycdb
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) -MD $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
CPPFLAGS += -DCONFIG_VERSION=\"2024-02-14\" -DCONFIG_BIGNUM #for quickjs
# ENABLE_SINC_[BEST|FAST|MEDIUM]_CONVERTER
# default, fast and medium available in game at runtime; best available in editor
PKGCMD = tar --directory $(BIN) --exclude="./*.a" --exclude="./obj" -czf $(DISTDIR)/$(DIST) .
PKGCMD = tar --directory --exclude="./*.a" --exclude="./obj" -czf $(DISTDIR)/$(DIST) .
ZIP = .tar.gz
UNZIP = cp $(DISTDIR)/$(DIST) $(DESTDIR) && tar xzf $(DESTDIR)/$(DIST) -C $(DESTDIR) && rm $(DESTDIR)/$(DIST)
@ -104,30 +92,31 @@ ifeq ($(ARCH),)
ARCH != uname -m
endif
STEAMPATH = steam/sdk/redistributable_bin
DISCORDPATH = discord/lib
INFO :=$(INFO)_$(ARCH)
ifdef DISCORD
LDPATHS += $(DISCORDPATH)/$(ARCH)
LDLIBS += discord_game_sdk
CPPFLAGS += -DDISCORD
endif
ifdef STEAM
LDLIBS += steam_api
LDPATHS += $(STEAMPATH)/$(ARCH)
endif
ifeq ($(OS), Windows_NT)
ifeq ($(OS), Windows_NT) # then WINDOWS
PLATFORM := win64
DEPS += resource.o
STEAMAPI := steam_api64
LDFLAGS += -mwin32 -static
CPPFLAGS += -mwin32
LDLIBS += mingw32 kernel32 d3d11 user32 shell32 dxgi gdi32 ws2_32 ole32 winmm setupapi m pthread
EXT = .exe
ARCH := x86_64
PKGCMD = cd $(BIN); zip -q -r $(MAKEDIR)/$(DISTDIR)/$(DIST) . -x \*.a ./obj/\*
PKGCMD = zip -q -r $(MAKEDIR)/$(DISTDIR)/$(DIST) . -x \*.a ./obj/\*
ZIP = .zip
UNZIP = unzip -o -q $(DISTDIR)/$(DIST) -d $(DESTDIR)
else ifeq ($(CC), emcc)
INFO :=$(INFO)_win
else ifeq ($(OS), IOS)
CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
SDK = iphoneos
SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/$(SDK).platform/Developer/SDKs/$(SDK).sdk
CFLAGS += -isysroot $(SDK_PATH) -miphoneos-version-min=13.0
LDFLAGS += -isysroot $(SDK_PATH) -miphoneos-version-min=13.0
LDFLAGS += -framework Foundation -framework UIKit -framework AudioToolbox -framework Metal -framework MetalKit -framework AVFoundation
CXXFLAGS += -std=c++11
CFLAGS += -x objective-c
INFO :=$(INFO)_ios
else ifeq ($(CC), emcc) # Then WEB
OS := Web
LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -pthread -sTOTAL_MEMORY=128MB
CPPFLAGS += -pthread
@ -135,213 +124,165 @@ else ifeq ($(CC), emcc)
EXT = .html
else
UNAME != uname -s
ifeq ($(UNAME), Linux)
ifeq ($(UNAME), Linux) # then LINUX
OS := Linux
PLATFORM := linux64
LDFLAGS += -pthread -rdynamic
LDLIBS += GL pthread c m dl X11 Xi Xcursor EGL asound
INFO :=$(INFO)_linux
endif
ifeq ($(UNAME), Darwin)
OS := macos
PLATFORM := osx
CPPFLAGS += -arch $(ARCH)
CFLAGS += -x objective-c
CXXFLAGS += -std=c++11
LDFLAGS += -framework Cocoa -framework QuartzCore -framework AudioToolbox -framework Metal -framework MetalKit
INFO :=$(INFO)_macos
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' | grep -vE 'test|tool|example|fuzz|main' | grep -vE 'quickjs'
CPPOBJS != find source/engine -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main'
OBJS != find source -type f -name '*.c' | grep -vE 'test|tool|example|fuzz|main' | grep -vE 'quickjs'
CPPOBJS != find source -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main'
OBJS += $(CPPOBJS)
OBJS += source/engine/yugine.c
OBJS += $(shell find source/engine -type f -name '*.m')
OBJS := $(patsubst %.cpp, %.o, $(OBJS))
OBJS := $(patsubst %.c, %.o,$(OBJS))
OBJS := $(patsubst %.m, %.o, $(OBJS))
OBJS := $(addprefix $(BIN)/obj/, $(OBJS))
QUICKJS := source/engine/thirdparty/quickjs
OBJS += $(addprefix $(QUICKJS)/, libregexp.c quickjs.c libunicode.c cutils.c libbf.c)
OBJS := $(patsubst %.cpp, %$(INFO).o, $(OBJS))
OBJS := $(patsubst %.c, %$(INFO).o,$(OBJS))
OBJS := $(patsubst %.m, %$(INFO).o, $(OBJS))
engineincs != find source/engine -maxdepth 1 -type d
includeflag != find source -type d -name include
includeflag += $(engineincs) source/engine/thirdparty/tinycdb source/shaders source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs
includeflag += $(engineincs) source/shaders source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs
includeflag += $(STEAM)/public
includeflag += source
includeflag := $(addprefix -I, $(includeflag))
# Adding different SDKs
ifdef STEAM
includeflag += -Isteam/sdk/public
CPPFLAGS += -DSTEAM
# BIN += /steam
endif
WARNING_FLAGS = -Wno-incompatible-function-pointer-types -Wno-incompatible-pointer-types
NAME = primum$(EXT)
SEM = 0.3.0
COM != fossil describe
VER = $(SEM)
APP = prosperon
NAME = $(APP)$(INFO)$(EXT)
SEM != git describe --tags --abbrev=0
COM != git rev-parse --short HEAD
LDLIBS += $(STEAMAPI)
LDLIBS := $(addprefix -l, $(LDLIBS))
LDPATHS := $(STEAM)/redistributable_bin/$(PLATFORM)
LDPATHS := $(addprefix -L, $(LDPATHS))
DEPENDS = $(OBJS:.o=.d)
-include $(DEPENDS)
DIST = yugine-$(OS)$(ARCH)$(INFO)-$(COM)$(ZIP)
DISTDIR = ./dist
.DEFAULT_GOAL := all
primum: all
all: $(BIN)/$(NAME)
cp $(BIN)/$(NAME) .
DESTDIR ?= ~/.bin
CDB = source/engine/thirdparty/tinycdb
all: $(NAME)
cp -f $(NAME) $(APP)$(EXT)
SHADERS = $(shell ls source/shaders/*.sglsl)
SHADERS := $(patsubst %.sglsl, %.sglsl.h, $(SHADERS))
install: $(BIN)/$(NAME)
cp -f $(BIN)/$(NAME) $(DESTDIR)
prereqs: $(SHADERS) source/engine/core.cdb.h
$(BIN)/$(NAME): $(BIN)/libengine.a $(BIN)/libquickjs.a
DESTDIR ?= ~/.bin
install: $(NAME)
@echo Copying to destination
cp -f $(NAME) $(DESTDIR)/$(APP)
$(NAME): $(OBJS) $(DEPS)
@echo Linking $(NAME)
$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L$(BIN) $(LDPATHS) $(LDLIBS) -o $@
$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@
@echo Finished build
$(DISTDIR)/$(DIST): $(BIN)/$(NAME)
@echo Creating distribution $(DIST)
@mkdir -p $(DISTDIR)
@$(PKGCMD)
$(BIN)/libengine.a: $(OBJS)
@$(AR) rcs $@ $(OBJS)
CDB_C != find $(CDB) -name *.c
CDB_O := $(patsubst %.c, %.o, $(CDB_C))
$(CDB)/libcdb.a:
rm -f $(CDB)/libcdb.a
make -C $(CDB) libcdb.a
tools/libcdb.a: $(CDB)/libcdb.a
cp $(CDB)/libcdb.a tools
DOCOS = Sound gameobject Game Window physics Profile Time Player Mouse IO Log ColorMap sprite SpriteAnim Render Geometry
DOCHTML := $(addsuffix .api.html, $(DOCOS))
DOCMD := $(addsuffix .api.md, $(DOCOS))
api.md: $(DOCMD)
@(echo "# API"; cat $^) > $@
@rm $^
INPUT = editor DebugControls component.sprite component.polygon2d component.edge2d component.circle2d
INPUTMD := $(addsuffix .input.md, $(INPUT))
input.md: $(INPUTMD)
@(echo "# Input"; cat $^) > $@
@rm $^
%.input.md: primum $(SCRIPTS)
@echo Printing controls for $*
@./primum -e $* > $@
%.api.md: primum $(SCRIPTS)
@echo Printing api for $*
@./primum -d $* > $@
QUICKJS := source/engine/thirdparty/quickjs
$(BIN)/libquickjs.a:
make -C $(QUICKJS) clean
make -C $(QUICKJS) SYSRT=$(SYSRT) TTARGET=$(TTARGET) ARCH=$(ARCH) DBG=$(DBG) OPT=$(OPT) AR=$(AR) OS=$(OS) libquickjs.a HOST_CC=$(CC) LEAK=$(LEAK)
@mkdir -p $(BIN)
cp -rf $(QUICKJS)/libquickjs.* $(BIN)
$(OBJDIR)/%.o: %.c source/engine/core.cdb.h $(SHADERS)
@mkdir -p $(@D)
%$(INFO).o: %.c
@echo Making C object $@
@$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(OBJDIR)/%.o: %.cpp
@mkdir -p $(@D)
%$(INFO).o: %.cpp
@echo Making C++ object $@
@$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -fpermissive -c $< -o $@
$(OBJDIR)/%.o: %.m
@mkdir -p $(@D)
%$(INFO).o: %.m
@echo Making Objective-C object $@
@$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
shaders: $(SHADERS)
@echo Making shaders
%.sglsl.h:%.sglsl
@echo Creating shader $^
@./sokol-shdc --ifdef -i $^ --slang=glsl330:hlsl5:metal_macos:metal_ios:metal_sim:glsl300es -o $@
cdb: tools/cdb.c tools/libcdb.a
cc $^ -I$(CDB) -o cdb
source/engine/core.cdb.h: core.cdb
xxd -i $< > $@
./sokol-shdc --ifdef -i $^ --slang=glsl330:hlsl5:metal_macos:metal_ios:metal_sim:glsl300es -o $@
SCRIPTS := $(shell ls scripts/*.js*)
SCRIPT_O := $(addsuffix o, $(SCRIPTS))
CORE != (ls icons/* fonts/*)
CORE := $(CORE) $(SCRIPTS)
core.cdb: packer $(CORE)
./packer $(CORE)
chmod 644 out.cdb
mv out.cdb core.cdb
packer$(EXT): tools/packer.c source/engine/miniz.c
@echo Making packer
$(CC) -O2 $^ -Isource/engine -o packer
packer: tools/packer.c tools/libcdb.a
cc $^ -I$(CDB) -o packer
core.cdb: packer$(EXT) $(CORE)
@echo Packing core.cdb
./packer$(EXT) $@ $(CORE)
jsc: tools/jso.c tools/libquickjs.a
$(CC) $^ -lm -Iquickjs -o $@
source/engine/core.cdb.h: core.cdb
@echo Making $@
xxd -i $< > $@
tools/libquickjs.a: $(BIN)/libquickjs.a
cp -f $(BIN)/libquickjs.a tools
ICNSIZE = 16 32 128 256 512 1024
ICNNAME := $(addsuffix .png, $(ICNSIZE))
ICON = icons/moon.gif
icon.ico: $(ICON)
for i in $(ICNSIZE); do convert $^ -thumbnail $${i}x$${i} $${i}.png; done
convert $(ICNNAME) icon.ico
rm $(ICNNAME)
WINCC = x86_64-w64-mingw32-gcc
#WINCC = i686-w64-mingw32-g++
.PHONY: crosswin
crosswin:
make packer
make CC=$(WINCC) OS=Windows_NT
resource.o: resource.rc resource.manifest icon.ico
windres -i $< -o $@
crossmac:
make ARCH=arm64
mv primum primum_arm64
make ARCH=x86_64
mv primum primum_x86_64
lipo primum_arm64 primum_x86_64 -create -output primum
crossios:
make OS=IOS ARCH=arm64 DEBUG=$(DEBUG) OPT=$(OPT)
Prosperon.icns: $(ICON)
mkdir -p Prosperon.iconset
for i in $(ICNSIZE); do magick $^ -size $${i}x$${i} Prosperon.iconset/icon_$${i}x$${i}.png; done
iconutil -c icns Prosperon.iconset
crossmac: Prosperon.icns
make ARCH=arm64 DEBUG=$(DEBUG) OPT=$(OPT)
mv $(APP) mac_arm64
make ARCH=x86_64 DEBUG=$(DEBUG) OPT=$(OPT)
mv $(APP) mac_x86_64
lipo mac_arm64 mac_x86_64 -create -output $(APP)_mac
rm mac_arm64 mac_x86_64
rm -rf Prosperon.app
mkdir Prosperon.app
mkdir Prosperon.app/Contents
mkdir Prosperon.app/Contents/MacOS
mkdir Prosperon.app/Contents/Resources
mv $(NAME) Prosperon.app/Contents/MacOS/Prosperon
cp Info.plist Prosperon.app/Contents
cp Prosperon.icns Prosperon.app/Contents/Resources
crossweb:
make packer
make CC=emcc
playweb:
make crossweb
emrun $(NAME).html
clean:
@echo Cleaning project
rm -rf bin dist
rm -f source/shaders/*.h core.cdb jso cdb packer TAGS source/engine/core.cdb.h tools/libcdb.a $(CDB)/libcdb.a
rm -f $(CDB)/*.o
@make -C $(QUICKJS) clean
rm -f source/shaders/*.h core.cdb jso cdb packer TAGS source/engine/core.cdb.h tools/libcdb.a $(APP)* *.icns *.ico
find . -type f -name "*.[oad]" -delete
rm -rf Prosperon.app
docs: doc/prosperon.org
make -C doc
mv doc/html .
test:
@echo No tests yet ...
TAGINC != find . -name "*.[chj]"
tags: $(TAGINC)
@echo Making tags.

View file

@ -212,7 +212,18 @@ Components only work in the context of an entity. They have no meaning outside o
While components can be added via scripting, it is easier to add them via the editor, as we will later see.
*** Ur system
The ur[fn::A German prefix meaning primitive, original, or earliest.] system is a prototypical inheritence system used by the actor files. When actor files are loaded, they are stored as an ur. An *ur* represents a set of instructions to create the (text, config) needed to spawn an actor or entity.
The ur[fn::A German prefix meaning primitive, original, or earliest.] system is a prototypical inheritence system used by the actor files. When actor files are loaded, they are stored as an ur. An *ur* holds a list of (text, config) required to create an entity.
When prosperon starts, it searches for urs by name. Any file ending in ".jso" or ".json" will be interpereted as an ur, with same named jso and json being applied as (text, config) for an ur. A jso or json alone also constitute an ur.
An ur can also be defined by a json file. If an ur is found, it takes predecent over auto generated urs. The json of an ur looks like this:
| field | description |
|----|----|
| text | Path to a script file, or array of script files, to apply to the object |
| data | Path to a json file, or array of json files, to apply to the object |
Any ur file with this sort of json creates an ur which can be created in the game. A file named "box.ur" will be ingested and be available as "ur.box". When saving differences, it creates a json file with the same name as an ur (in this case, "box.json").
#+begin_scholium
Create an ur from the *hello* files above, and then spawn it.
@ -223,6 +234,14 @@ Primum.spawn(ur.hello);
When creating an actor from source files, all of its setup must take place. In this example, the setup happens during *ur.create*, and spawning is simply a matter of prototyping it.
#+end_scholium
This method allows high composability of game objects.
If an entity is created without an ur, is ur is defined as its given text and data. It cannot be saved. It must be given a new ur name.
Objects can be composed on the fly by stringing together urs. For example, a "2x.json" might define scale as 2x. Then, you can create a goblin with `ur.goblin`, or a large goblin with `ur.goblin.2x`. This creates a goblin object, and then applies the 2x scripts and jsons onto the object.
*** Urs in game
Each ur has the following fields.
| field | description |

17
resource.manifest Normal file
View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<copyright>Copyright (c) 2024 Pockle World. All Rights Reserved.</copyright>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
name="Prosperon"
version="0.4"
type="win32"
/>
<description>Prosperon Game Engine</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

16
resource.rc Normal file
View file

@ -0,0 +1,16 @@
101 ICON "icon.ico"
102 RT_MANIFEST "resource.manifest"
103 VERSIONINFO
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Pockle World"
VALUE "FileDescription", "Prosperon Game Engine"
VALUE "FileVersion", "0.4.0"
VALUE "LegalCopyright", "Copyright 2024 Pockle World."
VALUE "ProductVersion", "0.4.0"
END
END
END

View file

@ -1,11 +1,10 @@
var actor = {};
var a_db = {};
actor.spawn = function(script, config){
actor.spawn = function(script, config, callback){
if (typeof script !== 'string') return undefined;
if (!a_db[script]) a_db[script] = io.slurp(script);
console.info(`spawning actor with script ${script}`);
var padawan = Object.create(actor);
eval_env(a_db[script], padawan, script);
use(script, padawan);
if (typeof config === 'object')
Object.merge(padawan,config);
@ -13,7 +12,7 @@ actor.spawn = function(script, config){
padawan.padawans = [];
padawan.timers = [];
padawan.master = this;
Object.hide(padawan, "master","timers", "padawans");
Object.hide(padawan, "master", "timers", "padawans");
check_registers(padawan);
this.padawans.push(padawan);
return padawan;
@ -30,7 +29,7 @@ actor.timers = [];
actor.kill = function(){
if (this.__dead__) return;
this.timers.forEach(t => t());
Player.do_uncontrol(this);
input.do_uncontrol(this);
Event.rm_obj(this);
if (this.master) this.master.rm_pawn(this);
this.padawans.forEach(p => p.kill());
@ -42,10 +41,48 @@ actor.kill = function(){
actor.kill.doc = `Remove this actor and all its padawans from existence.`;
actor.interval = function(fn, seconds) {
var cur;
var stop = function() {
cur();
}
var f = function() {
fn.call(this);
cur = this.delay(f,seconds);
}
cur = this.delay(f,seconds);
return stop;
}
actor.delay = function(fn, seconds) {
var t = timer.delay(fn.bind(this), seconds);
this.timers.push(t);
return t;
var timers = this.timers;
var stop = function() {
timers.remove(stop);
rm();
}
function execute() {
fn();
stop();
}
stop.remain = seconds;
stop.seconds = seconds;
stop.pct = function() { return 1-(stop.remain / stop.seconds); };
function update(dt) {
stop.remain -= dt;
if (stop.remain <= 0)
execute();
}
var rm = Register.appupdate.register(update);
timers.push(stop);
return stop;
};
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
@ -53,10 +90,6 @@ actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
actor.padawans = [];
global.app = Object.create(actor);
app.die = function()
{
Game.quit();
}
app.die = function() { os.quit(); }
return {actor, app};

View file

@ -2,26 +2,28 @@ var ai = {
race(list) {
return function(dt) {
var good = false;
list.forEach(function(x) { if (x.call(this,dt)) good = true; }, this);
for (var i = 0; i < list.length; i++)
if (list[i].call(this,dt)) good=true;
return good;
};
},
do(times, list) {
},
sequence(list) {
var i = 0;
return function(dt) {
var fn = function(dt) {
while (i !== list.length) {
if (list[i].call(this,dt))
i++;
else
return false;
i++;
else
return false;
}
if (fn.done) fn.done();
return true;
};
fn.restart = function() { i = 0; };
return fn;
},
parallel(list) {
@ -32,30 +34,21 @@ var ai = {
};
},
moveto() {
return function(dt) {
var dir = this.randomloc.sub(this.pos);
if (Vector.length(dir) < 10) return true;
this.velocity = Vector.norm(this.randomloc.sub(this.pos)).scale(20);
return False;
}
dofor(secs, fn) {
return ai.race([
ai.wait(secs),
fn
]);
},
move() {
return function(dt) {
this.velocity = this.left().scale(20);
return false;
}
},
wait(secs) {
secs ??= 1;
wait(secs = 1) {
var accum = 0;
return function(dt) {
accum += dt;
if (accum >= secs)
if (accum >= secs) {
accum = 0;
return true;
}
return false;
};

View file

@ -8,6 +8,7 @@
In addition to the removal of a bunch of stuff as seen here.
Access prototypes through __proto__ instead of the long-winded Object.getProtoTypeOf.
*/
/*
Object.getPrototypeOf = undefined;
Object.setPrototypeOf = undefined;
@ -62,12 +63,12 @@ convert.buf2hex = function(buffer) { // buffer is an ArrayBuffer
}
/* Time values are always expressed in terms of real earth-seconds */
var time = {
get hour2minute() { return this.hour/this.minute; },
get day2hour() { return this.day/this.hour; },
get minute2second() { return this.minute/this.second; },
get week2day() { return this.week/this.day; },
};
Object.assign(time, {
hour2minute() { return this.hour/this.minute; },
day2hour() { return this.day/this.hour; },
minute2second() { return this.minute/this.second; },
week2day() { return this.week/this.day; },
});
time.strparse = {
yyyy: "year",
@ -106,8 +107,6 @@ time.doc = {
text: "Return a text formatted time."
};
time.second = 1;
time.minute = 60;
time.hour = 3_600;
@ -117,9 +116,6 @@ time.weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday
time.monthstr = ["January", "February", "March", 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
time.epoch = 1970;
time.now = function() { return cmd(210);}
time.computer_zone = function() { return cmd(211)/this.hour; }
time.computer_dst = function() { return cmd(212); }
time.isleap = function(year) { return this.yearsize(year) === 366; }
time.isleap.doc = "Return true if the given year is a leapyear.";
@ -129,14 +125,20 @@ time.yearsize = function(y) {
return 365;
}
time.timecode = function(t, fps = 24)
{
var s = Math.trunc(t);
t -= s;
return `${s}:${Math.trunc(fps*s)}`;
}
time.monthdays = [31,28,31,30,31,30,31,31,30,31,30,31];
time.zones = {};
time.zones['-12'] = 'IDLW';
time.record = function(num, zone)
time.record = function(num, zone = this.computer_zone())
{
if (typeof num === 'object') return num;
else if (typeof num === 'number') {
zone ??= this.computer_zone();
var monthdays = this.monthdays.slice();
var rec = {
second: 0,
@ -215,9 +217,9 @@ time.number = function(rec)
c += this.day*this.yearsize(i);
c += (this.yearsize(year)-yday-1)*this.day;
c += (this.day2hour-hour-1)*this.hour;
c += (this.hour2minute-minute-1)*this.minute;
c += (this.minute2second-second);
c += (this.day2hour()-hour-1)*this.hour;
c += (this.hour2minute()-minute-1)*this.minute;
c += (this.minute2second()-second);
c += zone*this.hour;
c *= -1;
return c;
@ -258,9 +260,8 @@ time.number = function(rec)
time.fmt = "vB mB d h:nn:ss TZz a y c";
/* If num is a number, converts to a rec first. */
time.text = function(num, fmt, zone)
time.text = function(num, fmt = this.fmt, zone)
{
fmt ??= this.fmt;
var rec = num;
if (typeof rec === 'number')
@ -312,37 +313,6 @@ time.text = function(num, fmt, zone)
return fmt;
}
var json = {};
json.encode = function(value, space, replacer, whitelist)
{
return JSON.stringify(value, space, replacer);
}
json.decode = function(text, reviver)
{
if (!text) return undefined;
return JSON.parse(text,reviver);
}
json.readout = function(obj)
{
var j = {};
for (var k in obj)
if (typeof obj[k] === 'function')
j[k] = 'function ' + obj[k].toString();
else
j[k] = obj[k];
return json.encode(j);
}
json.doc = {
doc: "json implementation.",
encode: "Encode a value to json.",
decode: "Decode a json string to a value.",
readout: "Encode an object fully, including function definitions."
};
Object.methods = function(o)
{
var m = [];
@ -353,9 +323,8 @@ Object.methods = function(o)
}
Object.methods.doc = "Retun an array of all functions an object has access to.";
Object.dig = function(obj, path, def)
Object.dig = function(obj, path, def = {})
{
def ??= {};
var pp = path.split('.');
for (var i = 0; i < pp.length-1; i++) {
obj = obj[pp[i]] = obj[pp[i]] || {};
@ -364,17 +333,6 @@ Object.dig = function(obj, path, def)
return def;
}
Object.samenewkeys = function(a,b)
{
b ??= a.__proto__;
var ret = {};
ret.same = [];
ret.unique = [];
Object.keys(a).forEach(key => (key in b) ? ret.same.push(key) : ret.unique.push(key));
return ret;
}
Object.samenewkeys.doc = "Return an object listing which keys are the same and unique on a compared to b.";
Object.rkeys = function(o)
{
var keys = [];
@ -386,6 +344,20 @@ Object.rkeys = function(o)
return keys;
}
Object.readonly = function(o, name, msg)
{
var tmp = {};
var prop = Object.getOwnPropertyDescriptor(o, name);
if (!prop) {
console.error(`Attempted to make property ${name} readonly, but it doesn't exist on ${o}.`);
return;
}
Object.defineProperty(tmp, name, prop);
prop.get = function() { return tmp[name]; }
prop.set = function() { console.warn(`Attempted to set readonly property ${name}`); }
Object.defineProperty(o, name, prop);
}
Object.extend = function(from)
{
var n = {};
@ -587,10 +559,8 @@ Object.defineProperty(Object.prototype, 'obscure', {
Object.defineProperty(Object.prototype, 'mixin', {
value: function(obj) {
if (typeof obj === 'string') {
var script = io.slurp(obj);
obj = eval_env(script, this, obj);
}
if (typeof obj === 'string')
obj = use(obj, this);
if (obj)
Object.mixin(this, obj);
@ -816,14 +786,6 @@ Object.defineProperty(String.prototype, 'fromlast', {
}
});
Object.defineProperty(String.prototype, 'tolast', {
value: function(val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return this.slice();
return this.slice(0,idx);
}
});
Object.defineProperty(String.prototype, 'tofirst', {
value: function(val) {
var idx = this.indexOf(val);
@ -852,13 +814,6 @@ Object.defineProperty(String.prototype, 'base', {
value: function() { return this.fromlast('/'); }
});
Object.defineProperty(String.prototype, 'dir', {
value: function() {
if (!this.includes('/')) return "";
return this.tolast('/');
}
});
Object.defineProperty(String.prototype, 'splice', {
value: function(index, str) {
return this.slice(0,index) + str + this.slice(index);
@ -866,10 +821,7 @@ Object.defineProperty(String.prototype, 'splice', {
});
Object.defineProperty(String.prototype, 'rm', {
value: function(index, endidx) {
endidx ??= index+1;
return this.slice(0,index) + this.slice(endidx);
}
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
});
Object.defineProperty(String.prototype, 'updir', {
@ -927,6 +879,11 @@ Object.defineProperty(String.prototype, 'uc', { value: function() { return this.
Object.defineProperty(String.prototype, 'lc', {value:function() { return this.toLowerCase(); }});
/* ARRAY DEFS */
Object.defineProperty(Array.prototype, 'aspect', {
value: function() {
return this.x/this.y;
}
});
Object.defineProperty(Array.prototype, 'copy', {
value: function() {
var c = [];
@ -941,14 +898,12 @@ Object.defineProperty(Array.prototype, 'copy', {
Object.defineProperty(Array.prototype, 'dofilter', {
value: function(fn) {
var j = 0;
this.forEach(function(val,i) {
if (fn(val)) {
if (i !== j) this[j] = val;
j++;
for (let i = 0; i < this.length; i++) {
if (!fn.call(this, this[i], i, this)) {
this.splice(i, 1);
i--;
}
}, this);
this.length = j;
}
return this;
}
});
@ -1138,13 +1093,19 @@ Object.defineProperty(Array.prototype, 'scale', {
return this.map(function(x) { return x*s; });
}});
Object.defineProperty(Array.prototype, 'sorted', {
value: function() {
return this.toSorted();
}
});
Object.defineProperty(Array.prototype, 'equal', {
value: function(b) {
if (this.length !== b.length) return false;
if (b == null) return false;
if (this === b) return true;
return JSON.stringify(this.sort()) === JSON.stringify(b.sort());
return JSON.stringify(this.sorted()) === JSON.stringify(b.sorted());
for (var i = 0; i < this.length; i++) {
if (!this[i] === b[i])
@ -1401,6 +1362,21 @@ Math.randomint = function(max) { return Math.clamp(Math.floor(Math.random() * ma
/* BOUNDINGBOXES */
var bbox = {};
bbox.overlap = function(box1, box2) {
return (
box1.l > box2.l &&
box1.r < box2.r &&
box1.t < box2.t &&
box1.b > box2.b
);
return (
box1.l > box2.r ||
box1.r < box2.l ||
box1.t > box2.b ||
box1.b < box2.t
);
}
bbox.fromcwh = function(c, wh) {
return {
t: c.y+(wh.y/2),
@ -1499,67 +1475,53 @@ bbox.fromobjs = function(objs)
};
/* VECTORS */
var Vector = {
length(v) {
var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0);
return Math.sqrt(sum);
},
var Vector = {};
Vector.length = function(v) {
var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0);
return Math.sqrt(sum);
}
Vector.norm = function(v) {
var len = Vector.length(v);
if (!len) return [0,0];
return [v.x/len, v.y/len];
}
Vector.project = function(a, b) { return vector.project(a,b); }
Vector.dot = function(a, b) { return vector.dot(a,b); },
Vector.random = function() {
var vec = [Math.random()-0.5, Math.random()-0.5];
return Vector.norm(vec);
}
norm(v) {
var len = Vector.length(v);
return [v.x/len, v.y/len];
},
Vector.angle = function(v) { return Math.rad2turn(Math.atan2(v.y, v.x)); }
Vector.rotate = function(v,angle) {
var r = Vector.length(v);
angle += Vector.angle(v);
angle = Math.turn2rad(angle);
return [r*Math.cos(angle), r*Math.sin(angle)];
}
project(a, b) {
return cmd(85, a, b);
},
Vector.equal = function(v1, v2, tol) {
if (!tol)
return v1.equal(v2);
dot(a, b) {
return cmd(88,a,b);
},
var eql = true;
var c = v1.sub(v2);
random() {
var vec = [Math.random()-0.5, Math.random()-0.5];
return Vector.norm(vec);
},
c.forEach(function(x) {
if (!eql) return;
if (Math.abs(x) > tol)
eql = false;
});
angle(v) {
return Math.rad2turn(Math.atan2(v.y, v.x));
},
return eql;
}
rotate(v,angle) {
var r = Vector.length(v);
angle += Vector.angle(v);
angle = Math.turn2rad(angle);
return [r*Math.cos(angle), r*Math.sin(angle)];
},
Vector.reflect = function(vec, plane) {
var p = Vector.norm(plane);
return vec.sub(p.scale(2*Vector.dot(vec, p)));
}
equal(v1, v2, tol) {
if (!tol)
return v1.equal(v2);
var eql = true;
var c = v1.sub(v2);
c.forEach(function(x) {
if (!eql) return;
if (Math.abs(x) > tol)
eql = false;
});
return eql;
},
reflect(vec, plane) {
var p = Vector.norm(plane);
return vec.sub(p.scale(2*Vector.dot(vec, p)));
},
reflect_point(vec, point) {
return point.add(vec.sub(point).scale(-1));
},
};
Vector.reflect_point = function(vec, point) { return point.add(vec.sub(point).scale(-1)); }
/* POINT ASSISTANCE */

View file

@ -1,29 +1,44 @@
this.phys = physics.kinematic;
this.dir_view2world = function(dir) { return dir.scale(this.realzoom()); };
this.view2world = function(pos) { return cmd(137,pos); };
this.world2view = function(pos) { return cmd(136,pos); };
this.realzoom = function() { return cmd(135); };
this.dir_view2world = function(dir) { return dir.scale(this.zoom); };
this.view2world = function(pos) {
var useren = window.rendersize.scale(this.zoom);
if (window.mode === window.modetypes.stretch) {
pos = pos.scale([window.rendersize.x/window.size.x, window.rendersize.y/window.size.y]);
pos = pos.sub(window.rendersize.scale(0.5));
pos = pos.scale(this.zoom);
pos = pos.add(this.pos);
}
if (window.mode === window.modetypes.full) {
pos = pos.sub(window.size.scale(0.5));
pos = pos.scale(this.zoom);
pos = pos.add(this.pos);
}
return pos;
};
this.world2view = function(pos) {
if (window.mode === window.modetypes.stretch) {
pos = pos.sub(this.pos);
pos = pos.scale(1.0/this.zoom);
pos = pos.add(window.rendersize.scale(0.5));
}
if (window.mode === window.modetypes.full) {
pos = pos.sub(this.pos);
pos = pos.scale(1/this.zoom);
pos = pos.add(window.size.scale(0.5));
}
return pos;
};
this.right = function()
{
return this.pos.x + (Window.rendersize.x/2);
}
this.screenright = function() { return this.view2world(window.size).x; }
this.screenleft = function() { return this.view2world([0,0]).x; }
this.left = function()
{
return this.pos.x - (Window.rendersize.x/2);
}
this.mixin({
get zoom() {
// var z = Game.native.y / Window.dimensions.y;
return cmd(135);///z;
},
var zoom = 1;
Object.mixin(self, {
set zoom(x) {
x = Math.clamp(x,0.1,10);
// var z = Game.native.y / Window.dimensions.y;
// z *= x;
cmd(62,x);
zoom = x;
if (zoom <= 0.1) zoom = 0.1;
},
});
get zoom() { return zoom; }
}
);

View file

@ -173,9 +173,8 @@ ColorMap.Viridis = ColorMap.makemap({
Color.normalize(ColorMap);
ColorMap.sample = function(t, map)
ColorMap.sample = function(t, map = this)
{
map ??= this;
if (t < 0) return map[0];
if (t > 1) return map[1];

View file

@ -22,10 +22,11 @@ var component = {
make(go) {
var nc = Object.create(this);
nc.gameobject = go;
Object.mixin(nc, this._enghook(go.body));
Object.mixin(nc, this._enghook(go, nc));
assign_impl(nc,this.impl);
Object.hide(nc, 'gameobject', 'id');
nc.post();
nc.make = undefined;
return nc;
},
@ -75,7 +76,8 @@ function json_from_whitelist(whitelist)
}
}
Object.mixin(cmd(268,true), {
Object.mixin(os.sprite(true), {
loop: true,
toJSON:json_from_whitelist([
"path",
"pos",
@ -88,36 +90,66 @@ Object.mixin(cmd(268,true), {
]),
anim:{},
playing: 0,
play(str) {
var sp = this;
str ??= 0;
var playing = this.anim[str];
if (!playing) return; //TODO: ERROR
play(str = 0) {
this.del_anim?.();
var self = this;
var stop;
self.del_anim = function() {
self.del_anim = undefined;
self = undefined;
advance = undefined;
stop?.();
}
var playing = self.anim[str];
if (!playing) return;
var f = 0;
self.path = playing.path;
function advance() {
sp.path = playing.path;
sp.frame = playing.frames[f].rect;
if (!self) return;
if (!self.gameobject) return;
//self.path = playing.path;
self.frame = playing.frames[f].rect;
f = (f+1)%playing.frames.length;
if (f === 0)
sp.anim_done?.();
sp.gameobject?.delay(advance, playing.frames[f].time);
if (f === 0) {
self.anim_done?.();
if (!self.loop) { self.stop(); return; }
}
stop = self.gameobject.delay(advance, playing.frames[f].time);
}
this.tex(game.texture(playing.path));
advance();
},
stop() {},
stop() {
this.del_anim?.();
},
set path(p) {
p = Resources.find_image(p);
if (!p) return;
if (!p) {
console.warn(`Could not find image ${p}.`);
return;
}
if (p === this.path) return;
this.tex = cmd(269,p);
this._p = p;
this.del_anim?.();
this.texture = game.texture(p);
this.tex(this.texture);
var anim = SpriteAnim.make(p);
if (!anim) return;
this.anim = anim;
this.play();
this.pos = this.dimensions().scale(this.anchor);
},
get path() {
return this.tex.path();
return this._p;
},
kill() {
this.del_anim?.();
this.anim = undefined;
this.gameobject = undefined;
this.anim_done = undefined;
},
toString() { return "sprite"; },
move(d) { this.pos = this.pos.add(d); },
@ -125,7 +157,7 @@ Object.mixin(cmd(268,true), {
this.scale = this.scale.scale(x);
this.pos = this.pos.scale(x);
},
anchor:[0,0],
sync() { },
pick() { return this; },
boundingbox() {
@ -136,7 +168,7 @@ Object.mixin(cmd(268,true), {
},
dimensions() {
var dim = [this.tex.width(), this.tex.height()];
var dim = [this.texture.width, this.texture.height];
dim.x *= this.frame.w;
dim.y *= this.frame.h;
return dim;
@ -145,28 +177,15 @@ Object.mixin(cmd(268,true), {
height() { return this.dimensions().y; },
});
cmd(268,true).make = function(go)
os.sprite(true).make = function(go)
{
var sp = cmd(268);
sp.go = go.body;
var sp = os.sprite();
sp.go = go;
sp.gameobject = go;
return sp;
}
component.sprite = cmd(268,true);
Object.freeze(sprite);
component.model = Object.copy(component, {
path:"",
_enghook: make_model,
});
component.model.impl = {
set path(x) { cmd(149, this.id, x); },
draw() { cmd(150, this.id); },
kill() { cmd(213, this.id); },
};
component.sprite = os.sprite(true);
var sprite = component.sprite;
@ -176,16 +195,34 @@ sprite.doc = {
pos: "The offset position of the sprite, relative to its entity."
};
sprite.setanchor = function(anch) {
var off = [0,0];
switch(anch) {
case "ll": break;
case "lm": off = [-0.5,0]; break;
case "lr": off = [-1,0]; break;
case "ml": off = [0,-0.5]; break;
case "mm": off = [-0.5,-0.5]; break;
case "mr": off = [-1,-0.5]; break;
case "ul": off = [0,-1]; break;
case "um": off = [-0.5,-1]; break;
case "ur": off = [-1,-1]; break;
}
this.anchor = off;
this.pos = this.dimensions().scale(off);
}
sprite.inputs = {};
sprite.inputs.kp9 = function() { this.pos = this.dimensions().scale([0,0]); };
sprite.inputs.kp8 = function() { this.pos = this.dimensions().scale([-0.5, 0]); };
sprite.inputs.kp7 = function() { this.pos = this.dimensions().scale([-1,0]); };
sprite.inputs.kp6 = function() { this.pos = this.dimensions().scale([0,-0.5]); };
sprite.inputs.kp5 = function() { this.pos = this.dimensions().scale([-0.5,-0.5]); };
sprite.inputs.kp4 = function() { this.pos = this.dimensions().scale([-1,-0.5]); };
sprite.inputs.kp3 = function() { this.pos = this.dimensions().scale([0, -1]); };
sprite.inputs.kp2 = function() { this.pos = this.dimensions().scale([-0.5,-1]); };
sprite.inputs.kp1 = function() { this.pos = this.dimensions().scale([-1,-1]); };
sprite.inputs.kp9 = function() { this.setanchor("ll"); }
sprite.inputs.kp8 = function() { this.setanchor("lm"); }
sprite.inputs.kp7 = function() { this.setanchor("lr"); }
sprite.inputs.kp6 = function() { this.setanchor("ml"); }
sprite.inputs.kp5 = function() { this.setanchor("mm"); }
sprite.inputs.kp4 = function() { this.setanchor("mr"); }
sprite.inputs.kp3 = function() { this.setanchor("ur"); }
sprite.inputs.kp2 = function() { this.setanchor("um"); }
sprite.inputs.kp1 = function() { this.setanchor("ul"); }
Object.seal(sprite);
/* sprite anim returns a data structure for the given file path
@ -194,111 +231,123 @@ Object.seal(sprite);
time: miliseconds to hold the frame for
loop: true if it should be looped
*/
var SpriteAnim = {
make(path) {
if (path.ext() === 'gif')
return SpriteAnim.gif(path);
else if (path.ext() === 'ase')
return SpriteAnim.aseprite(path);
else
return undefined;
},
gif(path) {
var anim = {};
anim.frames = [];
anim.path = path;
var frames = Resources.gif.frames(path);
var yslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {
x: 0,
w: 1,
y: yslice*f,
h: yslice
};
frame.time = 0.05;
anim.frames.push(frame);
}
var times = cmd(224,path);
for (var i = 0; i < frames; i++)
anim.frames[i].time = times[i]/1000;
anim.loop = true;
var dim = Resources.texture.dimensions(path);
dim.y /= frames;
anim.dim = dim;
return {0:anim};
},
var animcache = {};
var SpriteAnim = {};
SpriteAnim.make = function(path) {
if (path in animcache) return animcache[path];
var anim;
if (io.exists(path.set_ext(".ase")))
anim = SpriteAnim.aseprite(path.set_ext(".ase"));
else if (io.exists(path.set_ext(".json")))
anim = SpriteAnim.aseprite(path.set_ext(".json"));
else if (path.ext() === 'ase')
anim = SpriteAnim.aseprite(path);
else if (path.ext() === 'gif')
anim = SpriteAnim.gif(path);
else
anim = undefined;
strip(path, frames, time=0.05) {
var anim = {};
anim.frames = [];
anim.path = path;
var xslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {s0:xslice*f, s1: xslice*(f+1), t0:0, t1:1};
frame.time = time;
anim.frames.push(frame);
}
anim.dim = Resources.texture.dimensions(path);
anim.dim.x /= frames;
return anim;
},
animcache[path] = anim;
return animcache[path];
};
SpriteAnim.gif = function(path) {
console.info(`making an anim from ${path}`);
var anim = {};
anim.frames = [];
anim.path = path;
var tex = game.texture(path);
var frames = tex.frames;
console.info(`frames are ${frames}`);
if (frames === 1) return undefined;
var yslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {
x: 0,
w: 1,
y: yslice*f,
h: yslice
};
frame.time = 0.05;
anim.frames.push(frame);
}
var times = tex.delays;
for (var i = 0; i < frames; i++)
anim.frames[i].time = times[i]/1000;
anim.loop = true;
var dim = [tex.width,tex.height];
console.info(`dimensions are ${dim}`);
dim.y /= frames;
anim.dim = dim;
return {0:anim};
};
aseprite(path) {
SpriteAnim.strip = function(path, frames, time=0.05) {
var anim = {};
anim.frames = [];
anim.path = path;
var xslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {s0:xslice*f, s1: xslice*(f+1), t0:0, t1:1};
frame.time = time;
anim.frames.push(frame);
}
anim.dim = Resources.texture.dimensions(path);
anim.dim.x /= frames;
return anim;
};
SpriteAnim.aseprite = function(path) {
function aseframeset2anim(frameset, meta) {
var anim = {};
anim.frames = [];
anim.path = meta.image;
anim.path = path.dir() + "/" + meta.image;
var dim = meta.size;
var ase_make_frame = function(ase_frame,i) {
var ase_make_frame = function(ase_frame) {
var f = ase_frame.frame;
var frame = {};
frame.rect = {
x: f.x/dim.w,
w: f.w/dim.w,
y: f.y/dim.h,
h: f.h/dim.h
x: f.x/dim.w,
w: f.w/dim.w,
y: f.y/dim.h,
h: f.h/dim.h
};
frame.time = ase_frame.duration / 1000;
anim.frames.push(frame);
};
frameset.forEach(ase_make_frame);
anim.dim = [frameset[0].sourceSize.x, frameset[0].sourceSize.y];
anim.dim = frameset[0].sourceSize;
anim.loop = true;
return anim;
};
};
var json = io.slurp(path);
json = JSON.parse(json);
var anims = {};
var frames = Array.isArray(json.frames) ? json.frames : Object.values(json.frames);
var f = 0;
for (var tag of json.meta.frameTags) {
anims[tag.name] = aseframeset2anim(frames.slice(tag.from, tag.to+1), json.meta);
anims[f] = anims[tag.name];
f++;
}
var data = json.decode(io.slurp(path));
if (!data?.meta?.app.includes("aseprite")) return;
var anims = {};
var frames = Array.isArray(data.frames) ? data.frames : Object.values(data.frames);
var f = 0;
for (var tag of data.meta.frameTags) {
anims[tag.name] = aseframeset2anim(frames.slice(tag.from, tag.to+1), data.meta);
anims[f] = anims[tag.name];
f++;
}
return anims;
},
return anims;
};
validate(anim)
{
if (!Object.isObject(anim)) return false;
if (typeof anim.path !== 'string') return false;
if (typeof anim.dim !== 'object') return false;
return true;
},
SpriteAnim.validate = function(anim) {
if (!Object.isObject(anim)) return false;
if (typeof anim.path !== 'string') return false;
if (typeof anim.dim !== 'object') return false;
return true;
};
find(path) {
if (!io.exists(path + ".asset")) return;
var asset = JSON.parse(io.slurp(path + ".asset"));
},
SpriteAnim.find = function(path) {
if (!io.exists(path + ".asset")) return;
var asset = JSON.parse(io.slurp(path + ".asset"));
};
SpriteAnim.doc = 'Functions to create Primum animations from varying sources.';
@ -309,16 +358,11 @@ SpriteAnim.find.doc = 'Given a path, find the relevant animation for the file.';
/* For all colliders, "shape" is a pointer to a phys2d_shape, "id" is a pointer to the shape data */
var collider2d = Object.copy(component, {
name: "collider 2d",
sensor: false,
kill() {}, /* No killing is necessary - it is done through the gameobject's kill */
impl: {
set sensor(x) { cmd(18,this.shape,x); },
get sensor() { return cmd(21,this.shape); },
set enabled(x) { cmd(22,this.shape,x); },
get enabled() { return cmd(23,this.shape); }
set sensor(x) { pshape.set_sensor(this.shape,x); },
get sensor() { return pshape.get_sensor(this.shape); },
set enabled(x) { pshape.set_enabled(this.shape,x); },
get enabled() { return pshape.get_enabled(this.shape); }
},
});
@ -345,7 +389,7 @@ component.polygon2d = Object.copy(collider2d, {
},
hides: ['id', 'shape', 'gameobject'],
_enghook: make_poly2d,
_enghook: os.make_poly2d,
points:[],
setpoints(points) {
this.points = points;
@ -376,7 +420,7 @@ component.polygon2d = Object.copy(collider2d, {
gizmo() {
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.green));
this.points.forEach((x,i)=>Debug.numbered_point(this.gameobject.this2screen(x), i));
this.points.forEach((x,i)=>render.coordinate(this.gameobject.this2screen(x)));
},
pick(pos) {
@ -398,8 +442,8 @@ function pointscaler(x) {
}
component.polygon2d.impl = Object.mix(collider2d.impl, {
sync() { cmd_poly2d(0, this.id, this.spoints());},
query() { return cmd(80, this.shape); },
sync() { poly2d.setverts(this.id,this.spoints()); },
query() { return physics.shape_query(this.shape); },
grow: pointscaler,
});
@ -413,14 +457,14 @@ polygon2d.inputs.f10 = function() {
polygon2d.inputs.f10.doc = "Sort all points to be CCW order.";
polygon2d.inputs['C-lm'] = function() {
this.points.push(this.gameobject.world2this(Mouse.worldpos()));
this.points.push(this.gameobject.world2this(input.mouse.worldpos()));
};
polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse.";
polygon2d.inputs.lm = function(){};
polygon2d.inputs.lm.released = function(){};
polygon2d.inputs['C-M-lm'] = function() {
var idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return;
this.points.splice(idx, 1);
};
@ -440,6 +484,7 @@ component.edge2d = Object.copy(collider2d, {
'points',
'hollow',
'hollowt',
'angle',
]),
dimensions:2,
thickness:0,
@ -467,7 +512,7 @@ component.edge2d = Object.copy(collider2d, {
for (var i = spoints.length-1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.x = -newpoint.x;
newpoint.x = -newpoint.x;
spoints.push(newpoint);
}
}
@ -478,13 +523,13 @@ component.edge2d = Object.copy(collider2d, {
for (var i = spoints.length-1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
newpoint.y = -newpoint.y;
spoints.push(newpoint);
}
}
if (this.hollow) {
var hpoints = inflate_cpv(spoints, spoints.length, this.hollowt);
var hpoints = vector.inflate(spoints, this.hollowt);
if (hpoints.length === spoints.length) return spoints;
var arr1 = hpoints.filter(function(x,i) { return i % 2 === 0; });
var arr2 = hpoints.filter(function(x,i) { return i % 2 !== 0; });
@ -530,20 +575,20 @@ component.edge2d = Object.copy(collider2d, {
boundingbox() { return bbox.frompoints(this.points.map(x => x.scale(this.gameobject.scale))); },
hides: ['gameobject', 'id', 'shape'],
_enghook: make_edge2d,
_enghook: os.make_edge2d,
/* EDITOR */
gizmo() {
if (this.type === Spline.type.catmull || this.type === -1) {
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.teal));
this.points.forEach((x,i) => Debug.numbered_point(this.gameobject.this2screen(x), i));
this.points.forEach((x,i) => render.coordinate(this.gameobject.this2screen(x)));
} else {
for (var i = 0; i < this.points.length; i += 3)
Debug.numbered_point(this.gameobject.this2screen(this.points[i]), i, Color.teal);
render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.teal);
for (var i = 1; i < this.points.length; i+=3) {
Debug.numbered_point(this.gameobject.this2screen(this.points[i]), i, Color.green);
Debug.numbered_point(this.gameobject.this2screen(this.points[i+1]), i+1, Color.green);
render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.green);
render.coordinate(this.gameobject.this2screen(this.points[i+1]), 1, Color.green);
render.line([this.gameobject.this2screen(this.points[i-1]), this.gameobject.this2screen(this.points[i])], Color.yellow);
render.line([this.gameobject.this2screen(this.points[i+1]), this.gameobject.this2screen(this.points[i+2])], Color.yellow);
}
@ -565,23 +610,23 @@ component.edge2d = Object.copy(collider2d, {
if (p) {
var o = {
pos: p,
sync: me.sync.bind(me)
sync: me.sync.bind(me)
};
if (Spline.bezier_is_handle(this.points,i))
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
Spline.bezier_cp_mirror(me.points,i);
};
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
Spline.bezier_cp_mirror(me.points,i);
};
else
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
var pp = Spline.bezier_point_handles(me.points,i);
pp.forEach(ph => me.points[ph] = me.points[ph].add(d));
}
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
var pp = Spline.bezier_point_handles(me.points,i);
pp.forEach(ph => me.points[ph] = me.points[ph].add(d));
}
return o;
}
},
@ -607,7 +652,7 @@ component.edge2d = Object.copy(collider2d, {
var idx = 0;
if (Spline.is_catmull(this.type) || this.type === -1) {
if (this.points.length >= 2)
idx = cmd(59, pos, this.points, 400);
idx = physics.closest_point(pos, this.points, 400);
if (idx === this.points.length)
this.points.push(pos);
@ -616,7 +661,7 @@ component.edge2d = Object.copy(collider2d, {
}
if (Spline.is_bezier(this.type)) {
idx = cmd(59, pos, Spline.bezier_nodes(this.points),400);
idx = physics.closest_point(pos, Spline.bezier_nodes(this.points),400);
if (idx < 0) return;
@ -642,16 +687,14 @@ component.edge2d = Object.copy(collider2d, {
});
component.edge2d.impl = Object.mix(collider2d.impl, {
set thickness(x) {
cmd_edge2d(1,this.id,x);
},
get thickness() { return cmd(112,this.id); },
set thickness(x) { edge2d.set_thickness(this.id,x); },
get thickness() { return edge2d.get_thickness(this.id); },
grow: pointscaler,
sync() {
var sensor = this.sensor;
var points = this.sample();
if (!points) return;
cmd_edge2d(0,this.id,points);
edge2d.setverts(this.id,points);
this.sensor = sensor;
},
});
@ -659,7 +702,6 @@ component.edge2d.impl = Object.mix(collider2d.impl, {
var bucket = component.edge2d;
bucket.spoints.doc = "Returns the controls points after modifiers are applied, such as it being hollow or mirrored on its axises.";
bucket.inputs = {};
//bucket.inputs.post = function() { this.sync(); };
bucket.inputs.h = function() { this.hollow = !this.hollow; };
bucket.inputs.h.doc = "Toggle hollow.";
@ -731,7 +773,7 @@ bucket.inputs['C-o'].doc = "Set spline to linear.";
bucket.inputs['C-M-lm'] = function() {
if (Spline.is_catmull(this.type)) {
var idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return;
} else {
@ -741,16 +783,16 @@ bucket.inputs['C-M-lm'] = function() {
};
bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
bucket.inputs['C-lm'] = function() { this.add_node(Mouse.worldpos()); }
bucket.inputs['C-lm'] = function() { this.add_node(input.mouse.worldpos()); }
bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
bucket.inputs['C-M-lm'] = function() {
var idx = -1;
if (Spline.is_catmull(this.type))
idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
else {
var nodes = Spline.bezier_nodes(this.points);
idx = Math.grab_from_points(Mouse.worldpos(), nodes.map(p => this.gameobject.this2world(p)), 25);
idx = Math.grab_from_points(input.mouse.worldpos(), nodes.map(p => this.gameobject.this2world(p)), 25);
idx *= 3;
}
@ -791,11 +833,11 @@ component.circle2d = Object.copy(collider2d, {
toString() { return "circle2d"; },
boundingbox() {
return bbox.fromcwh(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]);
return bbox.fromcwh([0,0], [this.radius,this.radius]);
},
hides: ['gameobject', 'id', 'shape', 'scale'],
_enghook: make_circle2d,
_enghook: os.make_circle2d,
});
component.circle2d.impl = Object.mix({
@ -804,17 +846,17 @@ component.circle2d.impl = Object.mix({
"radius",
]),
set radius(x) { cmd_circle2d(0,this.id,x); },
get radius() { return cmd_circle2d(2,this.id); },
set radius(x) { circle2d.set_radius(this.id,x); circle2d.sync(this.id); },
get radius() { return circle2d.get_radius(this.id); },
set scale(x) { this.radius = x; },
get scale() { return this.radius; },
set offset(x) { cmd_circle2d(1,this.id,x); },
get offset() { return cmd_circle2d(3,this.id); },
set offset(x) { circle2d.set_offset(this.id,x); circle2d.sync(this.id); },
get offset() { circle2d.get_offset(this.id); },
get pos() { return cmd_circle2d(3,this.id); },
set pos(x) { cmd_circle2d(1,this.id,x); },
get pos() { return this.offset; },
set pos(x) { this.offset = x; },
grow(x) {
if (typeof x === 'number') this.scale *= x;

View file

@ -1,94 +1,58 @@
var Debug = {
fn_break(fn, obj) {
if (typeof fn !== 'function') return;
obj ??= globalThis;
debug.fn_break = function(fn,obj = globalThis) {
if (typeof fn !== 'function') return;
var newfn = function() {
console.log("broke");
fn();
};
obj[fn.name] = newfn;
},
draw_grid(width, span, color) {
color = color ? color : Color.green;
cmd(47, width, span, color);
},
coordinate(pos, size, color) { GUI.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color); },
boundingbox(bb, color) {
color ??= Color.white;
cmd_points(0, bbox.topoints(bb), color);
},
numbered_point(pos, n, color) {
color ??= Color.white;
render.point(pos, 3, color);
GUI.text(n, pos.add([0,4]), 1, color);
},
phys_drawing: false,
draw_phys(on) {
this.phys_drawing = on;
cmd(4, this.phys_drawing);
},
draw_obj_phys(obj) {
cmd(82, obj.body);
},
gameobject(go) { cmd(15, go.body); },
draw_bb: false,
draw_gizmos: false,
draw_names: false,
draw() {
if (this.draw_bb)
Game.all_objects(function(x) { Debug.boundingbox(x.boundingbox(), Color.Debug.boundingbox.alpha(0.05)); });
if (Game.paused()) GUI.text("PAUSED", [0,0],1);
if (this.draw_gizmos)
Game.all_objects(function(x) {
if (!x.icon) return;
GUI.image(x.icon, Window.world2screen(x.pos));
});
if (this.draw_names)
Game.all_objects(function(x) {
GUI.text(x, Window.world2screen(x.pos).add([0,32]), 1, Color.Debug.names);
});
if (Debug.Options.gif.rec) {
GUI.text("REC", [0,40], 1);
GUI.text(Time.seconds_to_timecode(Time.time - Debug.Options.gif.start_time, Debug.Options.gif.fps), [0,30], 1);
}
GUI.text(Game.playing() ? "PLAYING"
: Game.stepping() ?
"STEP" :
Game.paused() ?
"PAUSED; EDITING" :
"EDIT", [0, 0], 1);
},
};
function assert(op, str)
{
str ??= `assertion failed [value '${op}']`;
if (!op) {
console.error(`Assertion failed: ${str}`);
Game.quit();
}
var newfn = function() {
console.log("broke");
fn();
};
obj[fn.name] = newfn;
}
Debug.Options = { };
Debug.Options.Color = {
set trigger(x) { cmd(17,x); },
set debug(x) { cmd(16, x); },
};
debug.draw_phys = false;
debug.draw_bb = false;
debug.draw_gizmos = false;
debug.draw_names = false;
debug.draw = function() {
if (this.draw_phys) game.all_objects(function(x) { debug.draw_gameobject(x); });
if (this.draw_bb)
game.all_objects(function(x) { debug.boundingbox(x.boundingbox(), Color.debug.boundingbox.alpha(0.05)); });
if (this.draw_gizmos)
game.all_objects(function(x) {
if (!x.icon) return;
gui.image(x.icon, game.camera.world2view(x.pos));
});
if (this.draw_names)
game.all_objects(function(x) {
render.text(x, game.camera.view2screen(x.pos).add([0,32]), 1, Color.debug.names);
});
if (debug.gif.rec) {
render.text("REC", [0,40], 1);
render.text(time.timecode(time.timenow() - debug.gif.start_time, debug.gif.fps), [0,30], 1);
}
return;
if (sim.paused()) render.text("PAUSED", [0,0],1);
render.text(sim.playing() ? "PLAYING"
: sim.stepping() ?
"STEP" :
sim.paused() ?
"PAUSED; EDITING" :
"EDIT", [0, 0], 1);
}
function assert(op, str = `assertion failed [value '${op}']`)
{
if (!op) {
console.error(str);
os.quit();
}
}
var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) {
@ -98,83 +62,34 @@ var Gizmos = {
},
};
Object.assign(performance, {
tick_now() { return cmd(127); },
ns(ticks) { return cmd(128, ticks); },
us(ticks) { return cmd(129, ticks); },
ms(ticks) { return cmd(130, ticks); },
best_t(ns) {
var e = ns;
var qq = 'ns';
if (e > 1000) {
e /= 1000;
qq = 'us';
if (e > 1000) {
e /= 1000;
qq = 'ms';
}
}
return {
time: e,
unit: qq
};
},
cpu(fn, times, q) {
times ??= 1;
q ??= "unnamed";
var start = performance.tick_now();
for (var i = 0; i < times; i++)
fn();
var elapsed = performance.tick_now() - start;
var avgt = performance.best_t(elapsed/times);
var totalt = performance.best_t(elapsed);
say(`performance [${q}]: ${avgt.time.toFixed(3)} ${avgt.unit} average [${totalt.time.toFixed(3)} ${totalt.unit} for ${times} loops]`);
},
get fps() { return sys_cmd(8); },
measure(fn, str) {
str ??= 'unnamed';
var start = performance.tick_now();
profile.cpu = function(fn, times = 1, q = "unnamed") {
var start = profile.now();
for (var i = 0; i < times; i++)
fn();
var elapsed = performance.tick_now()-start;
elapsed = performance.best_t(elapsed);
say(`performance [${str}]: ${elapsed.time.toFixed(3)} ${elapsed.unit}`);
},
});
performance.now = performance.tick_now;
var elapsed = profile.now() - start;
var avgt = profile.best_t(elapsed/times);
var totalt = profile.best_t(elapsed);
performance.test = {
barecall() { performance(0); },
unpack_num(n) { performance(1,n); },
unpack_array(n) { performance(2,n); },
pack_num() { performance(3); },
pack_string() { performance(6); },
unpack_string(s) { performance(4,s); },
unpack_32farr(a) { performance(5,a); },
call_fn_n(fn1, n) { performance(7,fn1,n,fn2); },
};
say(`profile [${q}]: ${profile.best_t(avgt)} average [${profile.best_t(totalt)} for ${times} loops]`);
}
performance.test.call_fn_n.doc = "Calls fn1 n times, and then fn2.";
performance.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`;
profile.ms = function(t) { return t/1000000; }
profile.secs = function(t) { return t/1000000000; }
/* These controls are available during editing, and during play of debug builds */
Debug.inputs = {};
Debug.inputs.f1 = function () { Debug.draw_phys(!Debug.phys_drawing); };
Debug.inputs.f1.doc = "Draw physics debugging aids.";
//Debug.inputs.f3 = function() { Debug.draw_bb = !Debug.draw_bb; };
//Debug.inputs.f3.doc = "Toggle drawing bounding boxes.";
Debug.inputs.f4 = function() {
Debug.draw_names = !Debug.draw_names;
Debug.draw_gizmos = !Debug.draw_gizmos;
debug.inputs = {};
debug.inputs.f1 = function () { debug.draw_phys = !debug.draw_phys; };
debug.inputs.f1.doc = "Draw physics debugging aids.";
debug.inputs.f3 = function() { debug.draw_bb = !debug.draw_bb; };
debug.inputs.f3.doc = "Toggle drawing bounding boxes.";
debug.inputs.f4 = function() {
debug.draw_names = !debug.draw_names;
debug.draw_gizmos = !debug.draw_gizmos;
};
Debug.inputs.f4.doc = "Toggle drawing gizmos and names of objects.";
debug.inputs.f4.doc = "Toggle drawing gizmos and names of objects.";
Debug.Options.gif = {
debug.gif = {
w: 640, /* Max width */
h: 480, /* Max height */
stretch: false, /* True if you want to stretch */
@ -189,7 +104,7 @@ Debug.Options.gif = {
var w = this.w;
var h = this.h;
if (!this.stretch) {
var win = Window.height / Window.width;
var win = window.height / window.width;
var gif = h/w;
if (gif > win)
h = w * win;
@ -197,84 +112,43 @@ Debug.Options.gif = {
w = h / win;
}
cmd(131, w, h, this.cpf, this.depth);
// cmd(131, w, h, this.cpf, this.depth);
this.rec = true;
this.fps = (1/this.cpf)*100;
this.start_time = Time.time;
this.start_time = time.now();
timer.oneshot(this.stop.bind(this), this.secs, this, true);
},
stop() {
if (!this.rec) return;
cmd(132, this.file);
// cmd(132, this.file);
this.rec = false;
},
};
Debug.inputs.f8 = function() {
debug.inputs.f8 = function() {
var now = new Date();
Debug.Options.gif.file = now.toISOString() + ".gif";
Debug.Options.gif.start();
debug.gif.file = now.toISOString() + ".gif";
debug.gif.start();
};
Debug.inputs.f9 = function() {
Debug.Options.gif.stop();
debug.inputs.f9 = function() {
debug.gif.stop();
}
Debug.inputs.f10 = function() { Time.timescale = 0.1; };
Debug.inputs.f10.doc = "Toggle timescale to 1/10.";
Debug.inputs.f10.released = function () { Time.timescale = 1.0; };
Debug.inputs.f12 = function() { GUI.defaults.debug = !GUI.defaults.debug; console.warn("GUI toggle debug");};
Debug.inputs.f12.doc = "Toggle drawing GUI debugging aids.";
debug.inputs.f10 = function() { time.timescale = 0.1; };
debug.inputs.f10.doc = "Toggle timescale to 1/10.";
debug.inputs.f10.released = function () { time.timescale = 1.0; };
debug.inputs.f12 = function() { gui.defaults.debug = !gui.defaults.debug; console.warn("gui toggle debug");};
debug.inputs.f12.doc = "Toggle drawing gui debugging aids.";
Debug.inputs['M-1'] = render.normal;
Debug.inputs['M-2'] = render.wireframe;
debug.inputs['M-1'] = render.normal;
debug.inputs['M-2'] = render.wireframe;
debug.inputs['C-M-f'] = function() {};
debug.inputs['C-M-f'].doc = "Enter camera fly mode.";
Debug.inputs['C-M-f'] = function() {};
Debug.inputs['C-M-f'].doc = "Enter camera fly mode.";
var Time = {
set timescale(x) { cmd(3, x); },
get timescale() { return cmd(121); },
set updateMS(x) { cmd(6, x); },
set physMS(x) { cmd(7, x); },
set renderMS(x) { cmd(5, x); },
get time() { return cmd(133); },
seconds_to_timecode(secs, fps)
{
var s = Math.trunc(secs);
secs -= s;
var f = Math.trunc(fps * secs);
return `${s}:${f}`;
},
pause() {
Time.stash = Time.timescale;
Time.timescale = 0;
},
play() {
if (!Time.stash) {
console.warn("Tried to resume time without calling Time.pause first.");
return;
}
Time.timescale = Time.stash;
},
};
Time.doc = {};
Time.doc.timescale = "Get and set the timescale. 1 is normal time; 0.5 is half speed; etc.";
Time.doc.updateMS = "Set the ms per game update.";
Time.doc.physMS = "Set the ms per physics update.";
Time.doc.renderMS = "Set the ms per render update.";
Time.doc.time = "Seconds elapsed since the game started.";
Time.doc.pause = "Pause the game by setting the timescale to 0; remembers the current timescale on play.";
Time.doc.play = "Resume the game after using Time.pause.";
Debug.api = {};
Debug.api.doc_entry = function(obj, key)
debug.api = {};
debug.api.doc_entry = function(obj, key)
{
if (typeof key !== 'string') {
console.warn("Cannot print a key that isn't a string.");
@ -310,7 +184,7 @@ ${doc}
`;
}
Debug.api.print_doc = function(name)
debug.api.print_doc = function(name)
{
var obj = name;
if (typeof name === 'string') {
@ -343,16 +217,24 @@ Debug.api.print_doc = function(name)
if (key === 'doc') continue;
if (key === 'toString') continue;
mdoc += Debug.api.doc_entry(obj, key) + "\n";
mdoc += debug.api.doc_entry(obj, key) + "\n";
}
return mdoc;
}
debug.log = {};
debug.log.time = function(fn, name, avg=0)
{
debug.log.time[name] ??= [];
var start = profile.now();
fn();
debug.log.time[name].push(profile.now()-start);
}
return {
Debug,
Time,
debug,
Gizmos,
performance,
assert
}

View file

@ -36,25 +36,25 @@ function ediff(from,to)
if (Array.isArray(v)) {
if (!Array.isArray(to[key]) || v.length !== to[key].length) {
var r = ediff(v,[]);
if (r) ret[key] = Object.values(r);
return;
if (r) ret[key] = Object.values(r);
return;
}
var diff = ediff(from[key], to[key]);
if (diff && !Object.empty(diff))
ret[key] = Object.values(ediff(v,[]));
ret[key] = Object.values(ediff(v,[]));
return;
}
if (typeof v === 'object') {
if (typeof v === 'object' && v !== null) {
var diff = ediff(v, to[key]);
if (diff && !Object.empty(diff))
ret[key] = diff;
ret[key] = diff;
return;
}
if (typeof v === 'number') {
if (typeof v === 'number' || v === null) {
if (!isFinite(v)) v = null; // Squash infinity to null
if (v !== to[key])
ret[key] = v;

File diff suppressed because it is too large Load diff

View file

@ -1,37 +1,234 @@
"use math";
Object.defineProperty(String.prototype, 'tolast', {
value: function(val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return this.slice();
return this.slice(0,idx);
}
});
Object.defineProperty(String.prototype, 'dir', {
value: function() {
if (!this.includes('/')) return "";
return this.tolast('/');
}
});
globalThis.Resources = {};
Resources.replpath = function(str, path)
{
if (!str) return str;
if (str[0] === "/")
return str.rm(0);
if (str[0] === "@")
return os.prefpath() + "/" + str.rm(0);
if (!path) return str;
var stem = path.dir();
while (stem) {
var tr = stem + "/" +str;
if (io.exists(tr)) return tr;
stem = stem.updir();
}
return str;
}
Resources.replstrs = function(path)
{
if (!path) return;
var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
script = script.replace(regexp,function(str) {
var newstr = Resources.replpath(str.trimchr('"'), path);
return `"${newstr}"`;
});
return script;
}
globalThis.json = {};
json.encode = function(value, replacer, space = 1)
{
return JSON.stringify(value, replacer, space);
}
json.decode = function(text, reviver)
{
if (!text) return undefined;
return JSON.parse(text,reviver);
}
json.readout = function(obj)
{
var j = {};
for (var k in obj)
if (typeof obj[k] === 'function')
j[k] = 'function ' + obj[k].toString();
else
j[k] = obj[k];
return json.encode(j);
}
json.doc = {
doc: "json implementation.",
encode: "Encode a value to json.",
decode: "Decode a json string to a value.",
readout: "Encode an object fully, including function definitions."
};
Resources.scripts = ["jsoc", "jsc", "jso", "js"];
Resources.images = ["png", "gif", "jpg", "jpeg"];
Resources.sounds = ["wav", 'flac', 'mp3', "qoa"];
Resources.is_image = function(path) {
var ext = path.ext();
return Resources.images.any(x => x === ext);
}
function find_ext(file, ext)
{
if (io.exists(file)) return file;
for (var e of ext) {
var nf = `${file}.${e}`;
if (io.exists(nf)) return nf;
}
return;
}
Resources.find_image = function(file) { return find_ext(file,Resources.images); }
Resources.find_sound = function(file) { return find_ext(file,Resources.sounds); }
Resources.find_script = function(file) { return find_ext(file,Resources.scripts); }
profile.best_t = function(t) {
var qq = 'ns';
if (t > 1000) {
t /= 1000;
qq = 'us';
if (t > 1000) {
t /= 1000;
qq = 'ms';
}
}
return `${t.toPrecision(4)} ${qq}`;
}
profile.report = function(start, msg = "[undefined report]")
{
console.info(`${msg} in ${profile.best_t(profile.now()-start)}`);
}
profile.addreport = function(cache, line, start)
{
cache[line] ??= [];
cache[line].push(profile.now()-start);
}
profile.printreport = function(cache, name)
{
var report = name + "\n";
for (var i in cache) {
report += `${i} ${profile.best_t(profcache[i].reduce((a,b) => a+b)/profcache[i].length)}\n`;
}
return report;
}
console.transcript = "";
console.say = function(msg) {
msg += "\n";
console.print(msg);
console.transcript += msg;
};
console.log = console.say;
var say = console.say;
var print = console.print;
console.pprint = function(msg,lvl = 0) {
if (typeof msg === 'object')
msg = JSON.stringify(msg, null, 2);
var file = "nofile";
var line = 0;
var caller = (new Error()).stack.split('\n')[2];
if (caller) {
var md = caller.match(/\((.*)\:/);
var m = md ? md[1] : "SCRIPT";
if (m) file = m;
md = caller.match(/\:(\d*)\)/);
m = md ? md[1] : 0;
if (m) line = m;
}
console.rec(lvl, msg, file, line);
};
console.spam = function(msg) { console.pprint (msg,0); };
console.debug = function(msg) { console.pprint(msg,1); };
console.info = function(msg) { console.pprint(msg, 2); };
console.warn = function(msg) { console.pprint(msg, 3); };
console.error = function(msg) { console.pprint(msg + "\n" + console.stackstr(2), 4);};
console.panic = function(msg) { console.pprint(msg + "\n" + console.stackstr(2), 5); };
console.stackstr = function(skip=0) {
var err = new Error();
var stack = err.stack.split('\n');
return stack.slice(skip,stack.length).join('\n');
};
console.stack = function(skip = 0) { console.log(console.stackstr(skip+1)); };
console.stdout_lvl = 1;
console.trace = console.stack;
console.doc = {
level: "Set level to output logging to console.",
info: "Output info level message.",
warn: "Output warn level message.",
error: "Output error level message, and print stacktrace.",
critical: "Output critical level message, and exit game immediately.",
write: "Write raw text to console.",
say: "Write raw text to console, plus a newline.",
stack: "Output a stacktrace to console.",
console: "Output directly to in game console.",
clear: "Clear console."
};
globalThis.global = globalThis;
function use(file)
var profcache = {};
function use(file, env = {}, script)
{
if (use.files[file]) return use.files[file];
if (globalThis.console)
console.info(`running ${file}`);
file = Resources.find_script(file);
var st = profile.now();
var c = io.slurp(file);
profcache[file] ??= [];
var script = `(function() { ${c} })();`;
use.files[file] = cmd(123,script,global,file);
return use.files[file];
}
use.files = {};
function include(file,that)
{
if (!that) return;
if (globalThis.console) console.info(`running ${file}`);
var c = io.slurp(file);
eval_env(c, that, file);
if (use.cache[file]) {
var ret = use.cache[file].call(env);
profile.report(st, `CACHE eval ${file}`);
profile.addreport(profcache, file, st);
return;
}
console.info(`slurping ${file}`);
script ??= Resources.replstrs(file);
script = `(function() { var self = this; ${script}; })`;
var fn = os.eval(file,script);
use.cache[file] = fn;
var ret = fn.call(env);
profile.report(st, `eval ${file}`);
profile.addreport(profcache, file, st);
return ret;
}
function eval_env(script, env, file)
{
env ??= {};
file ??= "SCRIPT";
if (globalThis.console) console.info(`eval ${file}`);
script = `(function() { ${script}; }).call(this);\n`;
return cmd(123,script,env,file);
}
use.cache = {};
global.check_registers = function(obj)
{
@ -41,12 +238,6 @@ global.check_registers = function(obj)
if (typeof obj.physupdate === 'function')
obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj)));
if (typeof obj.collide === 'function')
register_collide(0, obj.collide.bind(obj), obj.body);
if (typeof obj.separate === 'function')
register_collide(3,obj.separate.bind(obj), obj.body);
if (typeof obj.draw === 'function')
obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj));
@ -63,105 +254,147 @@ global.check_registers = function(obj)
};
}
eval_env.dov = `Counterpart to /load_env/, but with a string.`;
function feval_env(file, env)
{
eval_env(io.slurp(file), env, file);
}
function load_env(file,env)
{
env ??= global;
// var script = io.slurp(file);
var script = io.slurp(file);
eval_env(script, env, file);
// cmd(16, file, env);
// var script = io.slurp(file);
// cmd(123, script, env, file);
}
load_env.doc = `Load a given file with 'env' as **this**. Does not add to the global namespace.`;
var load = use;
Object.assign(global, use("scripts/base.js"));
Object.assign(global, use("scripts/base"));
global.obscure('global');
global.mixin("scripts/render.js");
global.mixin("scripts/render");
global.mixin("scripts/debug");
global.Game = {
engine_start(fn) {
console.info("Starting rendering and sound ...");
cmd(257, fn);
},
var frame_t = profile.secs(profile.now());
var phys_step = 1/240;
object_count() {
return cmd(214);
},
var sim = {};
sim.mode = "play";
sim.play = function() { this.mode = "play"; os.reindex_static(); };
sim.playing = function() { return this.mode === 'play'; };
sim.pause = function() { this.mode = "pause"; };
sim.paused = function() { return this.mode === 'pause'; };
sim.step = function() { this.mode = 'step'; };
sim.stepping = function() { return this.mode === 'step'; }
all_objects(fn) {
/* Wind down from Primum */
},
var physlag = 0;
/* Returns a list of objects by name */
find(name) {
var gggstart = game.engine_start;
game.engine_start = function(s) {
game.startengine = 1;
gggstart(function() {
global.mixin("scripts/sound.js");
world_start();
go_init();
window.set_icon(os.make_texture("icons/moon.gif"))
Object.readonly(window.__proto__, 'vsync');
Object.readonly(window.__proto__, 'enable_dragndrop');
Object.readonly(window.__proto__, 'enable_clipboard');
Object.readonly(window.__proto__, 'high_dpi');
Object.readonly(window.__proto__, 'sample_count');
s();
}, process);
}
},
game.startengine = 0;
/* Return a list of objects derived from a specific prototype */
find_proto(proto) {
function process()
{
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
},
prosperon.appupdate(dt);
prosperon.emitters_step(dt);
input.procdown();
/* List of all objects spawned that have a specific tag */
find_tag(tag){
if (sim.mode === "play" || sim.mode === "step") {
prosperon.update(dt*game.timescale);
if (sim.mode === "step")
sim.pause();
},
physlag += dt;
quit() {
sys_cmd(0);
},
while (physlag > phys_step) {
physlag -= phys_step;
prosperon.phys2d_step(phys_step*game.timescale);
prosperon.physupdate(phys_step*game.timescale);
}
}
pause() { sys_cmd(3); },
stop() { Game.pause(); },
step() { sys_cmd(4);},
playing() { return sys_cmd(5); },
paused() { return sys_cmd(6); },
stepping() { return cmd(79); },
play() { sys_cmd(1); },
if (!game.camera)
prosperon.window_render(world, 1);
else
prosperon.window_render(game.camera, game.camera.zoom);
wait_fns: [],
render.set_camera();
render.sprites(); // blits all sprites
render.models(); // blits all models
render.emitters(); // blits emitters
prosperon.draw(); // draw calls
debug.draw(); // calls needed debugs
render.flush();
wait_exec(fn) {
if (!phys_stepping())
fn();
else
this.wait_fns.push(fn);
},
render.set_window();
prosperon.gui();
render.flush();
exec() {
this.wait_fns.forEach(function(x) { x(); });
render.end_pass();
}
this.wait_fns = [];
},
};
game.timescale = 1;
Game.gc = function() { cmd(259); }
Game.gc.doc = "Force the garbage collector to run.";
var eachobj = function(obj,fn)
{
var val = fn(obj);
if (val) return val;
for (var o in obj.objects) {
if (obj.objects[o] === obj)
console.error(`Object ${obj.toString()} is referenced by itself.`);
val = eachobj(obj.objects[o],fn);
if (val) return val;
}
}
Game.doc = {};
Game.doc.object = "Returns the entity belonging to a given id.";
Game.doc.quit = "Immediately quit the game.";
Game.doc.pause = "Pause game simulation.";
Game.doc.stop = "Stop game simulation. This does the same thing as 'pause', and if the game is a debug build, starts its editor.";
Game.doc.play = "Resume or start game simulation.";
Game.doc.editor_mode = "Set to true for the game to only update on input; otherwise the game updates every frame.";
Game.doc.dt = "Current frame dt.";
Game.doc.view_camera = "Set the camera for the current view.";
Game.doc.camera = "Current camera.";
game.all_objects = function(fn, startobj = world) { return eachobj(startobj,fn); };
game.find_object = function(fn, startobj = world) {
global.prosperon = {};
prosperon.version = cmd(255);
prosperon.revision = cmd(256);
}
game.tags = {};
game.tag_add = function(tag, obj) {
game.tags[tag] ??= {};
game.tags[tag][obj.guid] = obj;
}
game.tag_rm = function(tag, obj) {
delete game.tags[tag][obj.guid];
}
game.tag_clear_guid = function(guid)
{
for (var tag in game.tags)
delete game.tags[tag][guid];
}
game.objects_with_tag = function(tag)
{
if (!game.tags[tag]) return;
return Object.values(game.tags[tag]);
}
game.doc = {};
game.doc.object = "Returns the entity belonging to a given id.";
game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation.";
game.doc.camera = "Current camera.";
game.texture = function(path)
{
if (game.texture.cache[path]) return game.texture.cache[path];
if (!io.exists(path)) {
console.warn(`Missing texture: ${path}`);
game.texture.cache[path] = game.texture("icons/no_tex.gif");
} else
game.texture.cache[path] ??= os.make_texture(path);
return game.texture.cache[path];
}
game.texture.cache = {};
prosperon.semver = {};
prosperon.semver.valid = function(v, range)
@ -222,13 +455,22 @@ prosperon.touchpress = function(touches){};
prosperon.touchrelease = function(touches){};
prosperon.touchmove = function(touches){};
prosperon.clipboardpaste = function(str){};
prosperon.quit = function(){
console.info(profile.printreport(profcache, "USE REPORT"));
console.info(profile.printreport(entityreport, "ENTITY REPORT"));
global.mixin("scripts/input.js");
global.mixin("scripts/std.js");
console.level = 1;
global.mixin("scripts/diff.js");
global.mixin("scripts/color.js");
global.mixin("scripts/gui.js");
console.info("QUITTING");
for (var i in debug.log.time)
console.warn(debug.log.time[i].map(x=>profile.ms(x)));
};
global.mixin("scripts/input");
global.mixin("scripts/std");
global.mixin("scripts/diff");
global.mixin("scripts/color");
global.mixin("scripts/gui");
global.mixin("scripts/tween");
global.mixin("scripts/ai");
var timer = {
update(dt) {
@ -241,7 +483,7 @@ var timer = {
kill() {
this.end();
this.fn = undefined;
delete this.fn;
},
delay(fn, secs) {
@ -250,68 +492,20 @@ var timer = {
t.remain = secs;
t.fn = fn;
t.end = Register.update.register(timer.update.bind(t));
return function() { t.kill(); };
var returnfn = timer.kill.bind(t);
returnfn.remain = secs;
return returnfn;
},
};
global.mixin("scripts/tween.js");
global.mixin("scripts/physics.js");
global.mixin("scripts/ai.js");
global.mixin("scripts/geometry.js");
global.mixin("scripts/physics");
global.mixin("scripts/geometry");
/*
Factory for creating registries. Register one with 'X.register',
which returns a function that, when invoked, cancels the registry.
*/
var Register = {
kbm_input(mode, btn, state, ...args) {
if (state === 'released') {
btn = btn.split('-').last();
}
switch(mode) {
case "emacs":
player[0].raw_input(btn, state, ...args);
break;
case "mouse":
player[0].mouse_input(btn, state, ...args);
break;
case "char":
player[0].char_input(btn);
break;
};
},
gamepad_playermap: [],
gamepad_input(pad, btn, state, ...args) {
var player = this.gamepad_playermap[pad];
if (!player) return;
var statestr = Input.state2str(state);
var rawfn = `gamepad_${btn}_${statestr}`;
player.input(rawfn, ...args);
input.action.actions.forEach(x => {
if (x.inputs.includes(btn))
player.input(`action_${x.name}_${statestr}`, ...args);
});
},
unregister_obj(obj) { Player.uncontrol(obj); },
endofloop(fn) {
if (!this.inloop)
fn();
else {
this.loopcbs.push(fn);
}
},
clear() {
Register.registries.forEach(function(n) {
n.entries = [];
});
},
registries: [],
add_cb(name) {
@ -323,9 +517,12 @@ var Register = {
if (typeof obj === 'object')
fn = fn.bind(obj);
fns.push(fn);
return function() { fns.remove(fn); };
return function() {
fns.remove(fn);
};
}
prosperon[name] = function(...args) { fns.forEach(x => x(...args)); }
prosperon[name].fns = fns;
n.clear = function() { fns = []; }
Register[name] = n;
@ -335,16 +532,13 @@ var Register = {
},
};
Register.add_cb("update").doc = "Called once per frame.";
Register.add_cb("appupdate");
Register.add_cb("update").doc = "Called once per frame.";
Register.add_cb("physupdate");
Register.add_cb("gui");
Register.add_cb("debug");
Register.add_cb("gamepad_input");
Register.add_cb("draw");
Register.gamepad_playermap[0] = Player.players[0];
var Event = {
events: {},
@ -361,84 +555,60 @@ var Event = {
Object.keys(this.events).forEach(name => Event.unobserve(name,obj));
},
notify(name) {
notify(name, ...args) {
if (!this.events[name]) return;
this.events[name].forEach(function(x) {
x[1].call(x[0]);
x[1].call(x[0], ...args);
});
},
};
Window.modetypes = {
stretch: 0, // stretch to fill window
keep: 1, // keep exact dimensions
width: 2, // keep width
height: 3, // keep height
// window.rendersize is the resolution the game renders at
// window.size is the physical size of the window on the desktop
window.modetypes = {
stretch: 0, // stretch render to fill window
keep: 1, // keep render exact dimensions, with no stretching
width: 2, // keep render at width
height: 3, // keep render at height
expand: 4, // expand width or height
full: 5 // expand out beyond window
};
Window.size = [640, 480];
window.size = [640, 480];
Window.screen2world = function(screenpos) {
if (Game.camera)
return Game.camera.view2world(screenpos);
window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
return screenpos;
}
global.mixin("scripts/spline");
global.mixin("scripts/components");
Window.world2screen = function(worldpos) {
return Game.camera.world2view(worldpos);
}
window.doc = {};
window.doc.dimensions = "Window width and height packaged in an array [width,height]";
window.doc.title = "Name in the title bar of the window.";
window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
Window.icon = function(path) { cmd(90, path); };
Window.icon.doc = "Set the icon of the window using the PNG image at path.";
global.mixin("scripts/debug.js");
global.mixin("scripts/spline.js");
global.mixin("scripts/components.js");
Window.doc = {};
Window.doc.width = "Width of the game window.";
Window.doc.height = "Height of the game window.";
Window.doc.dimensions = "Window width and height packaged in an array [width,height]";
Window.doc.title = "Name in the title bar of the window.";
Window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
Register.update.register(Game.exec, Game);
global.mixin("scripts/actor.js");
global.mixin("scripts/entity.js");
global.mixin("scripts/actor");
global.mixin("scripts/entity");
function world_start() {
globalThis.world = Object.create(gameobject);
globalThis.world = os.make_gameobject();
world.objects = {};
world.check_dirty = function() {};
world.namestr = function(){};
world._ed = {
selectable:false,
dirty:false,
};
world.toString = function() { return "world"; };
world.master = gameobject;
world.ur = "world";
world.kill = function() { this.clear(); };
world.phys = 2;
gameobject.level = world;
gameobject.body = make_gameobject();
cmd(113,gameobject.body, gameobject);
Object.hide(gameobject, 'timescale');
var cam = world.spawn("scripts/camera2d.jso");
Game.view_camera(cam);
world.zoom = 1;
world._ed = { selectable: false };
world.ur = {};
world.ur.fresh = {};
game.cam = world;
}
global.mixin("scripts/physics.js");
global.mixin("scripts/physics");
Game.view_camera = function(cam)
{
Game.camera = cam;
cmd(61, Game.camera.body);
window.title = `Prosperon v${prosperon.version}`;
window.size = [500,500];
window.boundingbox = function() {
var pos = game.camera.pos;
var wh = window.rendersize.scale(game.camera.zoom);
return bbox.fromcwh(pos,wh);
}
Window.title = `Prosperon v${prosperon.version}`;

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,9 @@ var shape = {};
shape.sphere = {};
shape.circle = {};
shape.sphere.volume = function(r) { return Math.pi*r*r*r*4/3; };
shape.sphere.random = function(r,theta,phi)
shape.sphere.random = function(r,theta = [0,1], phi = [-0.5,0.5])
{
if (typeof r === 'number') r = [r,r];
theta ??= [0,1];
phi ??= [-0.5,0.5];
if (typeof theta === 'number') theta = [theta,theta];
if (typeof phi === 'number') phi = [phi,phi];
@ -44,8 +42,7 @@ shape.ngon = function(radius, n) {
return shape.arc(radius,360,n);
};
shape.arc = function(radius, angle, n, start) {
start ??= 0;
shape.arc = function(radius, angle, n, start = 0) {
start = Math.deg2rad(start);
if (angle >= 360)
angle = 360;

View file

@ -1,100 +1,41 @@
/*
GUI functions take screen space coordinates
gui functions take screen space coordinates
*/
var GUI = {
text(str, pos, size, color, wrap, anchor, cursor) {
size ??= 1;
color ??= Color.white;
wrap ??= -1;
anchor ??= [0,1];
gui.scissor_win = function() { gui.scissor(0,0,window.size.x,window.y); }
cursor ??= -1;
gui.input_lmouse_pressed = function() {
if (gui.selected)
gui.selected.action();
};
var bb = cmd(118, str, size, wrap);
var w = bb.r*2;
var h = bb.t*2;
//ui_text draws with an anchor on top left corner
var p = pos.slice();
p.x -= w * anchor.x;
bb.r += (w*anchor.x);
bb.l += (w*anchor.x);
p.y += h * (1 - anchor.y);
bb.t += h*(1-anchor.y);
bb.b += h*(1-anchor.y);
ui_text(str, p, size, color, wrap, cursor);
return bb;
},
scissor(x,y,w,h) {
cmd(140,x,y,w,h);
},
scissor_win() { cmd(140,0,0,Window.width,Window.height); },
image(path,pos,color) {
color ??= Color.black;
var wh = cmd(64,path);
gui_img(path,pos, [1.0,1.0], 0.0, false, [0.0,0.0], Color.white);
return bbox.fromcwh([0,0], wh);
},
newmg(img) {
var def = {
path: "",
pos: [0,0],
size:[0,0],
frame: {
x: 0,
y: 0,
w: 1,
h: 1
},
angle: 0,
anchor: [0,0],
color: Color.white,
}
for (var i in def)
img[i] ??= def[i];
gui_newmg
},
input_lmouse_pressed() {
if (GUI.selected)
GUI.selected.action();
},
input_s_pressed() {
if (GUI.selected?.down) {
GUI.selected.selected = false;
GUI.selected = GUI.selected.down;
GUI.selected.selected = true;
}
},
input_w_pressed() {
if (GUI.selected?.up) {
GUI.selected.selected = false;
GUI.selected = GUI.selected.up;
GUI.selected.selected = true;
}
},
input_enter_pressed() {
if (GUI.selected) {
GUI.selected.action();
}
gui.input_s_pressed = function() {
if (gui.selected?.down) {
gui.selected.selected = false;
gui.selected = gui.selected.down;
gui.selected.selected = true;
}
};
GUI.controls = {};
GUI.controls.toString = function() { return "GUI controls"; };
GUI.controls.update = function() { };
gui.input_w_pressed = function() {
if (gui.selected?.up) {
gui.selected.selected = false;
gui.selected = gui.selected.up;
gui.selected.selected = true;
}
};
GUI.controls.set_mum = function(mum)
gui.input_enter_pressed = function() {
if (gui.selected) {
gui.selected.action();
}
}
gui.controls = {};
gui.controls.toString = function() { return "gui controls"; };
gui.controls.update = function() { };
gui.controls.set_mum = function(mum)
{
mum.selected = true;
@ -103,22 +44,22 @@ GUI.controls.set_mum = function(mum)
this.selected = mum;
}
GUI.controls.check_bb = function(mum)
gui.controls.check_bb = function(mum)
{
if (bbox.pointin(mum.bb, Mouse.screenpos()))
GUI.controls.set_mum(mum);
if (bbox.pointin(mum.bb, input.mouse.screenpos()))
gui.controls.set_mum(mum);
}
GUI.controls.inputs = {};
GUI.controls.inputs.fallthru = false;
GUI.controls.inputs.mouse = {};
GUI.controls.inputs.mouse.move = function(pos,dpos)
gui.controls.inputs = {};
gui.controls.inputs.fallthru = false;
gui.controls.inputs.mouse = {};
gui.controls.inputs.mouse.move = function(pos,dpos)
{
}
GUI.controls.inputs.mouse.scroll = function(scroll)
gui.controls.inputs.mouse.scroll = function(scroll)
{
}
GUI.controls.check_submit = function() {
gui.controls.check_submit = function() {
if (this.selected && this.selected.action)
this.selected.action(this.selected);
}
@ -177,11 +118,9 @@ var Mum = {
}
Mum.text = Mum.extend({
draw(cursor, cnt) {
cursor ??= [0,0];
cnt ??= Mum;
draw(cursor = [0,0], cnt = Mum) {
if (this.hide) return;
if (this.selectable) GUI.controls.check_bb(this);
if (this.selectable) gui.controls.check_bb(this);
this.caret ??= -1;
/* if (!this.bb)
@ -197,8 +136,8 @@ Mum.text = Mum.extend({
this.height = this.wh.y;
var aa = [0,1].sub(params.anchor);
var pos = cursor.add(params.wh.scale(aa)).add(params.offset);
cmd(263, params.font);
ui_text(params.str, pos, params.font_size, params.color, this.width, params.caret);
// gui.font_set(params.font);
render.text(params.str, pos, params.font_size, params.color, this.width, undefined, params.caret);
},
update_bb(cursor) {
@ -206,7 +145,7 @@ Mum.text = Mum.extend({
},
calc_bb(cursor) {
var bb = cmd(118,this.str, this.font_size, this.width);
var bb = render.text_size(this.str, this.font_size, this.width);
this.wh = bbox.towh(bb);
var pos = cursor.add(this.wh.scale([0,1].sub(this.anchor))).add(this.offset);
this.bb = bbox.move(bb,pos.add([this.wh.x/2,0]));
@ -230,23 +169,20 @@ Mum.window = Mum.extend({
this.wh = [this.width, this.height];
this.bb = bbox.fromcwh([0,0], this.wh);
},
draw(cursor, cnt) {
cursor ??= [0,0];
cnt ??= Mum;
draw(cursor = [0,0], cnt = Mum) {
var p = cursor.sub(this.wh.scale(this.anchor)).add(this.padding);
GUI.window(p,this.wh, this.color);
render.window(p,this.wh, this.color);
this.bb = bbox.blwh(p, this.wh);
GUI.flush();
GUI.scissor(p.x,p.y,this.wh.x,this.wh.y);
gui.flush();
this.max_width = this.width;
if (this.selectable) GUI.controls.check_bb(this);
if (this.selectable) gui.controls.check_bb(this);
var pos = [this.bb.l, this.bb.t].add(this.padding);
this.items.forEach(function(item) {
if (item.hide) return;
item.draw(pos.slice(),this);
}, this);
GUI.flush();
GUI.scissor_win();
gui.flush();
gui.scissor_win();
},
});
@ -258,7 +194,7 @@ Mum.image = Mum.extend({
return;
}
var tex_wh = cmd(64, this.path);
var tex_wh = texture.dimensions(this.path);
this.wh = tex_wh.slice();
if (this.width !== 0) this.wh.x = this.width;
if (this.height !== 0) this.wh.y = this.height;
@ -279,9 +215,7 @@ Mum.image = Mum.extend({
});
Mum.column = Mum.extend({
draw(cursor, cnt) {
cursor ??= [0,0];
cnt ??= Mum;
draw(cursor = [0,0], cnt = Mum) {
if (this.hide) return;
cursor = cursor.add(this.offset);
this.max_width = cnt.width;
@ -295,16 +229,6 @@ Mum.column = Mum.extend({
},
});
GUI.window = function(pos, wh, color)
{
var p = pos.slice();
p.x += wh.x/2;
p.y += wh.y/2;
render.box(p,wh,color);
}
GUI.flush = function() { cmd(141); };
Mum.debug_colors = {
bounds: Color.red.slice(),
margin: Color.blue.slice(),
@ -313,7 +237,4 @@ Mum.debug_colors = {
Object.values(Mum.debug_colors).forEach(function(v) { v.a = 100; });
return {
GUI,
Mum
};
return { Mum };

View file

@ -1,25 +1,25 @@
var keycodes = {
259: "back",
258: "tab",
257: "enter",
1: "escape",
input.keycodes = {
32: "space",
45: "minus",
256: "escape",
257: "enter",
258: "tab",
259: "backspace",
260: "insert",
261: "delete",
262: "right",
263: "left",
264: "down",
265: "up",
266: "pgup",
267: "pgdown",
268: "home",
269: "end",
263: "left",
265: "up",
262: "right",
265: "down",
260: "insert",
261: "delete",
45: "minus",
};
var codekeys = {};
for (var code in keycodes)
codekeys[keycodes[code]] = code;
input.codekeys = {};
for (var code in input.keycodes)
input.codekeys[input.keycodes[code]] = code;
var mod = {
shift: 0,
@ -36,6 +36,8 @@ pressrep
down
*/
function keycode(name) { return charCodeAt(name); }
function keyname_extd(key)
{
if (key > 289 && key < 302) {
@ -48,7 +50,7 @@ function keyname_extd(key)
return `kp${num}`;
}
if (keycodes[key]) return keycodes[key];
if (input.keycodes[key]) return input.keycodes[key];
if (key >= 32 && key <= 126) return String.fromCharCode(key).lc();
return undefined;
@ -67,7 +69,6 @@ function modstr()
prosperon.keydown = function(key, repeat)
{
prosperon.keys[key] = true;
if (key == 341 || key == 345)
@ -93,6 +94,7 @@ prosperon.keyup = function(key)
prosperon.keys[key] = false;
if (key == 341 || key == 345)
mod.ctrl = 0;
else if (key == 342 || key == 346)
mod.alt = 0;
else if (key == 343 || key == 347)
@ -112,78 +114,76 @@ prosperon.droppedfile = function(path)
var mousepos = [0,0];
prosperon.textinput = function(){};
prosperon.textinput = function(c){
player[0].raw_input("char", "pressed", c);
};
prosperon.mousemove = function(pos, dx){
mousepos = pos;
player[0].mouse_input(modstr() + "move", pos, dx);
player[0].mouse_input("move", pos, dx);
};
prosperon.mousescroll = function(dx){
player[0].mouse_input(modstr() + "scroll", dx);
};
prosperon.mousedown = function(b){
player[0].raw_input(modstr() + Mouse.button[b], "pressed");
player[0].raw_input(modstr() + input.mouse.button[b], "pressed");
};
prosperon.mouseup = function(b){
player[0].raw_input(modstr() + Mouse.button[b], "released");
player[0].raw_input(input.mouse.button[b], "released");
};
var Mouse = {
screenpos() { return mousepos.slice(); },
worldpos() { return Window.screen2world(mousepos); },
disabled() { cmd(46, 1); },
normal() { cmd(46, 0);},
mode(m) {
if (Mouse.custom[m])
cmd(97, Mouse.custom[m]);
else
cmd(17, m);
},
set_custom_cursor(img, mode) {
mode ??= Mouse.cursor.default;
if (!img)
delete Mouse.custom[mode];
else {
cmd(97, img);
Mouse.custom[mode] = img;
}
},
button: { /* left, right, middle mouse */
0: "lm",
1: "rm",
2: "mm"
},
custom:[],
cursor: {
default: 0,
arrow: 1,
ibeam: 2,
cross: 3,
hand: 4,
ew: 5,
ns: 6,
nwse: 7,
nesw: 8,
resize: 9,
no: 10
},
input.mouse = {};
input.mouse.screenpos = function() { return mousepos.slice(); };
input.mouse.worldpos = function() { return game.camera.view2world(mousepos); };
input.mouse.disabled = function() { input.mouse_mode(1); };
input.mouse.normal = function() { input.mouse_mode(0); };
input.mouse.mode = function(m) {
if (input.mouse.custom[m])
input.cursor_img(input.mouse.custom[m]);
else
input.mouse_cursor(m);
};
Mouse.doc = {};
Mouse.doc.pos = "The screen position of the mouse.";
Mouse.doc.worldpos = "The position in the game world of the mouse.";
Mouse.disabled.doc = "Set the mouse to hidden. This locks it to the game and hides it, but still provides movement and click events.";
Mouse.normal.doc = "Set the mouse to show again after hiding.";
var Keys = {
down(code) {
return prosperon.keys[code];
},
input.mouse.set_custom_cursor = function(img, mode = input.mouse.cursor.default) {
if (!img)
delete input.mouse.custom[mode];
else {
input.cursor_img(img);
input.mouse.custom[mode] = img;
}
};
var input = {};
input.mouse.button = { /* left, right, middle mouse */
0: "lm",
1: "rm",
2: "mm"
};
input.mouse.custom = [];
input.mouse.cursor = {
default: 0,
arrow: 1,
ibeam: 2,
cross: 3,
hand: 4,
ew: 5,
ns: 6,
nwse: 7,
nesw: 8,
resize: 9,
no: 10
};
input.mouse.doc = {};
input.mouse.doc.pos = "The screen position of the mouse.";
input.mouse.doc.worldpos = "The position in the game world of the mouse.";
input.mouse.disabled.doc = "Set the mouse to hidden. This locks it to the game and hides it, but still provides movement and click events.";
input.mouse.normal.doc = "Set the mouse to show again after hiding.";
input.keyboard = {};
input.keyboard.down = function(code) {
if (typeof code === 'number') return prosperon.keys[code];
if (typeof code === 'string') return (prosperon.keys[code.uc().charCodeAt()] || prosperon.keys[code.lc().charCodeAt()]);
return undefined;
}
input.state2str = function(state) {
if (typeof state === 'string') return state;
@ -207,6 +207,14 @@ input.print_pawn_kbm = function(pawn) {
return str;
};
input.procdown = function()
{
for (var k of prosperon.keys) {
if (!k) continue;
player[0].raw_input(keyname_extd(k), "down");
}
}
input.print_md_kbm = function(pawn) {
if (!('inputs' in pawn)) return;
@ -237,13 +245,6 @@ input.action = {
actions: [],
};
input.keyboard_show = function(show)
{
cmd(250,show);
}
input.keyboard_shown = function() { return cmd(248); }
/* May be a human player; may be an AI player */
var Player = {
players: [],
@ -287,7 +288,7 @@ var Player = {
continue;
}
var fn = null;
var fn = null;
switch (state) {
case 'pressed':
@ -310,8 +311,8 @@ var Player = {
switch (state) {
case 'released':
pawn.inputs.release_post?.call(pawn);
break;
pawn.inputs.release_post?.call(pawn);
break;
}
if (!pawn.inputs.fallthru) return;
@ -319,12 +320,6 @@ var Player = {
}
},
do_uncontrol(pawn) {
this.players.forEach(function(p) {
p.pawns = p.pawns.filter(x => x !== pawn);
});
},
obj_controlled(obj) {
for (var p in Player.players) {
if (p.pawns.contains(obj))
@ -359,6 +354,13 @@ var Player = {
},
};
input.do_uncontrol = function(pawn)
{
Player.players.forEach(function(p) {
p.pawns = p.pawns.filter(x => x !== pawn);
});
}
for (var i = 0; i < 4; i++) {
Player.create();
}
@ -372,11 +374,5 @@ Player.doc.players = "A list of current players.";
var player = Player;
return {
Mouse,
Keys,
input,
Player,
player,
keycodes,
codekeys
player
};

View file

@ -1,79 +1,57 @@
/* On collisions, entities are sent a 'hit' object, which looks like this:
var HIT = {
normal: "The normal of the collision point.",
hit: "The gameobject of the object that collided.",
obj: "The gameobject of the object that collided.",
sensor: "Boolean for if the colliding object was a sensor.",
velocity: "Velocity of the contact.",
pos: "Position in world space of the contact.",
depth: "Depth of the contact.",
};
*/
var physics = {
physics.pos_query = function(pos, start = world, give = 10) {
var ret;
ret = physics.point_query_nearest(pos, 0);
if (ret)
return ret.entity;
return game.all_objects(function(o) {
var dist = Vector.length(o.pos.sub(pos));
if (dist <= give) return o;
});
}
physics.box_point_query = function(box,points) {
if (!box || !points) return [];
var bbox = bbox.fromcwh(box.pos,box.wh);
var inside = [];
for (var i in points)
if (bbox.pointin(bbox,points[i])) inside.push[i];
return inside;
}
Object.assign(physics, {
dynamic: 0,
kinematic: 1,
static: 2,
pos_query(pos, give) {
give ??= 25;
return cmd(44, pos, give);
},
/* Returns a list of body ids that a box collides with */
box_query(box) { return cmd(52, box.pos, box.wh); },
box_point_query(box, points) {
if (!box || !points)
return [];
return cmd(86, box.pos, box.wh, points, points.length);
},
shape_query(shape) { return cmd(80,shape); },
com(pos) {
if (!Array.isArray(pos)) return [0,0];
return pos.reduce((a,i) => a.add(i)).map(g => g/pos.length);
},
};
});
physics.doc = {};
physics.doc.pos_query = "Returns any object colliding with the given point.";
physics.doc.box_query = "Returns an array of body ids that collide with a given box.";
physics.doc.box_query = "Calls a given function on every shape object in the given bbox.";
physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box.";
physics.collision = {
types: {},
num: 32,
set_collide(a, b, x) {
this.types[a][b] = x;
this.types[b][a] = x;
this.sync();
},
sync() {
for (var i = 0; i < this.num; i++)
cmd(76,i,this.types[i]);
},
};
for (var i = 0; i < physics.collision.num; i++) {
physics.collision.types[i] = [];
for (var j = 0; j < physics.collision.num; j++)
physics.collision.types[i][j] = false;
};
physics.collision.sync();
physics.warp = {};
physics.warp.gravity = function() { return cmd(253); }
physics.warp.damp = function() { return cmd(254); }
physics.gravity = physics.warp.gravity();
physics.gravity.mask = [true];
physics.gravity = physics.make_gravity();
physics.gravity.mask = ~1;
physics.gravity.strength = 500;
physics.damp = physics.warp.damp();
physics.damp.mask = [true];
physics.damp = physics.make_damp();
physics.damp.mask = ~1;
return {
physics

View file

@ -1,9 +1,3 @@
var render = {
normal() { cmd(67);},
wireframe() { cmd(68); },
pass() { },
};
render.doc = {
doc: "Functions for rendering modes.",
normal: "Final render with all lighting.",
@ -47,23 +41,16 @@ render.device = {
render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
/* All draw in screen space */
render.point = function(pos,size,color) {
color ??= Color.blue;
render.circle(pos,size,color);
render.point = function(pos,size,color = Color.blue) {
render.circle(pos,size,size,color);
};
var tmpline = render.line;
render.line = function(points, color = Color.white, thickness = 1) {
tmpline(points,color,thickness);
};
render.line = function(points, color, thickness) {
thickness ??= 1;
color ??= Color.white;
cmd(83, points, color, thickness);
};
render.poly = function(points, color) { cmd_points(0,points,color); };
render.circle = function(pos, radius, color) { cmd(115, pos, radius, color); };
render.cross = function(pos, size, color) {
color ??= Color.red;
render.cross = function(pos, size, color = Color.red) {
var a = [
pos.add([0,size]),
pos.add([0,-size])
@ -77,35 +64,79 @@ render.cross = function(pos, size, color) {
render.line(b,color);
};
render.arrow = function(start, end, color, wingspan, wingangle) {
color ??= Color.red;
wingspan ??= 4;
wingangle ??=10;
render.arrow = function(start, end, color = Color.red, wingspan = 4, wingangle = 10) {
var dir = end.sub(start).normalized();
var wing1 = [
Vector.rotate(dir, wingangle).scale(wingspan).add(end),
end
];
var wing2 = [
Vector.rotate(dir,-wingangle).scale(wingspan).add(end),
end
];
render.line([start,end],color);
render.line(wing1,color);
render.line(wing2,color);
};
var dir = end.sub(start).normalized();
var wing1 = [
Vector.rotate(dir, wingangle).scale(wingspan).add(end),
end
];
var wing2 = [
Vector.rotate(dir,-wingangle).scale(wingspan).add(end),
end
];
render.line([start,end],color);
render.line(wing1,color);
render.line(wing2,color);
};
render.coordinate = function(pos, size, color) {
render.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color);
render.point(pos, 2, color);
}
render.boundingbox = function(bb, color = Color.white) {
render.poly(bbox.topoints(bb), color);
}
render.rectangle = function(lowerleft, upperright, color) {
var pos = lowerleft.add(upperright).map(x=>x/2);
var wh = [upperright.x-lowerleft.x,upperright.y-lowerleft.y];
render.box(pos,wh,color);
};
var points = [lowerleft, lowerleft.add([upperright.x-lowerleft.x,0]), upperright, lowerleft.add([0,upperright.y-lowerleft.y])];
render.poly(points, color);
};
render.box = function(pos, wh, color) {
color ??= Color.white;
cmd(53, pos, wh, color);
};
render.box = function(pos, wh, color = Color.white) {
var lower = pos.sub(wh.scale(0.5));
var upper = pos.add(wh.scale(0.5));
render.rectangle(lower,upper,color);
};
render.window = function(pos, wh, color) {
var p = pos.slice();
p = p.add(wh.scale(0.5));
render.box(p,wh,color);
};
render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0,1], cursor = -1) {
var bb = render.text_size(str, size, wrap);
var w = bb.r*2;
var h = bb.t*2;
//render.text draws with an anchor on top left corner
var p = pos.slice();
p.x -= w * anchor.x;
bb.r += (w*anchor.x);
bb.l += (w*anchor.x);
p.y += h * (1 - anchor.y);
bb.t += h*(1-anchor.y);
bb.b += h*(1-anchor.y);
gui.text(str, p, size, color, wrap, cursor);
return bb;
};
render.image = function(tex, pos, rotation = 0, color = Color.white, dimensions = [tex.width, tex.height]) {
var scale = [dimensions.x/tex.width, dimensions.y/tex.height];
gui.img(tex,pos, scale, 0.0, false, [0.0,0.0], color);
return bbox.fromcwh([0,0], [tex.width,tex.height]);
}
render.fontcache = {};
render.set_font = function(path, size) {
var fontstr = `${path}-${size}`;
if (!render.fontcache[fontstr]) render.fontcache[fontstr] = os.make_font(path, size);
gui.font_set(render.fontcache[fontstr]);
render.font = render.fontcache[fontstr];
}
render.doc = "Draw shapes in screen space.";
render.circle.doc = "Draw a circle at pos, with a given radius and color.";

View file

@ -1,91 +1,84 @@
var audio = {};
/* This file runs after the audio system is initiated */
var sound_pref = ['wav', 'flac', 'mp3', 'qoa'];
var cries = {};
audio.sound = {
bus: {},
samplerate() { return cmd(198); },
sounds: [], /* array of loaded sound files */
play(file, bus) {
file = Resources.find_sound(file);
if (!file) {
console.error(`Cannot play sound ${file}: does not exist.`);
return;
}
var src = audio.dsp.source(file);
bus ??= audio.sound.bus.master;
src.plugin(bus);
return src;
},
Object.readonly(audio, 'samplerate');
Object.readonly(audio, 'channels');
Object.readonly(audio, 'buffer_frames');
doc: {
play: "Play the given file once.",
volume: "Set the volume. 0 is no sound and 100 is loudest."
},
audio.play = function(file,bus = audio.bus.master) {
file = Resources.find_sound(file);
if (!file) {
console.error(`Cannot play sound ${file}: does not exist.`);
return;
}
var src = audio.dsp.source(file);
src.plugin(bus);
src.guid = prosperon.guid();
return src;
}
audio.bus = {};
audio.bus.master = dspsound.master();
audio.dsp = {};
audio.dsp = dspsound;
audio.cry = function(file)
{
var player = audio.play(file);
if (!player) return;
player.guid = prosperon.guid();
cries[player.guid] = player;
player.ended = function() { delete cries[player.guid]; player = undefined; }
return player.ended;
}
var killer = Register.appupdate.register(function() {
for (var i in cries) {
var cry = cries[i];
if (!cry.ended) continue;
if (cry.frame < cry.lastframe || cry.frame === cry.frames())
cry.ended();
cry.lastframe = cry.frame;
}
});
var song;
audio.music = function(file, fade = 0) {
if (!fade) {
song = audio.play(file);
song.loop = true;
return;
}
var temp = audio.play(file);
if (!temp) return;
temp.volume = 0;
var temp2 = song;
tween(temp, 'volume', 1, fade);
tween(temp2, 'volume', 0, fade);
song = temp;
song.loop = true;
}
audio.dsp.mix = function(to) {
var n = audio.dsp.mix();
if (to) n.plugin(to);
return n;
};
audio.dsp = {
mix(to) {
var n = cmd(181);
if (to) n.plugin(to);
return n;
},
source(path) {
return cmd(182,path);
},
delay(secs,decay) {
return cmd(185, secs, decay);
},
fwd_delay(secs, decay) {
return cmd(207,secs,decay);
},
allpass(secs, decay) {
var composite = {};
var fwd = audio.dsp.fwd_delay(secs,-decay);
var fbk = audio.dsp.delay(secs,decay);
composite.id = fwd.id;
composite.plugin = composite.plugin.bind(fbk);
composite.unplug = dsp_node.unplug.bind(fbk);
fwd.plugin(fbk);
return composite;
},
lpf(f) {
return cmd(186,f);
},
hpf(f) {
return cmd(187,f);
},
mod(path) {
return cmd(188,path);
},
midi(midi,sf) {
return cmd(206,midi,sf);
},
crush(rate, depth) {
return cmd(189,rate,depth);
},
compressor() {
return cmd(190);
},
limiter(ceil) {
return cmd(191,ceil);
},
noise_gate(floor) {
return cmd(192,floor);
},
pitchshift(octaves) {
return cmd(200,octaves);
},
noise() {
return cmd(203);
},
pink() {
return cmd(204);
},
red() {
return cmd(205);
},
};
audio.dsp.allpass = function(secs, decay) {
var composite = {};
var fwd = audio.dsp.fwd_delay(secs,-decay);
var fbk = audio.dsp.delay(secs,decay);
composite.id = fwd.id;
composite.plugin = composite.plugin.bind(fbk);
composite.unplug = dsp_node.unplug.bind(fbk);
fwd.plugin(fbk);
return composite;
}
audio.dsp.doc = {
delay: "Delays the input by secs, multiplied by decay",
@ -103,18 +96,15 @@ audio.dsp.doc = {
red: "Red noise"
};
Object.mixin(cmd(180).__proto__, {
Object.mixin(audio.bus.master.__proto__, {
get db() { return 20*Math.log10(Math.abs(this.volume)); },
set db(x) { x = Math.clamp(x,-100,0); this.volume = Math.pow(10, x/20); },
get volume() { return this.gain; },
set volume(x) { this.gain = x; },
});
audio.sound.bus.master = cmd(180);
/*Object.mixin(audio.dsp.source().__proto__, {
frames() { return cmd(197,this); },
length() { return this.frames()/sound.samplerate(); },
length() { return this.frames()/audio.samplerate(); },
time() { return this.frame/sound.samplerate(); },
pct() { return this.time()/this.length(); },
});

View file

@ -1,6 +1,8 @@
var Spline = {};
Spline.sample_angle = function(type, points, angle) {
return spline_cmd(0, type, points[0].length, points, angle);
if (type === 0) return spline.catmull(points, angle);
else if (type === 1) return spline.bezier(points,angle);
return undefined;
}
Spline.bezier_loop = function(cp)

2
scripts/sprite.jso Normal file
View file

@ -0,0 +1,2 @@
this.phys = physics.static;
this.add_component(component.sprite);

View file

@ -6,17 +6,16 @@ if (os.sys() === 'windows')
else
os.user = os.env("USER");
var appy = {};
appy.inputs = {};
if (os.sys() === 'macos') {
appy.inputs['S-q'] = function() { Game.quit(); };
appy.inputs['S-q'] = os.quit;
appy.inputs['S-h'] = function() { };
appy.inputs['S-g'] = os.gc;
}
player[0].control(appy);
var steam = {};
steam.appid = 480;
steam.userid = 8437843;
@ -35,33 +34,18 @@ var otherpath = {
}
os.prefpath = function() {
return otherpath[os.sys()] + "/" + (Game.title ? Game.title : "Untitled Prosperon Game");
return otherpath[os.sys()] + "/" + (game.title ? game.title : "Untitled Prosperon Game");
}
os.openurl = function(url) {
if (os.sys() === 'windows')
os.system(`start ${url}`);
else
os.system(`open ${url}`);
}
var projectfile = ".prosperon/project.json";
var Resources = {};
Resources.images = ["png", "gif", "jpg", "jpeg"];
Resources.sounds = ["wav", 'flac', 'mp3', "qoa"];
Resources.scripts = "js";
Resources.is_image = function(path) {
var ext = path.ext();
return Resources.images.any(x => x === ext);
}
function find_ext(file, ext)
{
if (io.exists(file)) return file;
for (var e of ext) {
var nf = `${file}.${e}`;
if (io.exists(nf)) return nf;
}
return;
}
Resources.find_image = function(file) { return find_ext(file,Resources.images); }
Resources.find_sound = function(file) { return find_ext(file,Resources.sounds); }
Resources.is_sound = function(path) {
var ext = path.ext();
return Resources.sounds.any(x => x === ext);
@ -81,121 +65,10 @@ Resources.is_path = function(str)
}
Resources.texture = {};
Resources.texture.dimensions = function(path) { return cmd(64,path); }
Resources.texture.dimensions = function(path) { texture.dimensions(path); }
Resources.gif = {};
Resources.gif.frames = function(path) { return cmd(139,path); }
Resources.replpath = function(str, path)
{
if (str[0] === "/")
return str.rm(0);
if (str[0] === "@")
return os.prefpath() + "/" + str.rm(0);
if (!path) return str;
var stem = path.dir();
while (stem) {
var tr = stem + "/" +str;
if (io.exists(tr)) return tr;
stem = stem.updir();
}
return str;
}
Resources.replstrs = function(path)
{
var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
script = script.replace(regexp,function(str) {
var newstr = Resources.replpath(str.trimchr('"'), path);
return `"${newstr}"`;
});
return script;
}
var console = {
print(msg, lvl) {
var lg;
if (typeof msg === 'object') {
lg = JSON.stringify(msg, null, 2);
} else {
lg = msg;
}
var stack = (new Error()).stack;
var n = stack.next('\n',0)+1;
n = stack.next('\n', n)+1;
var nnn = stack.slice(n);
var fmatch = nnn.match(/\(.*\:/);
var file = fmatch ? fmatch[0].shift(1).shift(-1) : "nofile";
var lmatch = nnn.match(/\:\d*\)/);
var line = lmatch ? lmatch[0].shift(1).shift(-1) : "0";
yughlog(lvl, lg, file, line);
},
spam(msg) {
this.print(msg,0);
},
/* this always prints to stdout */
debug(msg) {
this.print(msg,1);
},
info(msg) {
this.print(msg, 2);
},
warn(msg) {
this.print(msg, 3);
},
error(msg) {
this.print(msg + "\n" + console.stackstr(3), 4);
},
panic(msg) {
this.print(msg + "\n" + console.stackstr(1), 5);
},
log(msg) {
if (typeof msg === 'object') msg = JSON.stringify(msg,null,1);
cmd(91, msg + '\n');
},
stackstr(skip=0) {
var err = new Error();
var stack = err.stack.split('\n');
return stack.slice(skip,stack.length-10).join('\n');
},
stack(skip = 0) {
console.log(stackstr(skip+1));
},
};
var say = console.log;
say.doc = "Print to std out with an appended newline.";
console.doc = {
level: "Set level to output logging to console.",
info: "Output info level message.",
warn: "Output warn level message.",
error: "Output error level message, and print stacktrace.",
critical: "Output critical level message, and exit game immediately.",
write: "Write raw text to console.",
say: "Write raw text to console, plus a newline.",
stack: "Output a stacktrace to console.",
console: "Output directly to in game console.",
clear: "Clear console."
};
Resources.gif.frames = function(path) { return render.gif_frames(path); }
/*
io path rules. Starts with, meaning:
@ -265,12 +138,6 @@ io.mixin({
paths = paths.filter(function(str) { return str.ext() === ext; });
return paths;
},
compile(script) {
return cmd(260, script);
},
run_bytecode(byte_file) {
return cmd(261, byte_file);
},
glob(pat) {
var paths = io.ls('.');
@ -311,10 +178,9 @@ Cmdline.register_cmd = function(flag, fn, doc) {
});
};
Cmdline.register_order = function(order, fn, doc, usage) {
Cmdline.register_order = function(order, fn, doc, usage = "") {
Cmdline.orders[order] = fn;
fn.doc = doc;
usage ??= "";
fn.usage = `${order} ${usage}`;
}
@ -324,24 +190,31 @@ Cmdline.register_order("edit", function() {
return;
}
Game.engine_start(function() {
window.size = [1280, 720];
window.mode = window.modetypes.full;
sim.pause();
game.engine_start(function() {
global.mixin("scripts/editor.js");
use("editorconfig.js");
use("config.js");
render.set_font("fonts/c64.ttf", 8);
editor.enter_editor();
});
}, "Edit the project in this folder. Give it the name of an UR to edit that specific object.", "?UR?");
Cmdline.register_order("init", function() {
say('top of init');
if (io.exists(projectfile)) {
say("Already a game here.");
return;
}
say('top of ls');
if (!(io.ls().length === 0)) {
say("Directory is not empty. Make an empty one and init there.");
return;
}
say('top of mkdir');
io.mkdir(".prosperon");
var project = {};
project.version = prosperon.version;
@ -358,7 +231,7 @@ Cmdline.register_order("play", function(argv) {
if (argv[0])
io.chdir(argv[0]);
Game.loadurs();
game.loadurs();
if (!io.exists(projectfile)) {
say("No game to play. Try making one with 'prosperon init'.");
@ -366,18 +239,19 @@ Cmdline.register_order("play", function(argv) {
}
var project = json.decode(io.slurp(projectfile));
Game.title = project.title;
Window.mode = Window.modetypes.expand;
game.title = project.title;
window.mode = window.modetypes.expand;
global.mixin("config.js");
if (project.title) Window.title = project.title;
if (project.title) window.title = project.title;
if (Window.rendersize.equal([0,0])) Window.rendersize = Window.size;
console.info(`Starting game with window size ${Window.size} and render ${Window.rendersize}.`);
if (window.rendersize.equal([0,0])) window.rendersize = window.size;
console.info(`Starting game with window size ${window.size} and render ${window.rendersize}.`);
Game.engine_start(function() {
global.mixin("scripts/sound.js");
global.game = actor.spawn("game.js");
if (project.icon) Window.icon(project.icon);
game.engine_start(function() {
render.set_font("fonts/c64.ttf", 8);
global.app = actor.spawn("game.js");
if (project.icon) window.set_icon(game.texture(project.icon));
game.camera = world.spawn("scripts/camera2d");
});
}, "Play the game present in this folder.");
@ -393,7 +267,7 @@ Cmdline.register_order("pack", function(str) {
say(`Packing into ${packname}`);
cmd(124, packname);
// io.pack_engine(packname);
io.chmod(packname, 666);
}, "Pack the game into the given name.", "NAME");
@ -414,7 +288,7 @@ Cmdline.register_order("qoa", function(argv) {
for (var file of argv) {
if (!sounds.includes(file.ext())) continue;
say(`converting ${file}`);
cmd(262,file);
io.save_qoa(file);
}
}, "Convert file(s) to qoa.");
@ -433,7 +307,7 @@ Cmdline.register_order("about", function(argv) {
}, "Get information about this game.");
Cmdline.register_order("ur", function(argv) {
Game.loadurs();
game.loadurs();
for (var i of ur._list.sort()) say(i);
}, "Get information about the ur types in your game.");
@ -500,23 +374,16 @@ Cmdline.register_order("api", function(obj) {
return;
}
load("scripts/editor.js");
var api = Debug.api.print_doc(obj[0]);
use("scripts/editor.js");
var api = debug.api.print_doc(obj[0]);
if (!api)
return;
say(api);
}, "Print the API for an object as markdown. Give it a file to save the output to.", "OBJECT");
Cmdline.register_order("compile", function(argv) {
for (var file of argv) {
var comp = io.compile(file);
io.slurpwrite(file + "c", comp);
}
}, "Compile one or more provided files into bytecode.", "FILE ...");
Cmdline.register_order("input", function(pawn) {
load("scripts/editor.js");
use("scripts/editor.js");
say(`## Input for ${pawn}`);
eval(`say(input.print_md_kbm(${pawn}));`);
}, "Print input documentation for a given object as markdown. Give it a file to save the output to", "OBJECT ?FILE?");
@ -528,17 +395,7 @@ Cmdline.register_order("run", function(script) {
return;
}
if (io.exists(script))
try {
if (script.endswith("c"))
cmd(261, script);
else
load(script);
} catch(e) { }
else {
var ret = eval(script);
if (ret) say(ret);
}
say(use(script));
}, "Run a given script. SCRIPT can be the script itself, or a file containing the script", "SCRIPT");
Cmdline.orders.script = Cmdline.orders.run;
@ -592,21 +449,24 @@ function cmd_args(cmdargs)
}
Cmdline.orders[cmds[0]](cmds.slice(1));
if (!game.startengine)
os.exit(0);
}
Cmdline.register_order("clean", function(argv) {
say("Cleaning not implemented.");
}, "Clean up a given object file.", "JSON ...");
Cmdline.register_order("test", function(argv) {
use("scripts/test.js");
}, "Run tests.");
Cmdline.register_cmd("l", function(n) {
console.level = n;
}, "Set log level.");
return {
console,
Resources,
say,
Cmdline,
cmd_args,
steam

38
scripts/test.js Normal file
View file

@ -0,0 +1,38 @@
/* Tests for prosperon */
var tests = [];
var pass = 0;
var fail = 0;
var failed = [];
var test = function(name, fn)
{
var func = function() {
print(`${pass+fail+1}/${tests.length}: ${name} ... `);
var p = profile.now();
var b = fn();
p = profile.lap(p);
print(`${b ? "pass" : "fail"} [${p}]`);
return b;
};
func.testname = name;
tests.push(func);
}
say(`Testing ${tests.length} tests.`);
for (var t of tests) {
if (t())
pass++;
else {
fail++;
failed.push(t.testname);
}
print("\n");
}
say(`Passed ${pass} tests and failed ${fail} [${(pass*100/(pass+fail)).toPrecision(4)}%].`);
say(`Failed tests are:`);
for (var f of failed)
say(f);
os.quit();

View file

@ -103,9 +103,24 @@ Ease.elastic = {
Ease.elastic.c4 = 2*Math.PI/3;
Ease.elastic.c5 = 2*Math.PI / 4.5;
var tween = function(obj, val, to, time)
{
var start = profile.secs(profile.now());
var startval = obj[val];
var update = function(dt) {
var elapsed = profile.secs(profile.now()) - start;
obj[val] = startval.lerp(to, elapsed/time);
if (elapsed >= time) {
obj[val] = to;
stop();
}
};
var stop = Register.update.register(update);
}
var Tween = {
default: {
loop: "restart",
loop: "hold",
/*
loop types
none: when done, return to first value
@ -142,12 +157,12 @@ var Tween = {
if (defn.accum >= defn.time && defn.loop === 'hold') {
if (typeof target === 'string')
obj[target] = tvals[tvals.length-1];
else
target(tvals[tvals.length-1]);
else
target(tvals[tvals.length-1]);
defn.pause();
defn.cb.call(obj);
return;
defn.cb.call(obj);
return;
}
defn.pct = (defn.accum % defn.time) / defn.time;
@ -197,4 +212,4 @@ var Tween = {
Tween.make = Tween.start;
return {Tween, Ease};
return {Tween, Ease, tween};

View file

@ -1,6 +1,5 @@
#include "2dphysics.h"
#include "debug.h"
#include "gameobject.h"
#include <string.h>
@ -29,13 +28,13 @@ struct rgba dynamic_color = {255,70,46,255};
struct rgba kinematic_color = {255, 194, 64, 255};
struct rgba static_color = {73,209,80,255};
static JSValue fns[100];
static JSValue hits[100];
static int cb_idx = 0;
static const unsigned char col_alpha = 40;
static const float sensor_seg = 10;
unsigned int category_masks[32];
void set_cat_mask(int cat, unsigned int mask) { category_masks[cat] = mask; }
cpTransform m3_to_cpt(HMM_Mat3 m)
{
cpTransform t;
@ -49,13 +48,7 @@ cpTransform m3_to_cpt(HMM_Mat3 m)
}
cpShape *phys2d_query_pos(cpVect pos) {
cpShapeFilter filter;
filter.group = CP_NO_GROUP;
filter.mask = CP_ALL_CATEGORIES;
filter.categories = CP_ALL_CATEGORIES;
cpShape *find = cpSpacePointQueryNearest(space, pos, 0.f, filter, NULL);
return find;
return cpSpacePointQueryNearest(space, pos, 0.f, CP_SHAPE_FILTER_ALL, NULL);
}
@ -70,17 +63,11 @@ void bbhit(cpShape *shape, int *data)
qhit++;
}
static cpShapeFilter ff = {
.group = CP_NO_GROUP,
.mask = CP_ALL_CATEGORIES,
.categories = CP_ALL_CATEGORIES,
};
int query_point(HMM_Vec2 pos)
{
qhit = 0;
// cpSpacePointQuery(space, pos.cp, 0, filter, qpoint, &qhit);
cpSpaceBBQuery(space, cpBBNewForCircle(pos.cp, 2), ff, bbhit, &qhit);
cpSpaceBBQuery(space, cpBBNewForCircle(pos.cp, 2), CP_SHAPE_FILTER_ALL, bbhit, &qhit);
return qhit;
}
@ -104,65 +91,6 @@ gameobject **clean_ids(gameobject **ids)
return ids;
}
typedef struct querybox {
cpBB bb;
gameobject **ids;
} querybox;
void querylist(cpShape *shape, cpContactPointSet *points, querybox *qb) {
arrput(qb->ids, shape2go(shape));
}
void querylistbodies(cpBody *body, querybox *qb) {
if (cpBBContainsVect(qb->bb, cpBodyGetPosition(body)))
arrput(qb->ids,body2go(body));
}
/* Return all points from a list of points in the given boundingbox */
int *phys2d_query_box_points(HMM_Vec2 pos, HMM_Vec2 wh, HMM_Vec2 *points, int n) {
cpBB bbox;
bbox = cpBBExpand(bbox, cpvadd(pos.cp, cpvmult(wh.cp,0.5)));
bbox = cpBBExpand(bbox, cpvsub(pos.cp, cpvmult(wh.cp,0.5)));
int *hits = NULL;
for (int i = 0; i < n; i++)
if (cpBBContainsVect(bbox, points[i].cp))
arrpush(hits, i);
return hits;
}
/* Return all gameobjects within the given box */
gameobject **phys2d_query_box(HMM_Vec2 pos, HMM_Vec2 wh) {
cpShape *box = cpBoxShapeNew(NULL, wh.x, wh.y, 0.f);
cpTransform T = {0};
T.a = 1;
T.d = 1;
T.tx = pos.x;
T.ty = pos.y;
cpShapeUpdate(box, T);
cpBB bbox = cpShapeGetBB(box);
querybox qb;
qb.bb = bbox;
qb.ids = NULL;
cpSpaceShapeQuery(space, box, querylist, &qb);
cpSpaceEachBody(space, querylistbodies, &qb);
cpShapeFree(box);
return clean_ids(qb.ids);
}
gameobject **phys2d_query_shape(struct phys2d_shape *shape)
{
gameobject **ids = NULL;
cpSpaceShapeQuery(space, shape->shape, querylist, ids);
return clean_ids(ids);
}
int cpshape_enabled(cpShape *c) {
cpShapeFilter filter = cpShapeGetFilter(c);
if (filter.categories == ~CP_ALL_CATEGORIES && filter.mask == ~CP_ALL_CATEGORIES)
@ -227,7 +155,7 @@ void constraint_break(constraint *constraint)
cpSpaceRemoveConstraint(space, constraint->c);
cpConstraintFree(constraint->c);
constraint->c = NULL;
script_call_sym(constraint->break_cb);
script_call_sym(constraint->break_cb,0,NULL);
}
void constraint_free(constraint *constraint)
@ -248,6 +176,7 @@ void constraint_test(cpConstraint *constraint, float *dt)
void phys2d_update(float deltaT) {
cpSpaceStep(space, deltaT);
cpSpaceEachConstraint(space, constraint_test, &deltaT);
cb_idx = 0;
}
void init_phys2dshape(struct phys2d_shape *shape, gameobject *go, void *data) {
@ -288,9 +217,8 @@ float phys2d_circle_moi(struct phys2d_circle *c) {
return cpMomentForCircle(m, 0, cpCircleShapeGetRadius(c->shape.shape), cpCircleShapeGetOffset(c->shape.shape));
}
void phys2d_circledel(struct phys2d_circle *c) {
phys2d_shape_del(&c->shape);
}
void phys2d_circledel(struct phys2d_circle *c) { phys2d_shape_del(&c->shape); }
void circle2d_free(circle2d *c) { phys2d_circledel(c); }
void phys2d_dbgdrawcpcirc(cpShape *c) {
HMM_Vec2 pos = mat_t_pos(t_go2world(shape2go(c)), (HMM_Vec2)cpCircleShapeGetOffset(c));
@ -311,6 +239,7 @@ void phys2d_shape_apply(struct phys2d_shape *s)
float newmoi = s->moi(s->data);
moment-=moi;
moment += newmoi;
if (moment < 0) moment = 0;
cpBodySetMoment(s->go->body, moment);
}
@ -352,6 +281,10 @@ void phys2d_poly_free(struct phys2d_poly *poly)
float phys2d_poly_moi(struct phys2d_poly *poly) {
float m = poly->shape.go->mass;
int len = cpPolyShapeGetCount(poly->shape.shape);
if (!len) {
YughWarn("Cannot evaluate the MOI of a polygon of length %d.", len);
return 0;
}
cpVect points[len];
for (int i = 0; i < len; i++)
points[i] = cpPolyShapeGetVert(poly->shape.shape, i);
@ -565,10 +498,13 @@ void phys2d_dbgdrawedge(struct phys2d_edge *edge) {
/************ COLLIDER ****************/
void shape_enabled(struct phys2d_shape *shape, int enabled) {
if (enabled)
cpShapeSetFilter(shape->shape, CP_SHAPE_FILTER_ALL);
else
cpShapeSetFilter(shape->shape, CP_SHAPE_FILTER_NONE);
cpShapeFilter set = enabled ? CP_SHAPE_FILTER_ALL : CP_SHAPE_FILTER_NONE;
if (!shape->shape) {
struct phys2d_edge *edge = shape->data;
for (int i = 0; i < arrlen(edge->shapes[i]); i++)
cpShapeSetFilter(edge->shapes[i], set);
} else
cpShapeSetFilter(shape->shape, set);
}
int shape_is_enabled(struct phys2d_shape *shape) {
@ -592,7 +528,6 @@ int shape_get_sensor(struct phys2d_shape *shape) {
if (!shape->shape) {
struct phys2d_edge *edge = shape->data;
if (arrlen(edge->shapes) > 0) return cpShapeGetSensor(edge->shapes[0]);
YughInfo("Attempted to get the sensor of an edge with no shapes. It has %d points.", arrlen(edge->points));
return 0;
}
@ -601,33 +536,24 @@ int shape_get_sensor(struct phys2d_shape *shape) {
void phys2d_reindex_body(cpBody *body) { cpSpaceReindexShapesForBody(space, body); }
int arb_valid(cpArbiter *arb)
{
cpBody *body1;
cpBody *body2;
cpArbiterGetBodies(arb, &body1, &body2);
gameobject *go2 = cpBodyGetUserData(body2);
return !JS_IsUndefined(go2->ref);
}
JSValue arb2js(cpArbiter *arb)
{
cpBody *body1;
cpBody *body2;
cpArbiterGetBodies(arb, &body1, &body2);
gameobject *go2 = cpBodyGetUserData(body2);
if (JS_IsUndefined(go2->ref)) return JS_UNDEFINED;
cpShape *shape1;
cpShape *shape2;
cpArbiterGetShapes(arb, &shape1, &shape2);
HMM_Vec2 norm;
norm.cp = cpArbiterGetNormal(arb);
struct phys2d_shape *pshape = cpShapeGetUserData(shape2);
gameobject *go2 = cpBodyGetUserData(body2);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "normal", vec22js(norm));
JS_SetPropertyStr(js, obj, "normal", vec22js((HMM_Vec2)cpArbiterGetNormal(arb)));
JS_SetPropertyStr(js, obj, "obj", JS_DupValue(js,go2->ref));
JS_SetPropertyStr(js, obj, "sensor", JS_NewBool(js, cpShapeGetSensor(shape2)));
JS_SetPropertyStr(js, obj, "shape", JS_DupValue(js, pshape->ref));
// JS_SetPropertyStr(js, obj, "point", vec22js((HMM_Vec2)cpArbiterGetPointA(arb, 0)));
HMM_Vec2 srfv;
srfv.cp = cpArbiterGetSurfaceVelocity(arb);
@ -638,73 +564,51 @@ JSValue arb2js(cpArbiter *arb)
void phys_run_post(cpSpace *space, JSValue *fn, JSValue *hit)
{
script_call_fn_arg(*fn, *hit);
script_call_sym(*fn, 1, hit);
JS_FreeValue(js, *hit);
JS_FreeValue(js, *fn);
}
/* TODO: Limitation, cannot handle multiple collision same frame */
int script_phys_cb_begin(cpArbiter *arb, cpSpace *space, gameobject *go)
void register_hit(cpArbiter *arb, gameobject *go, const char *name)
{
if (!arb_valid(arb)) return 1;
if (!JS_IsUndefined(go->cbs.begin) && cpSpaceAddPostStepCallback(space, phys_run_post, &go->cbs.begin, &go->cbs.bhit))
go->cbs.bhit = arb2js(arb);
cpShape *shape1;
cpShape *shape2;
cpArbiterGetShapes(arb, &shape1, &shape2);
struct phys2d_shape *pshape1 = cpShapeGetUserData(shape1);
for (int i = 0; i < arrlen(go->shape_cbs); i++) {
if (go->shape_cbs[i].shape != pshape1) continue;
if (!JS_IsUndefined(go->shape_cbs[i].cbs.begin) && cpSpaceAddPostStepCallback(space, phys_run_post, &go->shape_cbs[i].cbs.begin, &go->shape_cbs[i].cbs.bhit))
go->shape_cbs[i].cbs.bhit = arb2js(arb);
}
return 1;
}
void script_phys_cb_separate(cpArbiter *arb, cpSpace *space, gameobject *go)
{
if (!arb_valid(arb)) return;
if (JS_IsUndefined(go->cbs.separate)) return;
go->cbs.shit = arb2js(arb);
cpSpaceAddPostStepCallback(space, phys_run_post, &go->cbs.separate, &go->cbs.shit);
}
void phys2d_rm_go_handlers(gameobject *go) {
cpCollisionHandler *handler = cpSpaceAddWildcardHandler(space, (cpCollisionType)go);
if (!JS_IsUndefined(go->cbs.begin)) {
JS_FreeValue(js,go->cbs.begin);
go->cbs.begin = JS_UNDEFINED;
}
if (JS_IsFunction(js,go->cbs.separate)) {
JS_FreeValue(js,go->cbs.separate);
go->cbs.separate = JS_UNDEFINED;
if (JS_IsUndefined(go->ref)) return;
JSValue cb = JS_GetPropertyStr(js, go->ref, name);
if (!JS_IsUndefined(cb)) {
JSValue jarb = arb2js(arb);
fns[cb_idx] = JS_DupValue(js, cb);
hits[cb_idx] = jarb;
cpSpaceAddPostStepCallback(space, phys_run_post, &fns[cb_idx], &hits[cb_idx]);
cb_idx++;
}
for (int i = 0; i < arrlen(go->shape_cbs); i++) {
JS_FreeValue(js, go->shape_cbs[i].cbs.begin);
go->shape_cbs[i].cbs.begin = JS_UNDEFINED;
cpShape *s1, *s2;
cpArbiterGetShapes(arb, &s1, &s2);
gameobject *g1, *g2;
g1 = shape2go(s1);
g2 = shape2go(g2);
if (!g1) return;
if (!g2) return;
if (JS_IsUndefined(g1->ref)) return;
if (JS_IsUndefined(g2->ref)) return;
struct phys2d_shape *pshape1 = cpShapeGetUserData(s1);
if (JS_IsUndefined(pshape1->ref)) return;
cb = JS_GetPropertyStr(js, pshape1->ref, name);
if (!JS_IsUndefined(cb)) {
JSValue jarb = arb2js(arb);
fns[cb_idx] = JS_DupValue(js,cb);
hits[cb_idx] = jarb;
cpSpaceAddPostStepCallback(space, phys_run_post, &fns[cb_idx], &hits[cb_idx]);
cb_idx++;
}
}
int script_phys_cb_begin(cpArbiter *arb, cpSpace *space, gameobject *go) { register_hit(arb, go, "collide"); return 1; }
void script_phys_cb_separate(cpArbiter *arb, cpSpace *space, gameobject *go) { register_hit(arb, go, "separate"); }
void phys2d_setup_handlers(gameobject *go) {
cpCollisionHandler *handler = cpSpaceAddWildcardHandler(space, (cpCollisionType)go);
handler->userData = go;
handler->beginFunc = script_phys_cb_begin;
handler->separateFunc = script_phys_cb_separate;
}
static int airborne = 0;
void inair(cpBody *body, cpArbiter *arbiter, void *data) {
airborne = 0;
}
int phys2d_in_air(cpBody *body) {
airborne = 1;
cpBodyEachArbiter(body, inair, NULL);
return airborne;
}

View file

@ -29,6 +29,7 @@ void constraint_free(constraint *constraint);
struct phys2d_shape {
cpShape *shape; /* user data is this phys2d_shape */
JSValue ref;
transform2d t;
gameobject *go;
void *data; /* The specific subtype; phys2d_circle, etc */
@ -47,6 +48,8 @@ struct phys2d_circle {
struct phys2d_shape shape;
};
typedef struct phys2d_circle circle2d;
/* A convex polygon; defined as the convex hull around the given set of points */
struct phys2d_poly {
HMM_Vec2 *points;
@ -66,6 +69,7 @@ struct phys2d_edge {
struct phys2d_circle *Make2DCircle(gameobject *go);
void phys2d_circledel(struct phys2d_circle *c);
void circle2d_free(circle2d *c);
void phys2d_applycircle(struct phys2d_circle *circle);
void phys2d_dbgdrawcircle(struct phys2d_circle *circle);
float phys2d_circle_moi(struct phys2d_circle *c);
@ -99,7 +103,7 @@ void phys2d_edge_set_enabled(struct phys2d_edge *edge, int enabled);
void phys2d_init();
void phys2d_update(float deltaT);
cpShape *phys2d_query_pos(cpVect pos);
gameobject **phys2d_query_box(HMM_Vec2 pos, HMM_Vec2 wh);
void phys2d_query_ray(HMM_Vec2 start, HMM_Vec2 end, float radius, cpShapeFilter filter, JSValue cb);
struct shape_cb {
struct phys2d_shape *shape;
@ -108,7 +112,6 @@ struct shape_cb {
void fire_hits();
void phys2d_rm_go_handlers(gameobject *go);
void phys2d_set_gravity(HMM_Vec2 v);
void shape_enabled(struct phys2d_shape *shape, int enabled);
@ -120,14 +123,8 @@ struct rgba shape_color_s(cpShape *shape);
void shape_gui(struct phys2d_shape *shape);
void phys2d_setup_handlers(gameobject *go);
gameobject **phys2d_query_shape(struct phys2d_shape *shape);
int *phys2d_query_box_points(HMM_Vec2 pos, HMM_Vec2 wh, HMM_Vec2 *points, int n);
int query_point(HMM_Vec2 pos);
void flush_collide_cbs();
void phys2d_reindex_body(cpBody *body);
extern unsigned int category_masks[32];
void set_cat_mask(int cat, unsigned int mask);
int phys2d_in_air(cpBody *body);
#endif

View file

@ -167,7 +167,9 @@ sg_buffer texcoord_floats(float *f, int verts, int comp)
return sg_make_buffer(&(sg_buffer_desc){
.data.ptr = packed,
.data.size = sizeof(unsigned short) * verts});
.data.size = sizeof(unsigned short) * verts,
.label = "tex coord vert buffer",
});
}
sg_buffer normal_floats(float *f, int verts, int comp)
@ -178,7 +180,9 @@ sg_buffer normal_floats(float *f, int verts, int comp)
return sg_make_buffer(&(sg_buffer_desc){
.data.ptr = packed_norms,
.data.size = sizeof(uint32_t) * verts});
.data.size = sizeof(uint32_t) * verts,
.label = "normal vert buffer",
});
}
HMM_Vec3 index_to_vert(uint32_t idx, float *f)
@ -195,9 +199,11 @@ void mesh_add_primitive(mesh *mesh, cgltf_primitive *prim)
memcpy(idxs, cgltf_buffer_view_data(prim->indices->buffer_view), sizeof(uint16_t) * c);
mesh->bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = idxs,
.data.size = sizeof(uint16_t) * c,
.type = SG_BUFFERTYPE_INDEXBUFFER});
.data.ptr = idxs,
.data.size = sizeof(uint16_t) * c,
.type = SG_BUFFERTYPE_INDEXBUFFER,
.label = "mesh index buffer",
});
mesh->idx_count = c;
} else {
@ -232,8 +238,10 @@ void mesh_add_primitive(mesh *mesh, cgltf_primitive *prim)
switch (attribute.type) {
case cgltf_attribute_type_position:
mesh->bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = vs,
.data.size = sizeof(float) * n});
.data.ptr = vs,
.data.size = sizeof(float) * n,
.label = "mesh vert buffer"
});
break;
case cgltf_attribute_type_normal:
@ -369,11 +377,10 @@ struct model *MakeModel(const char *path)
/* eye position */
HMM_Vec3 eye = {0,0,100};
void draw_model(struct model *model, HMM_Mat4 amodel) {
HMM_Mat4 proj = projection;
void draw_model(struct model *model, HMM_Mat4 amodel, HMM_Mat4 *proj) {
HMM_Vec3 center = {0.f, 0.f, 0.f};
HMM_Mat4 view = HMM_LookAt_RH(eye, center, vUP);
HMM_Mat4 vp = HMM_MulM4(proj, view);
HMM_Mat4 vp = HMM_MulM4(*proj, view);
HMM_Vec3 dir_dir = HMM_NormV3(HMM_SubV3(center, dirl_pos));
@ -411,10 +418,10 @@ void draw_drawmodel(struct drawmodel *dm)
if (!dm->model) return;
struct gameobject *go = dm->go;
HMM_Mat4 rst = t3d_go2world(go);
draw_model(dm->model, rst);
draw_model(dm->model, rst, &useproj);
}
void free_drawmodel(struct drawmodel *dm) {
void drawmodel_free(struct drawmodel *dm) {
int rm;
for (int i = 0; i < arrlen(models); i++)
if (models[i] == dm) {

View file

@ -28,11 +28,11 @@ typedef struct model {
} model;
/* A model with draw information */
struct drawmodel {
typedef struct drawmodel {
struct model *model;
HMM_Mat4 amodel;
gameobject *go;
};
} drawmodel;
typedef struct bone {
transform3d t;
@ -53,7 +53,7 @@ void model_init();
struct drawmodel *make_drawmodel(gameobject *go);
void draw_drawmodel(struct drawmodel *dm);
void model_draw_all();
void free_drawmodel(struct drawmodel *dm);
void drawmodel_free(struct drawmodel *dm);
material *material_make();
void material_free(material *mat);

View file

@ -2,7 +2,6 @@
#define SOKOL_TRACE_HOOKS
#define SOKOL_IMPL
#define SOKOL_NO_ENTRY
#include "sokol/sokol_audio.h"
#include "sokol/sokol_time.h"
#include "sokol/sokol_args.h"

View file

@ -1,45 +0,0 @@
#include "debug.h"
#include "stb_ds.h"
#include "log.h"
#include "sokol/sokol_time.h"
unsigned long long triCount = 0;
void prof_start(struct d_prof *prof)
{
arrsetlen(prof->ms, prof->laps);
}
void prof_reset(struct d_prof *prof)
{
prof->lap = stm_now();
}
float prof_lap_avg(struct d_prof *prof)
{
float avg;
for (int i = 0; i < arrlen(prof->ms); i++)
avg += prof->ms[i];
avg /= arrlen(prof->ms);
return avg;
}
void prof_lap(struct d_prof *prof)
{
if (prof->ms == NULL)
prof_start(prof);
if (arrlen(prof->ms) >= prof->laps) {
YughWarn("Profiled time of %s is avg %g", prof->name, prof_lap_avg(prof));
arrsetlen(prof->ms, 0);
}
uint64_t t = stm_since(prof->lap);
arrput(prof->ms, stm_sec(t));
}
void resetTriangles()
{
triCount = 0;
}

View file

@ -1,20 +0,0 @@
#ifndef DEBUG_GUI_H
#define DEBUG_GUI_H
#include <stdint.h>
struct d_prof {
char *name;
float *ms;
uint64_t lap;
int laps;
};
extern unsigned long long triCount;
void resetTriangles();
void prof_start(struct d_prof *prof);
void prof_reset(struct d_prof *prof);
void prof_lap(struct d_prof *prof);
float prof_lap_avg(struct d_prof *prof);
#endif

View file

@ -4,7 +4,6 @@
#include "yugine.h"
#include "log.h"
#include <assert.h>
#include "debug.h"
#include "window.h"
#include "2dphysics.h"
#include "stb_ds.h"
@ -21,7 +20,7 @@
#include "font.h"
#define v_amt 5000
#define v_amt 500000
struct flush {
sg_shader shader;
@ -110,6 +109,26 @@ struct circle_vertex {
float fill;
};
void debug_nextpass()
{
point_sc = point_c;
point_c = 0;
circle_sc = circle_count;
circle_count = 0;
line_sv = line_v;
line_v = 0;
line_sc = line_c;
line_c = 0;
poly_sc = poly_c;
poly_c = 0;
poly_sv = poly_v;
poly_v = 0;
}
/* Writes debug data to buffers, and draws */
void debug_flush(HMM_Mat4 *view)
{
@ -143,26 +162,8 @@ void debug_flush(HMM_Mat4 *view)
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*view));
sg_draw(circle_sc,4,circle_count);
}
}
void debug_nextpass()
{
point_sc = point_c;
point_c = 0;
circle_sc = circle_count;
circle_count = 0;
line_sv = line_v;
line_v = 0;
line_sc = line_c;
line_c = 0;
poly_sc = poly_c;
poly_c = 0;
poly_sv = poly_v;
poly_v = 0;
debug_nextpass();
}
void debug_newframe()
@ -175,7 +176,7 @@ void debug_newframe()
}
static sg_shader_uniform_block_desc projection_ubo = {
.size = sizeof(projection),
.size = sizeof(useproj),
.uniforms = {
[0] = { .name = "proj", .type = SG_UNIFORMTYPE_MAT4 },
}
@ -197,8 +198,8 @@ void debugdraw_init()
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
[1].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
[2].format = SG_VERTEXFORMAT_FLOAT /* radius */
[1].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
[2].format = SG_VERTEXFORMAT_FLOAT /* radius */
}
},
.primitive_type = SG_PRIMITIVETYPE_POINTS,
@ -208,7 +209,8 @@ void debugdraw_init()
point_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(struct point_vertex)*v_amt,
.usage = SG_USAGE_STREAM
.usage = SG_USAGE_STREAM,
.label = "point vertex buffer"
});
line_shader = sg_make_shader(line_shader_desc(sg_query_backend()));
@ -218,10 +220,10 @@ void debugdraw_init()
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
[1].format = SG_VERTEXFORMAT_FLOAT, /* dist */
[2].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
[3].format = SG_VERTEXFORMAT_FLOAT, /* seg length */
[4].format = SG_VERTEXFORMAT_FLOAT /* dashed line speed */
[1].format = SG_VERTEXFORMAT_FLOAT, /* dist */
[2].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
[3].format = SG_VERTEXFORMAT_FLOAT, /* seg length */
[4].format = SG_VERTEXFORMAT_FLOAT /* dashed line speed */
}
},
.primitive_type = SG_PRIMITIVETYPE_LINES,
@ -232,13 +234,15 @@ void debugdraw_init()
line_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(struct line_vert)*v_amt,
.usage = SG_USAGE_STREAM
.usage = SG_USAGE_STREAM,
.label = "line vertex buffer",
});
line_bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(uint16_t)*v_amt,
.usage = SG_USAGE_STREAM,
.type = SG_BUFFERTYPE_INDEXBUFFER
.type = SG_BUFFERTYPE_INDEXBUFFER,
.label = "line index buffer",
});
csg = sg_make_shader(circle_shader_desc(sg_query_backend()));
@ -246,13 +250,13 @@ void debugdraw_init()
.shader = csg,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[0].buffer_index = 1,
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_FLOAT,
[3].format = SG_VERTEXFORMAT_UBYTE4N,
[4].format = SG_VERTEXFORMAT_FLOAT,
[5].format = SG_VERTEXFORMAT_FLOAT
[0].format = SG_VERTEXFORMAT_FLOAT2,
[0].buffer_index = 1,
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_FLOAT,
[3].format = SG_VERTEXFORMAT_UBYTE4N,
[4].format = SG_VERTEXFORMAT_FLOAT,
[5].format = SG_VERTEXFORMAT_FLOAT
},
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE,
},
@ -265,6 +269,7 @@ void debugdraw_init()
circle_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(struct circle_vertex)*v_amt,
.usage = SG_USAGE_STREAM,
.label = "circle vert buffer",
});
float circleverts[8] = {
@ -277,6 +282,7 @@ void debugdraw_init()
circle_bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
.data = (sg_range){.ptr = circleverts, .size = sizeof(float)*8},
.usage = SG_USAGE_IMMUTABLE,
.label = "circle quarter buffer",
});
grid_shader = sg_make_shader(grid_shader_desc(sg_query_backend()));
@ -301,8 +307,8 @@ void debugdraw_init()
.shader = poly_shader,
.layout = {
.attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
[1].format = SG_VERTEXFORMAT_FLOAT2, /* uv */
[2].format = SG_VERTEXFORMAT_UBYTE4N /* color rgba */
[1].format = SG_VERTEXFORMAT_FLOAT2, /* uv */
[2].format = SG_VERTEXFORMAT_UBYTE4N /* color rgba */
}
},
.index_type = SG_INDEXTYPE_UINT32,
@ -314,12 +320,14 @@ void debugdraw_init()
.size = sizeof(struct poly_vertex)*v_amt,
.usage = SG_USAGE_STREAM,
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.label = "poly vert buffer",
});
poly_bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(uint32_t)*6*v_amt,
.usage = SG_USAGE_STREAM,
.type = SG_BUFFERTYPE_INDEXBUFFER
.type = SG_BUFFERTYPE_INDEXBUFFER,
.label = "poly index buffer"
});
}
@ -364,6 +372,7 @@ void draw_line(HMM_Vec2 *points, int n, struct rgba color, float seg_len, float
.size = sizeof(uint16_t)*i_c
};
if (sg_query_buffer_will_overflow(line_bind.vertex_buffers[0], vr.size) || sg_query_buffer_will_overflow(line_bind.index_buffer, ir.size)) return;
sg_append_buffer(line_bind.vertex_buffers[0], &vr);
sg_append_buffer(line_bind.index_buffer, &ir);
@ -427,10 +436,10 @@ HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n)
}
/* Given a strip of points, draws them as segments. So 5 points is 4 segments, and ultimately 8 vertices */
void draw_edge(HMM_Vec2 *points, int n, struct rgba color, int thickness, int flags, struct rgba line_color, float line_seg)
void draw_edge(HMM_Vec2 *points, int n, struct rgba color, float thickness, int flags, struct rgba line_color, float line_seg)
{
int closed = 0;
if (thickness <= 1) {
if (thickness <= 0) {
draw_line(points,n,line_color,0,0);
return;
}
@ -547,8 +556,8 @@ void draw_box(HMM_Vec2 c, HMM_Vec2 wh, struct rgba color)
void draw_grid(float width, float span, struct rgba color)
{
HMM_Vec2 offset = (HMM_Vec2)cam_pos();
offset = HMM_MulV2F(offset, 1/cam_zoom());
HMM_Vec2 offset = campos;
offset = HMM_MulV2F(offset, 1/camzoom);
float ubo[4];
ubo[0] = offset.x;
@ -563,7 +572,7 @@ void draw_grid(float width, float span, struct rgba color)
fs_params_t pt;
pt.thickness = (float)width;
pt.span = span/cam_zoom();
pt.span = span/camzoom;
memcpy(&pt.color, col, sizeof(float)*4);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(ubo));
sg_apply_uniforms(SG_SHADERSTAGE_FS, 0, SG_RANGE_REF(pt));

View file

@ -9,7 +9,7 @@ void draw_cppoint(HMM_Vec2 point, float r, struct rgba color);
void draw_points(HMM_Vec2 *points, int n, float size, struct rgba color);
void draw_line(HMM_Vec2 *points, int n, struct rgba color, float seg_len, float seg_speed);
void draw_edge(HMM_Vec2 *points, int n, struct rgba color, int thickness, int flags, struct rgba line_color, float line_seg);
void draw_edge(HMM_Vec2 *points, int n, struct rgba color, float thickness, int flags, struct rgba line_color, float line_seg);
/* pixels - how many pixels thick, segsize - dashed line seg len */
void draw_circle(HMM_Vec2 c, float radius, float pixels, struct rgba color, float seg);
@ -20,7 +20,6 @@ void draw_grid(float width, float span, struct rgba color);
void debug_flush(HMM_Mat4 *view);
void debug_newframe();
void debug_nextpass();
HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n);

View file

@ -7,22 +7,52 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "yugine.h"
#include "resources.h"
#include "quickjs/quickjs.h"
#include "script.h"
#define ESC "\033["
#define BLACK 30
#define RED 31
#define GREEN 32
#define YELLOW 33
#define BLUE 34
#define MAGENTA 35
#define CYAN 36
#define WHITE 37
#define COLOR(TXT, _C) ESC #_C "m" #TXT ESC "0m"
char *logstr[] = { "spam", "debug", "info", "warn", "error", "panic"};
char *logcolor[] = { COLOR(spam,37), COLOR(debug,32), COLOR(info,36), COLOR(warn,33), COLOR(error,31), COLOR(panic,45) };
char *catstr[] = {"engine", "script", "render"};
static FILE *logout; /* where logs are written to */
static FILE *writeout; /* where console is written to */
static FILE *dump; /* where data is dumped to */
int stdout_lvl = LOG_ERROR;
void log_init()
{
#ifndef NDEBUG
logout = fopen(".prosperon/log.txt", "w");
writeout = fopen(".prosperon/transcript.txt", "w");
if (!fexists(".prosperon")) {
logout = tmpfile();
dump = tmpfile();
writeout = stdout;
}
else {
logout = fopen(".prosperon/log.txt", "w");
writeout = fopen(".prosperon/transcript.txt", "w");
dump = fopen(".prosperon/quickjs.txt", "w");
quickjs_set_dumpout(dump);
}
quickjs_set_dumpout(dump);
#endif
}
@ -30,6 +60,7 @@ void log_shutdown()
{
fclose(logout);
fclose(writeout);
fclose(dump);
}
const char *logfmt = "%s:%d: [%s] %s, %s: ";
@ -49,15 +80,24 @@ void mYughLog(int category, int priority, int line, const char *file, const char
va_end(args);
fprintf(logout, "\n");
if (priority == LOG_DEBUG) {
printf(logfmt, file, line, timebuf, logstr[priority], catstr[category]);
if (priority == LOG_DEBUG || priority >= stdout_lvl) {
printf(logfmt, file, line, timebuf, logcolor[priority], catstr[category]);
va_list args;
va_start(args,message);
vprintf(message, args);
va_end(args);
printf("\n");
fflush(stdout);
}
if (priority >= LOG_PANIC) {
js_stacktrace();
#ifdef __WIN32
DebugBreak();
#else
raise(SIGTRAP);
#endif
}
#endif
}
@ -67,7 +107,6 @@ void log_print(const char *str)
#ifndef NDEBUG
fprintf(writeout, str);
#endif
printf(str);
fflush(stdout);
}

View file

@ -18,6 +18,8 @@
void log_init();
void log_shutdown();
extern int stdout_lvl;
#ifndef NDEBUG
#define YughLog(cat, pri, msg, ...) mYughLog(cat, pri, __LINE__, __FILE__, msg, ##__VA_ARGS__)
#define YughSpam(msg, ...) mYughLog(0, LOG_SPAM, __LINE__, __FILE__, msg, ##__VA_ARGS__);
@ -27,6 +29,7 @@ void log_shutdown();
#define YughCritical(msg, ...) mYughLog(0, LOG_PANIC, __LINE__, __FILE__, msg, ##__VA_ARGS__);
#else
#define YughLog(cat, pri, msg, ...)
#define YughSpam(msg,...)
#define YughInfo(msg, ...)
#define YughWarn(msg, ...)
#define YughError(msg, ...)

View file

@ -17,18 +17,12 @@
#include "stb_image_write.h"
#include "stb_rect_pack.h"
#include "stb_truetype.h"
#include "stb_ds.h"
#include "HandmadeMath.h"
struct sFont *font;
struct sFont *use_font;
static struct {
char *key;
struct sFont *value;
} *fonthash = NULL;
#define max_chars 10000
#define max_chars 100000
static sg_shader fontshader;
static sg_bindings bind_text;
@ -50,15 +44,15 @@ void font_init() {
.shader = fontshader,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2, /* verts */
[0].buffer_index = 1,
[1].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
[2].format = SG_VERTEXFORMAT_FLOAT2, /* width and height */
[3].format = SG_VERTEXFORMAT_USHORT2N, /* uv pos */
[4].format = SG_VERTEXFORMAT_USHORT2N, /* uv width and height */
[5].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
[0].format = SG_VERTEXFORMAT_FLOAT2, /* verts */
[0].buffer_index = 1,
[1].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
[2].format = SG_VERTEXFORMAT_FLOAT2, /* width and height */
[3].format = SG_VERTEXFORMAT_USHORT2N, /* uv pos */
[4].format = SG_VERTEXFORMAT_USHORT2N, /* uv width and height */
[5].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
},
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE
},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
.colors[0].blend = blend_trans,
@ -74,7 +68,8 @@ void font_init() {
bind_text.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(text_verts),
.usage = SG_USAGE_IMMUTABLE
.usage = SG_USAGE_IMMUTABLE,
.label = "text rectangle buffer",
});
bind_text.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
@ -84,31 +79,19 @@ void font_init() {
.label = "text buffer"
});
font_set("fonts/c64.ttf");
bind_text.fs.images[0] = font->texID;
bind_text.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
}
void font_set(const char *path)
void font_free(font *f)
{
if (shlen(fonthash) == 0) sh_new_arena(fonthash);
int index = shgeti(fonthash, path);
if (index != -1) {
if (font == fonthash[index].value) return;
font = fonthash[index].value;
bind_text.fs.images[0] = font->texID;
return;
}
sg_destroy_image(f->texID);
free(f);
}
struct sFont *newfont = MakeFont(path, 8);
if (!newfont) {
YughError("Could not make font from %s.", path);
return;
}
font = newfont;
shput(fonthash, path, newfont);
bind_text.fs.images[0] = font->texID;
void font_set(font *f)
{
use_font = f;
bind_text.fs.images[0] = f->texID;
}
struct sFont *MakeSDFFont(const char *fontfile, int height)
@ -145,6 +128,10 @@ struct sFont *MakeFont(const char *fontfile, int height) {
newfont->height = height;
unsigned char *ttf_buffer = slurp_file(fontfile, NULL);
if (!ttf_buffer) {
YughWarn("Could not find font at %s.");
return NULL;
}
unsigned char *bitmap = malloc(packsize * packsize);
stbtt_packedchar glyphs[95];
@ -163,19 +150,21 @@ struct sFont *MakeFont(const char *fontfile, int height) {
}
stbtt_GetFontVMetrics(&fontinfo, &newfont->ascent, &newfont->descent, &newfont->linegap);
newfont->emscale = stbtt_ScaleForMappingEmToPixels(&fontinfo, 16);
newfont->linegap = (newfont->ascent - newfont->descent) * newfont->emscale;
//newfont->emscale = stbtt_ScaleForMappingEmToPixels(&fontinfo, 16);
newfont->emscale = stbtt_ScaleForPixelHeight(&fontinfo, height);
newfont->linegap = (newfont->ascent - newfont->descent) * newfont->emscale*1.5;
newfont->texID = sg_make_image(&(sg_image_desc){
.type = SG_IMAGETYPE_2D,
.width = packsize,
.height = packsize,
.pixel_format = SG_PIXELFORMAT_R8,
.usage = SG_USAGE_IMMUTABLE,
.data.subimage[0][0] = {
.ptr = bitmap,
.size = packsize * packsize}});
.type = SG_IMAGETYPE_2D,
.width = packsize,
.height = packsize,
.pixel_format = SG_PIXELFORMAT_R8,
.usage = SG_USAGE_IMMUTABLE,
.data.subimage[0][0] = {
.ptr = bitmap,
.size = packsize * packsize
}
});
for (unsigned char c = 32; c < 127; c++) {
stbtt_packedchar glyph = glyphs[c - 32];
@ -208,7 +197,7 @@ static int curchar = 0;
void draw_underline_cursor(HMM_Vec2 pos, float scale, struct rgba color)
{
pos.Y -= 2;
sdrawCharacter(font->Characters['_'], pos, scale, color);
sdrawCharacter(use_font->Characters['_'], pos, scale, color);
}
void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color)
@ -244,7 +233,7 @@ void text_flush(HMM_Mat4 *proj) {
}
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color) {
if (curchar+1 >= max_chars)
if (curchar-10 >= max_chars)
return;
struct rgba colorbox = {0,0,0,255};
@ -270,10 +259,6 @@ void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgb
curchar++;
}
void text_settype(struct sFont *mfont) {
font = mfont;
}
const char *esc_color(const char *c, struct rgba *color, struct rgba defc)
{
struct rgba d;
@ -303,6 +288,7 @@ const char *esc_color(const char *c, struct rgba *color, struct rgba defc)
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking)
{
if (!use_font) return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){0,0});
struct rgba dummy;
HMM_Vec2 cursor = {0,0};
const char *line, *wordstart;
@ -310,10 +296,10 @@ struct boundingbox text_bb(const char *text, float scale, float lw, float tracki
while (*line != '\0') {
if (isblank(*line)) {
cursor.X += font->Characters[*line].Advance * tracking * scale;
cursor.X += use_font->Characters[*line].Advance * tracking * scale;
line++;
} else if (isspace(*line)) {
cursor.Y -= scale * font->linegap;
cursor.Y -= scale * use_font->linegap;
cursor.X = 0;
line++;
} else {
@ -324,26 +310,26 @@ struct boundingbox text_bb(const char *text, float scale, float lw, float tracki
int wordWidth = 0;
while (!isspace(*line) && *line != '\0') {
wordWidth += font->Characters[*line].Advance * tracking * scale;
wordWidth += use_font->Characters[*line].Advance * tracking * scale;
line++;
}
if (lw > 0 && (cursor.X + wordWidth) >= lw) {
cursor.X = 0;
cursor.Y -= scale * font->linegap;
cursor.Y -= scale * use_font->linegap;
}
while (wordstart < line) {
if (*wordstart == '\e')
line = esc_color(wordstart, NULL, dummy);
cursor.X += font->Characters[*wordstart].Advance * tracking * scale;
cursor.X += use_font->Characters[*wordstart].Advance * tracking * scale;
wordstart++;
}
}
}
return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){cursor.X,font->linegap-cursor.Y});
return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){cursor.X,use_font->linegap-cursor.Y});
}
void check_caret(int caret, int l, HMM_Vec2 pos, float scale, struct rgba color)
@ -354,6 +340,11 @@ void check_caret(int caret, int l, HMM_Vec2 pos, float scale, struct rgba color)
/* pos given in screen coordinates */
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking) {
if (!use_font) {
YughError("Cannot render text before a font is set.");
return pos.y;
}
int len = strlen(text);
HMM_Vec2 cursor = pos;
@ -366,13 +357,13 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f
while (*line != '\0') {
if (isblank(*line)) {
sdrawCharacter(font->Characters[*line], cursor, scale, usecolor);
cursor.X += font->Characters[*line].Advance * tracking * scale;
sdrawCharacter(use_font->Characters[*line], cursor, scale, usecolor);
cursor.X += use_font->Characters[*line].Advance * tracking * scale;
line++;
check_caret(caret, line-drawstart, cursor, scale, usecolor);
} else if (isspace(*line)) {
sdrawCharacter(font->Characters[*line], cursor, scale, usecolor);
cursor.Y -= scale * font->linegap;
sdrawCharacter(use_font->Characters[*line], cursor, scale, usecolor);
cursor.Y -= scale * use_font->linegap;
cursor.X = pos.X;
line++;
check_caret(caret, line-drawstart, cursor, scale, usecolor);
@ -385,25 +376,25 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f
while (!isspace(*line) && *line != '\0') {
wordWidth += font->Characters[*line].Advance * tracking * scale;
wordWidth += use_font->Characters[*line].Advance * tracking * scale;
line++;
}
if (lw > 0 && (cursor.X + wordWidth - pos.X) >= lw) {
cursor.X = pos.X;
cursor.Y -= scale * font->linegap;
cursor.Y -= scale * use_font->linegap;
}
while (wordstart < line) {
if (*wordstart == '\e')
wordstart = esc_color(wordstart, &usecolor, color);
if (*wordstart == '\e')
wordstart = esc_color(wordstart, &usecolor, color);
sdrawCharacter(font->Characters[*wordstart], HMM_AddV2(cursor, HMM_MulV2F((HMM_Vec2){1,-1},scale)), scale, (rgba){0,0,0,255});
sdrawCharacter(font->Characters[*wordstart], cursor, scale, usecolor);
sdrawCharacter(use_font->Characters[*wordstart], HMM_AddV2(cursor, HMM_MulV2F((HMM_Vec2){1,-1},scale)), scale, (rgba){0,0,0,255});
sdrawCharacter(use_font->Characters[*wordstart], cursor, scale, usecolor);
cursor.X += font->Characters[*wordstart].Advance * tracking * scale;
cursor.X += use_font->Characters[*wordstart].Advance * tracking * scale;
wordstart++;
check_caret(caret, wordstart-drawstart, cursor, scale, usecolor);
check_caret(caret, wordstart-drawstart, cursor, scale, usecolor);
}
}
}

View file

@ -28,9 +28,13 @@ struct sFont {
sg_image texID;
};
typedef struct sFont font;
void font_free(font *f);
void font_init();
struct sFont *MakeFont(const char *fontfile, int height);
void font_set(const char *path);
void font_set(font *f);
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color);
void text_settype(struct sFont *font);
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking);

View file

@ -8,12 +8,12 @@
#include "stb_ds.h"
static gameobject **gameobjects;
int go_count() { return arrlen(gameobjects); }
gameobject *body2go(cpBody *body) { return cpBodyGetUserData(body); }
gameobject *shape2go(cpShape *shape) { return ((struct phys2d_shape *)cpShapeGetUserData(shape))->go; }
gameobject *shape2go(cpShape *shape) {
struct phys2d_shape *pshape = cpShapeGetUserData(shape);
if (!pshape) return NULL;
return pshape->go;
}
HMM_Vec2 go_pos(gameobject *go)
{
@ -44,23 +44,6 @@ HMM_Mat3 t_world2go(gameobject *go) { return HMM_InvGeneralM3(t_go2world(go)); }
HMM_Mat4 t3d_go2world(gameobject *go) { return transform3d2mat(go2t3(go)); }
HMM_Mat4 t3d_world2go(gameobject *go) { return HMM_InvGeneralM4(t3d_go2world(go)); }
gameobject *pos2gameobject(HMM_Vec2 pos, float give) {
cpShape *hit = phys2d_query_pos(pos.cp);
if (hit)
return shape2go(hit);
for (int i = 0; i < arrlen(gameobjects); i++) {
if (!gameobjects[i]->body) continue;
HMM_Vec2 gpos = go_pos(gameobjects[i]);
float dist = HMM_DistV2(gpos,pos);
if (dist <= give) return gameobjects[i];
}
return NULL;
}
transform2d go2t(gameobject *go)
{
transform2d t;
@ -72,17 +55,15 @@ transform2d go2t(gameobject *go)
return t;
}
unsigned int editor_cat = 1<<31;
void go_shape_apply(cpBody *body, cpShape *shape, gameobject *go) {
cpShapeSetFriction(shape, go->f);
cpShapeSetElasticity(shape, go->e);
cpShapeSetFriction(shape, go->friction);
cpShapeSetElasticity(shape, go->elasticity);
cpShapeSetCollisionType(shape, (cpCollisionType)go);
cpShapeFilter filter;
filter.group = (cpCollisionType)go;
filter.categories = 1<<go->layer | editor_cat;
filter.mask = category_masks[go->layer] | editor_cat;
filter.categories = go->categories;
filter.mask = go->mask;
// filter.mask = CP_ALL_CATEGORIES;
cpShapeSetFilter(shape, filter);
@ -105,18 +86,17 @@ void go_shape_moi(cpBody *body, cpShape *shape, gameobject *go) {
}
void gameobject_apply(gameobject *go) {
cpBodySetType(go->body, go->bodytype);
YughSpam("Applying gameobject %p", go);
cpBodySetType(go->body, go->phys);
cpBodyEachShape(go->body, go_shape_apply, go);
if (go->bodytype == CP_BODY_TYPE_DYNAMIC) {
if (go->phys == CP_BODY_TYPE_DYNAMIC) {
cpBodySetMass(go->body, go->mass);
cpBodySetMoment(go->body, 0.f);
cpBodyEachShape(go->body, go_shape_moi, go);
if (cpBodyGetMoment(go->body) <= 0.f)
cpBodySetMoment(go->body, 1.f);
return;
}
}
@ -124,7 +104,7 @@ static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt
{
gameobject *go = body2go(body);
cpVect pos = cpBodyGetPosition(body);
HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, go->warp_filter).xy;
HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, go->warp_mask).xy;
if (!go) {
cpBodyUpdateVelocity(body,g.cp,damping,dt);
return;
@ -149,40 +129,43 @@ gameobject *MakeGameobject() {
gameobject *ngo = malloc(sizeof(*ngo));
gameobject go = {
.scale = (HMM_Vec3){1.f,1.f,1.f},
.bodytype = CP_BODY_TYPE_STATIC,
.phys = CP_BODY_TYPE_STATIC,
.maxvelocity = INFINITY,
.maxangularvelocity = INFINITY,
.mass = 1.f,
.next = -1,
.drawlayer = 0,
.shape_cbs = NULL,
.damping = INFINITY,
.timescale = 1.0,
.ref = JS_UNDEFINED,
.mask = ~0,
.categories = 1,
.warp_mask = ~0
};
go.cbs.begin = JS_UNDEFINED;
go.cbs.separate = JS_UNDEFINED;
go.body = cpSpaceAddBody(space, cpBodyNew(go.mass, 1.f));
cpBodySetVelocityUpdateFunc(go.body, velocityFn);
*ngo = go;
cpBodySetUserData(go.body, ngo);
phys2d_setup_handlers(ngo);
arrpush(gameobjects, ngo);
return ngo;
}
void rm_body_shapes(cpBody *body, cpShape *shape, void *data) {
struct phys2d_shape *s = cpShapeGetUserData(shape);
if (s) {
JS_FreeValue(js, s->ref);
s->ref = JS_UNDEFINED;
if (s->free)
s->free(s->data);
else
free(s->data);
}
cpShapeSetFilter(shape, CP_SHAPE_FILTER_NONE);
cpSpaceRemoveShape(space, shape);
cpShapeFree(shape);
}
@ -193,22 +176,12 @@ void rm_body_constraints(cpBody *body, cpConstraint *constraint, void *data)
}
void gameobject_free(gameobject *go) {
arrfree(go->shape_cbs);
go->ref = JS_UNDEFINED;
cpBodyEachShape(go->body, rm_body_shapes, NULL);
cpBodyEachConstraint(go->body, rm_body_constraints, NULL);
cpSpaceRemoveBody(space, go->body);
cpBodyFree(go->body);
go->body = NULL;
free(go);
for (int i = arrlen(gameobjects)-1; i >= 0; i--) {
if (gameobjects[i] == go) {
arrdelswap(gameobjects,i);
return;
}
}
}
void gameobject_setangle(gameobject *go, float angle) {
@ -230,12 +203,5 @@ void body_draw_shapes_dbg(cpBody *body, cpShape *shape, void *data) {
void gameobject_draw_debug(gameobject *go) {
if (!go || !go->body) return;
cpVect pos = cpBodyGetPosition(go->body);
cpBodyEachShape(go->body, body_draw_shapes_dbg, NULL);
}
void gameobject_draw_debugs()
{
for (int i = 0; i < arrlen(gameobjects); i++)
gameobject_draw_debug(gameobjects[i]);
}

View file

@ -27,23 +27,21 @@
}while(0)
struct gameobject {
cpBodyType bodytype;
cpBodyType phys;
cpBody *body; /* NULL if this object is dead; has 2d position and rotation, relative to global 0 */
HMM_Vec3 scale; /* local */
int next;
float mass;
float f; /* friction */
float e; /* elasticity */
float friction;
float elasticity;
float damping;
float timescale;
float maxvelocity;
float maxangularvelocity;
unsigned int layer;
cpShapeFilter filter;
unsigned int warp_filter;
// warpmask warpmask;
struct phys_cbs cbs;
struct shape_cb *shape_cbs;
cpBitmask categories;
cpBitmask mask;
unsigned int warp_mask;
JSValue ref;
HMM_Mat4 world;
float drawlayer;
@ -67,7 +65,6 @@ struct gameobject {
typedef struct gameobject gameobject;
gameobject *MakeGameobject();
int go_count();
void gameobject_apply(gameobject *go);
void gameobject_free(gameobject *go);
@ -93,8 +90,6 @@ gameobject *shape2go(cpShape *shape);
void go_shape_apply(cpBody *body, cpShape *shape, gameobject *go);
/* Tries a few methods to select a gameobject; if none is selected returns -1 */
gameobject *pos2gameobject(HMM_Vec2 pos, float give);
void gameobject_draw_debug(gameobject *go);
void gameobject_draw_debugs();
#endif

299
source/engine/gif_load.h Normal file
View file

@ -0,0 +1,299 @@
#ifndef GIF_LOAD_H
#define GIF_LOAD_H
/** gif_load: A slim, fast and header-only GIF loader written in C.
Original author: hidefromkgb (hidefromkgb@gmail.com)
_________________________________________________________________________
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
_________________________________________________________________________
**/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> /** imports uint8_t, uint16_t and uint32_t **/
#ifndef GIF_MGET
#include <stdlib.h>
#define GIF_MGET(m,s,a,c) m = (uint8_t*)realloc((c)? 0 : m, (c)? s : 0UL);
#endif
#ifndef GIF_BIGE
#define GIF_BIGE 0
#endif
#ifndef GIF_EXTR
#define GIF_EXTR static
#endif
#define _GIF_SWAP(h) ((GIF_BIGE)? ((uint16_t)(h << 8) | (h >> 8)) : h)
#pragma pack(push, 1)
struct GIF_WHDR { /** ======== frame writer info: ======== **/
long xdim, ydim, clrs, /** global dimensions, palette size **/
bkgd, tran, /** background index, transparent index **/
intr, mode, /** interlace flag, frame blending mode **/
frxd, fryd, frxo, fryo, /** current frame dimensions and offset **/
time, ifrm, nfrm; /** delay, frame number, frame count **/
uint8_t *bptr; /** frame pixel indices or metadata **/
struct { /** [==== GIF RGB palette element: ====] **/
uint8_t R, G, B; /** [color values - red, green, blue ] **/
} *cpal; /** current palette **/
};
#pragma pack(pop)
enum {GIF_NONE = 0, GIF_CURR = 1, GIF_BKGD = 2, GIF_PREV = 3};
/** [ internal function, do not use ] **/
static long _GIF_SkipChunk(uint8_t **buff, long size) {
long skip;
for (skip = 2, ++size, ++(*buff); ((size -= skip) > 0) && (skip > 1);
*buff += (skip = 1 + **buff));
return size;
}
/** [ internal function, do not use ] **/
static long _GIF_LoadHeader(unsigned gflg, uint8_t **buff, void **rpal,
unsigned fflg, long *size, long flen) {
if (flen && (!(*buff += flen) || ((*size -= flen) <= 0)))
return -2; /** v--[ 0x80: "palette is present" flag ]--, **/
if (flen && (fflg & 0x80)) { /** local palette has priority | **/
*rpal = *buff; /** [ 3L: 3 uint8_t color channels ]--, | **/
*buff += (flen = 2 << (fflg & 7)) * 3L; /** <--| | **/
return ((*size -= flen * 3L) > 0)? flen : -1; /** <--' | **/
} /** no local palette found, checking for the global one | **/
return (gflg & 0x80)? (2 << (gflg & 7)) : 0; /** <-----' **/
}
/** [ internal function, do not use ] **/
static long _GIF_LoadFrame(uint8_t **buff, long *size,
uint8_t *bptr, uint8_t *blen) {
typedef uint16_t GIF_H;
const long GIF_HLEN = sizeof(GIF_H), /** to rid the scope of sizeof **/
GIF_CLEN = 1 << 12; /** code table length: 4096 items **/
GIF_H accu, mask; /** bit accumulator / bit mask **/
long ctbl, iter, /** last code table index / index string iterator **/
prev, curr, /** codes from the stream: previous / current **/
ctsz, ccsz, /** code table bit sizes: min LZW / current **/
bseq, bszc; /** counters: block sequence / bit size **/
uint32_t *code = (uint32_t*)bptr - GIF_CLEN; /** code table pointer **/
/** preparing initial values **/
if ((--(*size) <= GIF_HLEN) || !*++(*buff))
return -4; /** unexpected end of the stream: insufficient size **/
mask = (GIF_H)((1 << (ccsz = (ctsz = *(*buff - 1)) + 1)) - 1);
if ((ctsz < 2) || (ctsz > 8))
return -3; /** min LZW size is out of its nominal [2; 8] bounds **/
if ((ctbl = (1L << ctsz)) != (mask & _GIF_SWAP(*(GIF_H*)(*buff + 1))))
return -2; /** initial code is not equal to min LZW size **/
for (curr = ++ctbl; curr; code[--curr] = 0); /** actual color codes **/
/** getting codes from stream (--size makes up for end-of-stream mark) **/
for (--(*size), bszc = -ccsz, prev = curr = 0;
((*size -= (bseq = *(*buff)++) + 1) >= 0) && bseq; *buff += bseq)
for (; bseq > 0; bseq -= GIF_HLEN, *buff += GIF_HLEN)
for (accu = (GIF_H)(_GIF_SWAP(*(GIF_H*)*buff)
& ((bseq < GIF_HLEN)? ((1U << (8 * bseq)) - 1U) : ~0U)),
curr |= accu << (ccsz + bszc), accu = (GIF_H)(accu >> -bszc),
bszc += 8 * ((bseq < GIF_HLEN)? bseq : GIF_HLEN);
bszc >= 0; bszc -= ccsz, prev = curr, curr = accu,
accu = (GIF_H)(accu >> ccsz))
if (((curr &= mask) & ~1L) == (1L << ctsz)) {
if (~(ctbl = curr + 1) & 1) /** end-of-data code (ED). **/
/** -1: no end-of-stream mark after ED; 1: decoded **/
return (*((*buff += bseq + 1) - 1))? -1 : 1;
mask = (GIF_H)((1 << (ccsz = ctsz + 1)) - 1);
} /** ^- table drop code (TD). TD = 1 << ctsz, ED = TD + 1 **/
else { /** single-pixel (SP) or multi-pixel (MP) code. **/
if (ctbl < GIF_CLEN) { /** is the code table full? **/
if ((ctbl == mask) && (ctbl < GIF_CLEN - 1)) {
mask = (GIF_H)(mask + mask + 1);
ccsz++; /** yes; extending **/
} /** prev = TD? => curr < ctbl = prev **/
code[ctbl] = (uint32_t)prev + (code[prev] & 0xFFF000);
} /** appending SP / MP decoded pixels to the frame **/
prev = (long)code[iter = (ctbl > curr)? curr : prev];
if ((bptr += (prev = (prev >> 12) & 0xFFF)) > blen)
continue; /** skipping pixels above frame capacity **/
for (prev++; (iter &= 0xFFF) >> ctsz;
*bptr-- = (uint8_t)((iter = (long)code[iter]) >> 24));
(bptr += prev)[-prev] = (uint8_t)iter;
if (ctbl < GIF_CLEN) { /** appending the code table **/
if (ctbl == curr)
*bptr++ = (uint8_t)iter;
else if (ctbl < curr)
return -5; /** wrong code in the stream **/
code[ctbl++] += ((uint32_t)iter << 24) + 0x1000;
}
} /** 0: no ED before end-of-stream mark; -4: see above **/
return (++(*size) >= 0)? 0 : -4; /** ^- N.B.: 0 error is recoverable **/
}
/** _________________________________________________________________________
The main loading function. Returns the total number of frames if the data
includes proper GIF ending, and otherwise it returns the number of frames
loaded per current call, multiplied by -1. So, the data may be incomplete
and in this case the function can be called again when more data arrives,
just remember to keep SKIP up to date.
_________________________________________________________________________
DATA: raw data chunk, may be partial
SIZE: size of the data chunk that`s currently present
GWFR: frame writer function, MANDATORY
EAMF: metadata reader function, set to 0 if not needed
ANIM: implementation-specific data (e.g. a structure or a pointer to it)
SKIP: number of frames to skip before resuming
**/
GIF_EXTR long GIF_Load(void *data, long size,
void (*gwfr)(void*, struct GIF_WHDR*),
void (*eamf)(void*, struct GIF_WHDR*),
void *anim, long skip) {
const long GIF_BLEN = (1 << 12) * sizeof(uint32_t);
const uint8_t GIF_EHDM = 0x21, /** extension header mark **/
GIF_FHDM = 0x2C, /** frame header mark **/
GIF_EOFM = 0x3B, /** end-of-file mark **/
GIF_EGCM = 0xF9, /** extension: graphics control mark **/
GIF_EAMM = 0xFF; /** extension: app metadata mark **/
#pragma pack(push, 1)
struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/
uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/
uint16_t xdim, ydim; /** total image width, total image height **/
uint8_t flgs; /** FLAGS:
GlobalPlt bit 7 1: global palette exists
0: local in each frame
ClrRes bit 6-4 bits/channel = ClrRes+1
[reserved] bit 3 0
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
uint8_t bkgd, aspr; /** background color index, aspect ratio **/
} *ghdr = (struct GIF_GHDR*)data;
struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/
uint16_t frxo, fryo; /** offset of this frame in a "full" image **/
uint16_t frxd, fryd; /** frame width, frame height **/
uint8_t flgs; /** FLAGS:
LocalPlt bit 7 1: local palette exists
0: global is used
Interlaced bit 6 1: interlaced frame
0: non-interlaced frame
Sorted bit 5 usually 0
[reserved] bit 4-3 [undefined]
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
} *fhdr;
struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/
uint8_t flgs; /** FLAGS:
[reserved] bit 7-5 [undefined]
BlendMode bit 4-2 000: not set; static GIF
001: leave result as is
010: restore background
011: restore previous
1--: [undefined]
UserInput bit 1 1: show frame till input
0: default; ~99% of GIFs
TransColor bit 0 1: got transparent color
0: frame is fully opaque
**/
uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/
uint8_t tran; /** transparent color index **/
} *egch = 0;
#pragma pack(pop)
struct GIF_WHDR wtmp, whdr = {0};
long desc, blen;
uint8_t *buff;
/** checking if the stream is not empty and has a 'GIF8[79]a' signature,
the data has sufficient size and frameskip value is non-negative **/
if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71)
|| (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0)
|| ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr)
return 0;
buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/
+ _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L;
if ((size -= buff - (uint8_t*)ghdr) <= 0)
return 0;
whdr.xdim = _GIF_SWAP(ghdr->xdim);
whdr.ydim = _GIF_SWAP(ghdr->ydim);
for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size;
(blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/
blen = _GIF_SkipChunk(&whdr.bptr, blen) - 1) /** count all frames **/
if (desc == GIF_FHDM) {
fhdr = (struct GIF_FHDR*)whdr.bptr;
if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal,
fhdr->flgs, &blen, sizeof(*fhdr)) <= 0)
break;
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo;
whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo;
whdr.ifrm++;
}
blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr);
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1)
whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm;
for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/
&& (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0);
size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))?
_GIF_SkipChunk(&buff, size) - 1 : size - 1 : -1)
if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/
whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40);
*(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/
whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal,
fhdr->flgs, &size, sizeof(*fhdr));
if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0)
|| (_GIF_LoadFrame(&buff, &size,
whdr.bptr, whdr.bptr + blen) < 0)))
size = -(whdr.ifrm--) - 1; /** failed to load the frame **/
else if (skip <= whdr.ifrm) {
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = _GIF_SWAP(fhdr->frxo);
whdr.fryo = _GIF_SWAP(fhdr->fryo);
whdr.time = (egch)? _GIF_SWAP(egch->time) : 0;
whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1;
whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1
: whdr.time;
whdr.mode = (egch && !(egch->flgs & 0x10))?
(egch->flgs & 0x0C) >> 2 : GIF_NONE;
egch = 0;
wtmp = whdr;
gwfr(anim, &wtmp); /** passing the frame to the caller **/
}
}
else if (desc == GIF_EHDM) { /** found an extension **/
if (*buff == GIF_EGCM) /** graphics control ext. **/
egch = (struct GIF_EGCH*)(buff + 1 + 1);
else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/
wtmp = whdr;
wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/
eamf(anim, &wtmp);
}
}
whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0)
return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1);
}
#undef _GIF_SWAP
#ifdef __cplusplus
}
#endif
#endif /** GIF_LOAD_H **/

View file

@ -18,8 +18,4 @@ void touch_cancelled(sapp_touchpoint *touch, int n);
void input_dropped_files(int n);
void input_clipboard_paste(char *str);
const char *keyname_extd(int key);
void quit();
#endif

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,121 @@
#include "quickjs/quickjs.h"
#include "HandmadeMath.h"
#include "dsp.h"
#include <stdarg.h>
#define MIST_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN)
#define GETFN(ID, ENTRY, TYPE) JSC_CCALL(ID##_##ENTRY, return TYPE##2js(js2##ID (this)->ENTRY))
#define JSC_SCALL(NAME, FN) JSC_CCALL(NAME, \
const char *str = js2str(argv[0]); \
FN ; \
JS_FreeCString(js,str); \
) \
#define JSC_SSCALL(NAME, FN) JSC_CCALL(NAME, \
const char *str = js2str(argv[0]); \
const char *str2 = js2str(argv[1]); \
FN ; \
JS_FreeCString(js,str2); \
JS_FreeCString(js,str); \
) \
#define MIST_CGETSET_BASE(name, fgetter, fsetter, props) { name, props, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }
#define MIST_CGETSET_DEF(name, fgetter, fsetter) MIST_CGETSET_BASE(name, fgetter, fsetter, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)
#define MIST_CGETET_HID(name, fgetter, fsetter) MIST_CGETSET_BASE(name, fgetter, fsetter, JS_PROP_CONFIGURABLE)
#define MIST_GET(name, fgetter) { #fgetter , JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = js_##name##_get_##fgetter } } } }
#define CGETSET_ADD_NAME(ID, ENTRY, NAME) MIST_CGETSET_DEF(#NAME, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY)
#define CGETSET_ADD(ID, ENTRY) MIST_CGETSET_DEF(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY)
#define CGETSET_ADD_HID(ID, ENTRY) MIST_CGETSET_BASE(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY, JS_PROP_CONFIGURABLE)
#define JSC_CCALL(NAME, FN) JSValue js_##NAME (JSContext *js, JSValue this, int argc, JSValue *argv) { \
JSValue ret = JS_UNDEFINED; \
{FN;} \
return ret; \
} \
#define GETSETPAIR(ID, ENTRY, TYPE, FN) \
JSValue js_##ID##_set_##ENTRY (JSContext *js, JSValue this, JSValue val) { \
js2##ID (this)->ENTRY = js2##TYPE (val); \
{FN;} \
return JS_UNDEFINED; \
} \
\
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(js2##ID (this)->ENTRY); \
} \
#define JSC_GETSET(ID, ENTRY, TYPE) GETSETPAIR( ID , ENTRY , TYPE , ; )
#define JSC_GETSET_APPLY(ID, ENTRY, TYPE) GETSETPAIR(ID, ENTRY, TYPE, ID##_apply(js2##ID (this));)
#define JSC_GETSET_HOOK(ID, ENTRY) GETSETPAIR(ID, ENTRY, JSValue, ;)
#define JSC_GETSET_GLOBAL(ENTRY, TYPE) \
JSValue js_global_set_##ENTRY (JSContext *js, JSValue this, JSValue val) { \
ENTRY = js2##TYPE (val); \
return JS_UNDEFINED; \
} \
\
JSValue js_global_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(ENTRY); \
} \
#define JSC_GETSET_BODY(ENTRY, CPENTRY, TYPE) \
JSValue js_gameobject_set_##ENTRY (JSContext *js, JSValue this, JSValue val) { \
cpBody *b = js2gameobject(this)->body; \
cpBodySet##CPENTRY (b, js2##TYPE (val)); \
return JS_UNDEFINED; \
} \
\
JSValue js_gameobject_get_##ENTRY (JSContext *js, JSValue this) { \
cpBody *b = js2gameobject(this)->body; \
return TYPE##2js (cpBodyGet##CPENTRY (b)); \
} \
#define JSC_GET(ID, ENTRY, TYPE) \
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(js2##ID (this)->ENTRY); } \
#define QJSCLASS(TYPE)\
static JSClassID js_##TYPE##_id;\
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\
YughSpam("Freeing " #TYPE " at %p", n); \
TYPE##_free(n);}\
static JSClassDef js_##TYPE##_class = {\
#TYPE,\
.finalizer = js_##TYPE##_finalizer,\
};\
static TYPE *js2##TYPE (JSValue val) { return JS_GetOpaque(val,js_##TYPE##_id); }\
static JSValue TYPE##2js(TYPE *n) { \
JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\
YughSpam("Created " #TYPE " at %p", n); \
JS_SetOpaque(j,n);\
return j; }\
\
static JSValue js_##TYPE##_memid (JSContext *js, JSValue this) { return str2js("%p", js2##TYPE(this)); } \
#define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
JS_SetPropertyStr(js, globalThis, #NAME, NAME); \
#define QJSCLASSPREP(TYPE) \
JS_NewClassID(&js_##TYPE##_id);\
JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
/* Defines a class and uses its function list as its prototype */
#define QJSCLASSPREP_FUNCS(TYPE) \
QJSCLASSPREP(TYPE); \
JSValue TYPE##_proto = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \
JS_SetPropertyStr(js, TYPE##_proto, "memid", JS_NewCFunction(js, &js_##TYPE##_memid, "memid", 0)); \
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
#define countof(x) (sizeof(x)/sizeof((x)[0]))
void ffi_load();
void ffi_stop();
@ -18,8 +132,7 @@ int js_print_exception(JSValue v);
struct rgba js2color(JSValue v);
double js2number(JSValue v);
JSValue number2js(double g);
JSValue int2js(int i);
JSValue str2js(const char *c);
JSValue str2js(const char *c, ...);
void nota_int(char *blob);

7833
source/engine/miniz.c Normal file

File diff suppressed because it is too large Load diff

1422
source/engine/miniz.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -80,7 +80,8 @@ void particle_init()
par_bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
.data = (sg_range){.ptr = circleverts, .size = sizeof(float)*8},
.usage = SG_USAGE_IMMUTABLE
.usage = SG_USAGE_IMMUTABLE,
.label = "particle quater buffer"
});
par_bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
@ -99,7 +100,7 @@ emitter *make_emitter() {
sampler_add(&e->color, 0, (HMM_Vec4){1,1,1,1});
e->scale = 1;
e->speed = 20;
e->texture = texture_from_file("glass_chunk2.gif");
e->texture = NULL;
arrpush(emitters,e);
return e;
}
@ -166,12 +167,12 @@ void parallel_pv(emitter *e, struct scheduler *sched, struct sched_task_partitio
s = lerp(p->time/e->grow_for, 0, p->scale);
else if (p->time > (p->life - e->shrink_for))
s = lerp((p->time-(p->life-e->shrink_for))/e->shrink_for, p->scale, 0);
pv[i].scale = HMM_ScaleV2(tex_get_dimensions(e->texture), s);
pv[i].scale = HMM_ScaleV2((HMM_Vec2){e->texture->width,e->texture->height}, s);
pv[i].color = vec2rgba(p->color);
}
}
void emitters_draw()
void emitters_draw(HMM_Mat4 *proj)
{
if (arrlen(emitters) == 0) return;
int draw_count = 0;
@ -188,7 +189,7 @@ void emitters_draw()
}
sg_apply_pipeline(par_pipe);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection));
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*proj));
sg_apply_bindings(&par_bind);
sg_draw(0, 4, draw_count);
}

View file

@ -70,7 +70,7 @@ void stop_emitter(emitter *e);
void emitter_emit(emitter *e, int count);
void emitters_step(double dt);
void emitters_draw();
void emitters_draw(HMM_Mat4 *proj);
void emitter_step(emitter *e, double dt);
#endif

View file

@ -24,8 +24,13 @@
#include "sokol/sokol_gfx.h"
#include "sokol_gfx_ext.h"
#include "crt.sglsl.h"
#include "msf_gif.h"
HMM_Vec2 campos = {0,0};
float camzoom = 1;
static struct {
sg_swapchain swap;
sg_pipeline pipe;
@ -35,6 +40,12 @@ static struct {
sg_image depth;
} sg_gif;
static struct {
sg_pipeline pipe;
sg_bindings bind;
sg_shader shader;
} sg_crt;
static struct {
int w;
int h;
@ -110,143 +121,101 @@ void capture_screen(int x, int y, int w, int h, const char *path)
#include "HandmadeMath.h"
int renderMode = LIT;
struct shader *spriteShader = NULL;
struct shader *wireframeShader = NULL;
struct shader *animSpriteShader = NULL;
static struct shader *textShader;
struct rgba editorClearColor = {35,60,92,255};
float shadowLookahead = 8.5f;
struct rgba gridSmallColor = {
.r = 255 * 0.35f,
.g = 255,
.b = 255 * 0.9f
};
struct rgba gridBigColor = {
.r = 255 * 0.92f,
.g = 255 * 0.92f,
.b = 255 * 0.68f
};
float gridScale = 500.f;
float smallGridUnit = 1.f;
float bigGridUnit = 10.f;
float gridSmallThickness = 2.f;
float gridBigThickness = 7.f;
float gridOpacity = 0.3f;
// Debug render modes
bool renderGizmos = false;
bool showGrid = true;
bool debugDrawPhysics = false;
bool renderNav = false;
// Lighting effect flags
bool renderAO = true;
bool renderDynamicShadows = true;
bool renderRefraction = true;
bool renderReflection = true;
///// for editing
struct gameobject *selectedobject = NULL;
char objectName[200] = {'\0'}; // object name buffer
sg_image ddimg;
void debug_draw_phys(int draw) {
debugDrawPhysics = draw;
}
void opengl_rendermode(enum RenderMode r) {
renderMode = r;
}
sg_pipeline mainpip;
sg_pass_action pass_action = {0};
static struct {
sg_pass_action pass_action;
sg_pass pass;
sg_pipeline pipe;
sg_shader shader;
} sg_shadow;
void trace_make_image(const sg_image_desc *d, sg_image id, void *data)
{
YughSpam("Made image %s.", d->label);
}
void trace_init_image(sg_image id, const sg_image_desc *d, void *data)
{
YughSpam("Init image %s", d->label);
}
void trace_make_shader(const sg_shader_desc *d, sg_shader id, void *data)
{
YughSpam("Making shader %s", d->label);
if (sg_query_shader_state(id) == SG_RESOURCESTATE_FAILED)
YughError("FAILED MAKING A SHADER: %s\n%s\n%s", d->label);
}
void trace_fail_shader(sg_shader id, void *data)
{
YughError("Shader %u did not compile.", id);
}
void trace_destroy_shader(sg_shader id, void *data)
{
YughSpam("Destroyed shader %u.", id);
}
void trace_fail_image(sg_image id, void *data)
{
sg_image_desc desc = sg_query_image_desc(id);
YughError("Failed to make image %u %s", id, desc.label);
}
void trace_make_pipeline(const sg_pipeline_desc *d, sg_pipeline id, void *data)
{
YughSpam("Making pipeline %u [%s].", id, d->label);
}
void trace_apply_pipeline(sg_pipeline pip, void *data)
{
YughSpam("Applying pipeline %u %s.", pip, sg_query_pipeline_desc(pip).label);
}
void trace_fail_pipeline(sg_pipeline pip, void *data)
{
YughError("Failed pipeline %s", sg_query_pipeline_desc(pip).label);
}
void trace_make_attachments(const sg_attachment_desc *d, sg_attachments result, void *data)
{
YughSpam("Making attachments %s", "IMPLEMENT");
// YughSpam("Applying pipeline %u %s.", pip, sg_query_pipeline_desc(pip).label);
}
void trace_begin_pass(sg_pass pass, const sg_pass_action *action, void *data)
{
YughSpam("Begin pass %s", pass.label);
// YughSpam("Begin pass %s", pass.label);
}
#define SG_TRACE_SET(NAME) \
void trace_alloc_##NAME (sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Alloc " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_dealloc_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Dealloc " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_make_##NAME(sg_##NAME##_desc *desc, void *data) \
{ \
YughSpam("Make " #NAME " [%s]", desc->label); \
} \
\
void trace_destroy_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Destroy " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_init_##NAME(sg_##NAME id, sg_##NAME##_desc *desc, void *data) \
{ \
YughSpam("Init " #NAME " %d [%s]", id, desc->label); \
} \
\
void trace_uninit_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Init " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_fail_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughError("Failed " #NAME " %d: %s", id, desc.label); \
} \
SG_TRACE_SET(buffer)
SG_TRACE_SET(image)
SG_TRACE_SET(sampler)
SG_TRACE_SET(shader)
SG_TRACE_SET(pipeline)
SG_TRACE_SET(attachments)
#define SG_HOOK_SET(NAME) \
.alloc_##NAME = trace_alloc_##NAME, \
.dealloc_##NAME = trace_dealloc_##NAME, \
.init_##NAME = trace_init_##NAME, \
.uninit_##NAME = trace_uninit_##NAME, \
.fail_##NAME = trace_fail_##NAME, \
.destroy_##NAME = trace_destroy_##NAME, \
.make_##NAME = trace_make_##NAME \
void trace_append_buffer(sg_buffer id, sg_range *data, void *user)
{
sg_buffer_desc desc = sg_query_buffer_desc(id);
// YughSpam("Appending buffer %d [%s]", id, desc.label);
}
static sg_trace_hooks hooks = {
.fail_shader = trace_fail_shader,
.make_shader = trace_make_shader,
.destroy_shader = trace_destroy_shader,
.fail_image = trace_fail_image,
.make_image = trace_make_image,
.init_image = trace_init_image,
.make_pipeline = trace_make_pipeline,
.fail_pipeline = trace_fail_pipeline,
.apply_pipeline = trace_apply_pipeline,
.begin_pass = trace_begin_pass,
.make_attachments = trace_make_attachments,
SG_HOOK_SET(buffer),
SG_HOOK_SET(image),
SG_HOOK_SET(shader),
SG_HOOK_SET(sampler),
SG_HOOK_SET(pipeline),
SG_HOOK_SET(attachments),
.append_buffer = trace_append_buffer
};
void render_init() {
@ -278,14 +247,13 @@ void render_init() {
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2
[1].format = SG_VERTEXFORMAT_FLOAT2
}
},
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "gif pipe",
});
#if defined SOKOL_GLCORE33 || defined SOKOL_GLES3
float crt_quad[] = {
-1, 1, 0, 1,
-1, -1, 0, 0,
@ -294,16 +262,7 @@ void render_init() {
1, -1, 1, 0,
1, 1, 1, 1
};
#else
float crt_quad[] = {
-1, 1, 0, 0,
-1, -1, 0, 1,
1, -1, 1, 1,
-1, 1, 0, 0,
1, -1, 1, 1,
1, 1, 1, 0
};
#endif
float gif_quad[] = {
-1, 1, 0, 1,
-1, -1, 0, 0,
@ -316,78 +275,35 @@ void render_init() {
sg_gif.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(gif_quad),
.data = gif_quad,
.label = "gif vert buffer",
});
sg_gif.bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
/*
sg_image_desc shadow_desc = {
.render_target = true,
.width = 1024,
.height = 1024,
.pixel_format = SG_PIXELFORMAT_R32F,
};
sg_image depth_img = sg_make_image(&shadow_desc);
shadow_desc.pixel_format = sapp_depth_format();
ddimg = sg_make_image(&shadow_desc);
sg_shadow.pass = sg_make_pass(&(sg_pass_desc){
.color_attachments[0].image = depth_img,
.depth_stencil_attachment.image = ddimg,
});
sg_shadow.pass_action = (sg_pass_action) {
.colors[0] = { .action=SG_ACTION_CLEAR, .value = {1,1,1,1} } };
sg_shadow.shader = sg_make_shader(shadow_shader_desc(sg_query_backend()));
sg_shadow.pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_shadow.shader,
sg_crt.shader = sg_make_shader(crt_shader_desc(sg_query_backend()));
sg_crt.pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_crt.shader,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT3,
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2
}
},
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
.pixel_format = sapp_depth_format()
},
.colors[0].pixel_format = SG_PIXELFORMAT_R32F,
.index_type = SG_INDEXTYPE_UINT16,
.cull_mode = SG_CULLMODE_BACK,
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
});
*/
sg_crt.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(crt_quad),
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.usage = SG_USAGE_IMMUTABLE,
.data = SG_RANGE(crt_quad),
}
static cpBody *camera = NULL;
void set_cam_body(cpBody *body) { camera = body; }
cpVect cam_pos() { return camera ? cpBodyGetPosition(camera) : cpvzero; }
static float zoom = 1.f;
float cam_zoom() { return zoom; }
void add_zoom(float val) { zoom = val; }
HMM_Vec2 world2screen(HMM_Vec2 pos)
{
pos = HMM_SubV2(pos, HMM_V2(cam_pos().x, cam_pos().y));
pos = HMM_ScaleV2(pos, 1.0/zoom);
pos = HMM_AddV2(pos, HMM_ScaleV2(mainwin.size,0.5));
return pos;
}
HMM_Vec2 screen2world(HMM_Vec2 pos)
{
pos = HMM_ScaleV2(pos, 1/mainwin.dpi);
pos = HMM_SubV2(pos, HMM_ScaleV2(mainwin.size, 0.5));
pos = HMM_ScaleV2(pos, zoom);
pos = HMM_AddV2(pos, HMM_V2(cam_pos().x, cam_pos().y));
return pos;
.label = "crt vert buffer",
});
}
HMM_Mat4 projection = {0.f};
HMM_Mat4 hudproj = {0.f};
HMM_Mat4 useproj = {0};
HMM_Vec3 dirl_pos = {4, 100, 20};
@ -398,8 +314,14 @@ HMM_Vec3 dirl_pos = {4, 100, 20};
#define MODE_EXPAND 4
#define MODE_FULL 5
void full_2d_pass(struct window *window)
{
void openglRender(struct window *window, gameobject *cam, float zoom) {
sg_swapchain sch = sglue_swapchain();
sg_begin_pass(&(sg_pass){
.action = pass_action,
.swapchain = sglue_swapchain(),
.label = "window pass"
});
HMM_Vec2 usesize = window->rendersize;
switch(window->mode) {
@ -427,70 +349,17 @@ void full_2d_pass(struct window *window)
}
// 2D projection
cpVect pos = cam_pos();
campos = go_pos(cam);
camzoom = zoom;
projection = HMM_Orthographic_LH_NO(
pos.x - zoom * usesize.x / 2,
pos.x + zoom * usesize.x / 2,
pos.y - zoom * usesize.y / 2,
pos.y + zoom * usesize.y / 2, -10000.f, 10000.f);
campos.x - camzoom * usesize.x / 2,
campos.x + camzoom * usesize.x / 2,
campos.y - camzoom * usesize.y / 2,
campos.y + camzoom * usesize.y / 2, -10000.f, 10000.f);
hudproj = HMM_Orthographic_LH_ZO(0, usesize.x, 0, usesize.y, -1.f, 1.f);
sprite_draw_all();
model_draw_all();
script_evalf("prosperon.draw();");
emitters_draw();
//// DEBUG
if (debugDrawPhysics) {
gameobject_draw_debugs();
script_evalf("prosperon.debug();");
}
debug_flush(&projection);
text_flush(&projection);
////// TEXT && GUI
debug_nextpass();
script_evalf("prosperon.gui();");
debug_flush(&hudproj);
text_flush(&hudproj);
sprite_flush();
}
void full_3d_pass(struct window *window)
{
HMM_Mat4 model = HMM_M4D(1.f);
float scale = 0.08;
model = HMM_MulM4(model, HMM_Scale((HMM_Vec3){scale,scale,scale}));
// Shadow pass
// sg_begin_pass(sg_shadow.pass, &sg_shadow.pass_action);
// sg_apply_pipeline(sg_shadow.pipe);
HMM_Mat4 light_proj = HMM_Orthographic_RH_ZO(-100.f, 100.f, -100.f, 100.f, 1.f, 100.f);
HMM_Mat4 light_view = HMM_LookAt_RH(dirl_pos, (HMM_Vec3){0,0,0}, (HMM_Vec3){0,1,0});
HMM_Mat4 lsm = HMM_MulM4(light_proj, light_view);
HMM_Mat4 subo[2];
subo[0] = lsm;
subo[1] = model;
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(subo));
}
void openglRender(struct window *window) {
sg_swapchain sch = sglue_swapchain();
sg_begin_pass(&(sg_pass){
.action = pass_action,
.swapchain = sglue_swapchain(),
.label = "window pass"
});
full_2d_pass(window);
sg_end_pass();
/* if (gif.rec && (apptime() - gif.timer) > gif.spf) {
sg_begin_pass(&(sg_pass){
.action = pass_action,
@ -506,30 +375,8 @@ void openglRender(struct window *window) {
msf_gif_frame(&gif_state, gif.buffer, gif.cpf, gif.depth, gif.w * -4);
}
*/
sg_commit();
debug_newframe();
}
sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d)
{
YughInfo("Making shader with %s and %s", v, f);
char *vs = slurp_text(v, NULL);
char *fs = slurp_text(f, NULL);
d->vs.source = vs;
d->fs.source = fs;
d->label = v;
sg_shader ret = sg_make_shader(d);
free(vs);
free(fs);
return ret;
}
struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh) {
struct boundingbox bb = {
.t = c.Y + wh.Y/2,

View file

@ -7,13 +7,13 @@
#define SOKOL_GLES3
#elif __WIN32
#define SOKOL_D3D11
#define SOKOL_WIN32_FORCE_MAIN
#elif __APPLE__
#define SOKOL_METAL
#endif
#include "sokol/sokol_gfx.h"
#include "HandmadeMath.h"
#include "gameobject.h"
#define RGBA_MAX 255
@ -29,12 +29,7 @@ extern HMM_Vec3 dirl_pos;
extern HMM_Mat4 projection;
extern HMM_Mat4 hudproj;
struct camera3d {
};
typedef struct camera3d camera3d;
extern HMM_Mat4 useproj;
struct draw_p {
float x;
@ -52,24 +47,15 @@ enum RenderMode {
};
void render_init();
void openglRender(struct window *window);
extern HMM_Vec2 campos;
extern float camzoom;
void openglRender(struct window *window, gameobject *cam, float zoom);
void opengl_rendermode(enum RenderMode r);
void openglInit3d(struct window *window);
void openglRender3d(struct window *window, camera3d *camera);
void capture_screen(int x, int y, int w, int h, const char *path);
void debug_draw_phys(int draw);
void set_cam_body(cpBody *body);
cpVect cam_pos();
float cam_zoom();
void add_zoom(float val);
HMM_Vec2 world2screen(HMM_Vec2 pos);
HMM_Vec2 screen2world(HMM_Vec2 pos);
sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d);
void gif_rec_start(int w, int h, int cpf, int bitdepth);
void gif_rec_end(const char *path);

View file

@ -15,7 +15,7 @@
#include "font.h"
#include <fcntl.h>
#include "cdb.h"
#include "miniz.h"
#ifndef __EMSCRIPTEN__
#include <ftw.h>
@ -40,8 +40,8 @@ struct dirent *c_dirent = NULL;
char pathbuf[MAXPATH + 1];
static struct cdb corecdb;
static struct cdb game_cdb;
static mz_zip_archive corecdb;
static mz_zip_archive game_cdb;
int LOADED_GAME = 0;
uint8_t *gamebuf;
@ -49,7 +49,7 @@ uint8_t *gamebuf;
static void response_cb(const sfetch_response_t *r)
{
if (r->fetched) {
cdb_initf(&game_cdb, r->data.ptr, r->data.size);
mz_zip_reader_init_mem(&game_cdb, r->data.ptr, r->data.size,0);
LOADED_GAME = 1;
}
if (r->finished) {
@ -78,7 +78,7 @@ void resources_init() {
}
});
cdb_initf(&corecdb, core_cdb, core_cdb_len);
mz_zip_reader_init_mem(&corecdb, core_cdb, core_cdb_len, 0);
}
char *get_filename_from_path(char *path, int extension) {
@ -137,6 +137,12 @@ static int ls_ftw(const char *path, const struct stat *sb, int typeflag)
return 0;
}
time_t file_mod_secs(const char *file) {
struct stat attr;
stat(file, &attr);
return attr.st_mtime;
}
// TODO: Not reentrant
char **ls(const char *path)
{
@ -154,6 +160,7 @@ char **ls(const char *path)
#else
void fill_extensions(char *paths, const char *path, const char *ext)
{};
char **ls(const char *path) { return NULL; }
#endif
char *str_replace_ext(const char *s, const char *newext) {
@ -166,32 +173,11 @@ char *str_replace_ext(const char *s, const char *newext) {
return ret;
}
FILE *path_open(const char *tag, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vsnprintf(pathbuf, MAXPATH+1, fmt, args);
va_end(args);
FILE *f = fopen(pathbuf, tag);
return f;
}
void *cdb_slurp(struct cdb *cdb, const char *file, size_t *size)
{
unsigned vlen, vpos;
vpos = cdb_datapos(cdb);
vlen = cdb_datalen(cdb);
char *data = malloc(vlen+1);
cdb_read(cdb, data, vlen, vpos);
if (size) *size = vlen;
return data;
}
int fexists(const char *path)
{
int len = strlen(path);
if (cdb_find(&game_cdb, path, len)) return 1;
else if (cdb_find(&corecdb, path, len)) return 1;
if (mz_zip_reader_locate_file(&game_cdb, path, NULL, 0) != -1) return 1;
else if (mz_zip_reader_locate_file(&corecdb, path, NULL, 0) != -1) return 1;
else if (!access(path, R_OK)) return 1;
return 0;
@ -220,12 +206,13 @@ void *os_slurp(const char *file, size_t *size)
void *slurp_file(const char *filename, size_t *size)
{
void *ret;
if (!access(filename, R_OK))
return os_slurp(filename, size);
else if (cdb_find(&game_cdb, filename, strlen(filename)))
return cdb_slurp(&game_cdb, filename, size);
else if (cdb_find(&corecdb, filename, strlen(filename)))
return cdb_slurp(&corecdb, filename, size);
else if (ret = mz_zip_reader_extract_file_to_heap(&game_cdb, filename, size, 0))
return ret;
else if (ret = mz_zip_reader_extract_file_to_heap(&corecdb, filename, size, 0))
return ret;
return NULL;
}
@ -304,58 +291,3 @@ int slurp_write(const char *txt, const char *filename, size_t len) {
fclose(f);
return 0;
}
#ifndef __EMSCRIPTEN__
static struct cdb_make cdbm;
static const char *pack_ext[] = {".qoi", ".qoa", ".js", ".wav", ".mp3", ".png", ".sf2", ".midi", ".lvl", ".glsl", ".ttf", ".json", ".jso"};
static int ftw_pack(const char *path, const struct stat *sb, int flag)
{
if (flag != FTW_F) return 0;
int pack = 0;
char *ext = strrchr(path, '.');
if (!ext)
return 0;
for (int i = 0; i < 13; i++) {
if (!strcmp(ext, pack_ext[i])) {
pack = 1;
break;
}
}
if (!pack) return 0;
size_t len;
void *file = slurp_file(path, &len);
cdb_make_add(&cdbm, &path[2], strlen(&path[2]), file, len);
free(file);
return 0;
}
void pack_engine(const char *fname)
{
int fd;
char *key, *va;
unsigned klen, vlen;
fd = creat(fname, O_RDWR);
if (fd == -1) {
YughError("Couldn't make file at %s.", fname);
return;
}
cdb_make_start(&cdbm, fd);
ftw(".", ftw_pack, 20);
cdb_make_finish(&cdbm);
close(fd);
}
#else
void pack_engine(const char *fname){
YughError("Cannot pack engine on a web build.");
}
char **ls(const char *path) { return NULL; }
#endif

View file

@ -4,6 +4,7 @@
#include <stdio.h>
#include "stb_ds.h"
#include "string.h"
#include <time.h>
extern char *DATA_PATH;
extern int LOADED_GAME;
@ -13,10 +14,10 @@ char *get_filename_from_path(char *path, int extension);
char *get_directory_from_path(char *path);
char *str_replace_ext(const char *s, const char *newext);
FILE *res_open(char *path, const char *tag);
FILE *path_open(const char *tag, const char *fmt, ...);
char **ls(const char *path);
int cp(const char *p1, const char *p2);
int fexists(const char *path);
time_t file_mod_secs(const char *file);
char *dirname(const char *path);
@ -26,8 +27,6 @@ int slurp_write(const char *txt, const char *filename, size_t len);
char *seprint(char *fmt, ...);
void pack_engine(const char *fname);
static inline void *stbarrdup(void *mem, size_t size, int len) {
void *out = NULL;
arrsetlen(out, len);

View file

@ -1,22 +1,8 @@
#include "script.h"
#include "log.h"
#include "stdio.h"
#include "jsffi.h"
#include "font.h"
#include "gameobject.h"
#include "ftw.h"
#include "stb_ds.h"
#include "sys/stat.h"
#include "sys/types.h"
#include "time.h"
#include "resources.h"
#include "input.h"
#include <stdarg.h>
@ -29,100 +15,40 @@ JSRuntime *rt = NULL;
#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT | JS_EVAL_FLAG_STRIP
#endif
static struct {
char *key;
JSValue value;
} *jsstrs = NULL;
JSValue jstr(const char *str)
{
int index = shgeti(jsstrs, str);
if (index != -1) return jsstrs[index].value;
JSValue v = str2js(str);
shput(jsstrs, str, v);
return v;
}
static int load_prefab(const char *fpath, const struct stat *sb, int typeflag) {
if (typeflag != FTW_F)
return 0;
if (!strcmp(".prefab", strrchr(fpath, '.')))
script_dofile(fpath);
return 0;
}
void script_startup() {
rt = JS_NewRuntime();
js = JS_NewContext(rt);
sh_new_arena(jsstrs);
ffi_load();
for (int i = 0; i < 100; i++)
num_cache[i] = int2js(i);
script_dofile("scripts/engine.js");
// jso_file("scripts/engine.js");
size_t len;
char *eng = slurp_text("scripts/engine.js", &len);
JSValue v = script_eval("scripts/engine.js", eng);
JS_FreeValue(js,v);
free(eng);
}
static int stopped = 0;
void script_stop()
{
script_evalf("Event.notify('quit');");
send_signal("quit",0,NULL);
script_evalf("prosperon.quit();");
#ifndef LEAK
return;
#endif
printf("FREEING CONTEXT\n");
ffi_stop();
for (int i = 0; i < shlen(jsstrs); i++)
JS_FreeValue(js,jsstrs[i].value);
#if LEAK
JS_FreeContext(js);
script_gc();
JS_FreeRuntime(rt);
#endif
js = NULL;
rt = NULL;
}
void script_gc()
{
JS_RunGC(rt);
}
void script_gc() { JS_RunGC(rt); }
JSValue num_cache[100] = {0};
/*int js_print_exception(JSValue v) {
void js_stacktrace() {
#ifndef NDEBUG
if (JS_IsException(v)) {
JSValue exception = JS_GetException(js);
if (JS_IsNull(exception)) {
JS_FreeValue(js,exception);
return 0;
}
JSValue val = JS_ToCStringJS_GetPropertyStr(js, exception, "stack");
const char *name = JS_ToCString(js, JS_GetPropertyStr(js, exception, "name"));
const char *msg = JS_ToCString(js, JS_GetPropertyStr(js, exception, "message"));
const char *stack = JS_ToCString(js, val);
YughLog(LOG_SCRIPT, LOG_ERROR, "%s :: %s\n%s", name, msg,stack);
js_stacktrace();
JS_FreeCString(js, name);
JS_FreeCString(js, msg);
JS_FreeCString(js, stack);
JS_FreeValue(js,val);
JS_FreeValue(js,exception);
return 1;
}
script_evalf("console.stack();");
#endif
return 0;
}
*/
void script_run(const char *script, const char *file) {
JSValue obj = JS_Eval(js, script, strlen(script), file, JS_EVAL_FLAGS);
js_print_exception(obj);
JS_FreeValue(js,obj);
}
void script_evalf(const char *format, ...)
@ -138,121 +64,16 @@ void script_evalf(const char *format, ...)
JS_FreeValue(js,obj);
}
uint8_t *script_compile(const char *file, size_t *len) {
size_t file_len;
char *script = slurp_text(file, &file_len);
JSValue obj = JS_Eval(js, script, file_len, file, JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAGS);
free(script);
uint8_t *out = JS_WriteObject(js, len, obj, JS_WRITE_OBJ_BYTECODE);
JS_FreeValue(js,obj);
return out;
}
JSValue script_run_bytecode(uint8_t *code, size_t len)
JSValue script_eval(const char *file, const char *script)
{
JSValue b = JS_ReadObject(js, code, len, JS_READ_OBJ_BYTECODE);
JSValue ret = JS_EvalFunction(js, b);
js_print_exception(ret);
JS_FreeValue(js,b);
return ret;
}
struct callee stacktrace_callee;
time_t file_mod_secs(const char *file) {
struct stat attr;
stat(file, &attr);
return attr.st_mtime;
}
void js_stacktrace() {
#ifndef NDEBUG
script_evalf("console.stack();");
#endif
}
void js_dump_stack() {
js_stacktrace();
}
int script_dofile(const char *file) {
JSValue ret = script_runfile(file);
JS_FreeValue(js,ret);
return file_mod_secs(file);
}
JSValue script_runjso(const uint8_t *buf, size_t len)
{
JSValue obj = JS_ReadObject(js, buf, len, JS_EVAL_FLAGS);
JSValue ret = JS_EvalFunction(js, obj);
js_print_exception(ret);
return ret;
}
time_t jso_file(const char *file)
{
size_t len;
uint8_t *byte = slurp_file(file, &len);
JSValue obj = JS_ReadObject(js, byte, len, JS_READ_OBJ_BYTECODE);
JSValue ret = JS_EvalFunction(js, obj);
js_print_exception(ret);
JS_FreeValue(js,ret);
JS_FreeValue(js,obj);
free(byte);
return file_mod_secs(file);
}
JSValue script_runfile(const char *file)
{
size_t len;
char *script = slurp_text(file, &len);
if (!script) return JS_UNDEFINED;
YughWarn("Eval %s.", file);
JSValue obj = JS_Eval(js, script, len, file, JS_EVAL_FLAGS);
js_print_exception(obj);
free(script);
return obj;
}
/* env is an object in the scripting environment;
s is the function to call on that object
*/
void script_eval_w_env(const char *s, JSValue env, const char *file) {
JSValue v = JS_EvalThis(js, env, s, strlen(s), file, JS_EVAL_FLAGS);
js_print_exception(v);
JS_FreeValue(js, v);
}
JSValue eval_file_env(const char *script, const char *file, JSValue env)
{
JSValue v = JS_EvalThis(js, env, script, strlen(script), file, JS_EVAL_FLAGS);
JSValue v = JS_Eval(js, script, strlen(script), file, JS_EVAL_FLAGS);
js_print_exception(v);
return v;
}
JSValue file_eval_env(const char *file, JSValue env)
{
size_t len;
char *script = slurp_text(file, &len);
JSValue v = JS_EvalThis(js, env, script, len, file, JS_EVAL_FLAGS);
free(script);
js_print_exception(v);
return v;
}
void script_call_sym(JSValue sym) {
void script_call_sym(JSValue sym, int argc, JSValue *argv) {
if (!JS_IsFunction(js, sym)) return;
struct callee c;
c.fn = sym;
c.obj = JS_GetGlobalObject(js);
call_callee(&c);
}
void script_call_fn_arg(JSValue fn, JSValue arg)
{
if (!JS_IsFunction(js,fn)) return;
JSValue ret = JS_Call(js, fn, JS_GetGlobalObject(js), 1, &arg);
JSValue ret = JS_Call(js, sym, JS_UNDEFINED, argc, argv);
js_print_exception(ret);
JS_FreeValue(js, ret);
}
@ -266,62 +87,3 @@ void out_memusage(const char *file)
JS_DumpMemoryUsage(f, &jsmem, rt);
fclose(f);
}
JSValue js_callee_exec(struct callee *c, int argc, JSValue *argv)
{
if (JS_IsUndefined(c->fn)) return JS_UNDEFINED;
if (JS_IsUndefined(c->obj)) return JS_UNDEFINED;
JSValue ret = JS_Call(js, c->fn, c->obj, argc, argv);
js_print_exception(ret);
JS_FreeValue(js, ret);
return JS_UNDEFINED;
}
void call_callee(struct callee *c) {
js_callee_exec(c, 0, NULL);
}
void callee_dbl(struct callee c, double d) {
JSValue v = number2js(d);
js_callee_exec(&c, 1, &v);
JS_FreeValue(js, v);
}
void callee_int(struct callee c, int i) {
JSValue v = int2js(i);
js_callee_exec(&c, 1, &v);
JS_FreeValue(js, v);
}
void callee_vec2(struct callee c, HMM_Vec2 vec) {
JSValue v = vec22js(vec);
js_callee_exec(&c, 1, &v);
JS_FreeValue(js, v);
}
void script_callee(struct callee c, int argc, JSValue *argv) {
js_callee_exec(&c, argc, argv);
}
void free_callee(struct callee c)
{
JS_FreeValue(js,c.fn);
JS_FreeValue(js,c.obj);
}
void send_signal(const char *signal, int argc, JSValue *argv)
{
JSValue globalThis = JS_GetGlobalObject(js);
JSValue sig = JS_GetPropertyStr(js, globalThis, "Signal");
JS_FreeValue(js, globalThis);
JSValue fn = JS_GetPropertyStr(js, sig, "call");
JSValue args[argc+1];
args[0] = jstr(signal);
for (int i = 0; i < argc; i++)
args[1+i] = argv[i];
JS_FreeValue(js,JS_Call(js, fn, sig, argc+1, args));
JS_FreeValue(js, sig);
JS_FreeValue(js, fn);
}

View file

@ -6,56 +6,24 @@
extern JSContext *js;
struct callee {
JSValue fn;
JSValue obj;
};
struct phys_cbs {
JSValue begin;
JSValue bhit;
JSValue separate;
JSValue shit;
};
void script_call_fn_arg(JSValue fn, JSValue arg);
extern struct callee stacktrace_callee;
extern JSValue num_cache[100];
JSValue jstr(const char *str);
void js_stacktrace();
void script_startup();
void script_stop();
void script_run(const char *script, const char *file);
void script_evalf(const char *format, ...);
int script_dofile(const char *file);
time_t jso_file(const char *file);
JSValue script_runfile(const char *file);
JSValue eval_file_env(const char *script, const char *file, JSValue env);
void script_update(double dt);
void script_draw();
void free_callee(struct callee c);
void duk_run_err();
void js_dump_stack();
void out_memusage(const char *f);
void js_stacktrace();
void script_editor();
void script_call(const char *f);
void script_call_sym(JSValue sym);
void call_callee(struct callee *c);
void script_callee(struct callee c, int argc, JSValue *argv);
int script_has_sym(void *sym);
void script_eval_w_env(const char *s, JSValue env, const char *file);
JSValue file_eval_env(const char *file, JSValue env);
void script_evalf(const char *format, ...);
JSValue script_eval(const char *file, const char *script);
time_t file_mod_secs(const char *file);
void script_call_sym(JSValue sym, int argc, JSValue *argv);
void send_signal(const char *signal, int argc, JSValue *argv);
void script_gc();
JSValue script_run_bytecode(uint8_t *code, size_t len);
uint8_t *script_compile(const char *file, size_t *len);
#endif

View file

@ -13,6 +13,10 @@
#define PI 3.14159265
int SAMPLERATE = 44100;
int BUF_FRAMES = 2048;
int CHANNELS = 2;
dsp_node *masterbus = NULL;
void iir_free(struct dsp_iir *iir)
@ -136,11 +140,14 @@ void dsp_node_run(dsp_node *node)
}
}
static int node_count = 0;
dsp_node *make_node(void *data, void (*proc)(void *data, soundbyte *out, int samples), void (*fr)(void *data))
{
dsp_node *self = malloc(sizeof(dsp_node));
memset(self, 0, sizeof(*self));
self->data = data;
self->cache = calloc(BUF_FRAMES*CHANNELS*sizeof(soundbyte),1);
self->proc = proc;
self->pass = 0;
self->gain = 1;
@ -149,7 +156,10 @@ dsp_node *make_node(void *data, void (*proc)(void *data, soundbyte *out, int sam
void node_free(dsp_node *node)
{
if (node == masterbus) return; /* Simple check to not delete the masterbus */
if (node == masterbus) {
YughWarn("Attempted to delete the master bus.");
return; /* Simple check to not delete the masterbus */
}
pthread_mutex_lock(&soundrun);
unplug_node(node);
if (node->data) {

View file

@ -1,9 +1,9 @@
#ifndef DSP_H
#define DSP_H
#define SAMPLERATE 44100
#define BUF_FRAMES 2048 /* At 48k, 128 needed for 240fps consistency */
#define CHANNELS 2
extern int SAMPLERATE;
extern int BUF_FRAMES;
extern int CHANNELS;
#include "sound.h"
#include "cbuf.h"
@ -15,7 +15,7 @@ typedef struct dsp_node {
void (*proc)(void *dsp, soundbyte *buf, int samples); /* processor */
void *data; /* Node specific data to use in the proc function, passed in as dsp */
void (*data_free)(void *data);
soundbyte cache[BUF_FRAMES*CHANNELS]; /* Cached process */
soundbyte *cache;
struct dsp_node **ins; /* Array of in nodes */
struct dsp_node *out; /* node this one is connected to */
int pass; /* True if the filter should be bypassed */

View file

@ -8,7 +8,6 @@
#include "time.h"
#include <stdlib.h>
#include "pthread.h"
#include "debug.h"
#include "jsffi.h"
pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER;
@ -138,6 +137,10 @@ void sound_init() {
.buffer_frames = BUF_FRAMES,
.logger.func = sg_logging,
});
SAMPLERATE = saudio_sample_rate();
CHANNELS = saudio_channels();
BUF_FRAMES = saudio_buffer_frames();
}
typedef struct {
@ -279,14 +282,11 @@ void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
if(end) {
if (s->loop)
s->frame = 0;
script_call_sym(s->hook);
}
}
void free_source(struct sound *s)
{
JS_FreeValue(js, s->hook);
src_delete(s->src);
free(s);
}
@ -307,7 +307,6 @@ struct dsp_node *dsp_source(const char *path)
self->loop = false;
self->src = src_callback_new(src_cb, SRC_SINC_MEDIUM_QUALITY, 2, NULL, self);
self->timescale = 1;
self->hook = JS_UNDEFINED;
dsp_node *n = make_node(self, sound_fillbuf, free_source);
return n;
}

View file

@ -17,7 +17,6 @@ typedef struct sound {
int loop;
float timescale;
SRC_STATE *src;
JSValue hook;
} sound;
/* Represents a sound file source, fulled loaded*/

View file

@ -5,7 +5,6 @@
#include "render.h"
#include "stb_ds.h"
#include "texture.h"
#include "timer.h"
#include "HandmadeMath.h"
#include "sprite.sglsl.h"
@ -52,7 +51,7 @@ sprite *sprite_make()
sp->color = color_white;
sp->emissive = color_clear;
sp->go = NULL;
sp->tex = texture_from_file(NULL);
sp->tex = NULL;
sp->frame = ST_UNIT;
sp->drawmode = DRAW_SIMPLE;
sp->enabled = 1;
@ -65,8 +64,6 @@ sprite *sprite_make()
void sprite_free(sprite *sprite)
{
YughWarn("Freeing sprite %p.", sprite);
free(sprite);
for (int i = arrlen(sprites)-1; i >= 0; i--)
if (sprites[i] == sprite) {
@ -88,8 +85,9 @@ int sprite_sort(sprite **sa, sprite **sb)
if (!goa && !gob) return 0;
if (!goa) return -1;
if (!gob) return 1;
if (goa->drawlayer == gob->drawlayer) return 0;
if (goa->drawlayer > gob->drawlayer) return 1;
if (gob->drawlayer > goa->drawlayer) return -1;
if (*sa > *sb) return 1;
return -1;
}
@ -97,16 +95,17 @@ void sprite_draw_all() {
if (arrlen(sprites) == 0) return;
sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection));
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(useproj));
qsort(sprites, arrlen(sprites), sizeof(*sprites), sprite_sort);
for (int i = 0; i < arrlen(sprites); i++)
for (int i = 0; i < arrlen(sprites); i++) {
if (!sprites[i]->enabled) continue;
sprite_draw(sprites[i]);
}
}
void sprite_initialize() {
shader_sprite = sg_make_shader(sprite_shader_desc(sg_query_backend()));
pip_sprite = sg_make_pipeline(&(sg_pipeline_desc){
@ -114,9 +113,9 @@ void sprite_initialize() {
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_UBYTE4N,
[3].format = SG_VERTEXFORMAT_UBYTE4N}},
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_UBYTE4N,
[3].format = SG_VERTEXFORMAT_UBYTE4N}},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
.label = "sprite pipeline",
.colors[0].blend = blend_trans,
@ -137,10 +136,10 @@ void sprite_initialize() {
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_USHORT4N,
[3].format = SG_VERTEXFORMAT_FLOAT2,
[4].format = SG_VERTEXFORMAT_UBYTE4N
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_USHORT4N,
[3].format = SG_VERTEXFORMAT_FLOAT2,
[4].format = SG_VERTEXFORMAT_UBYTE4N
}},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
});
@ -149,6 +148,7 @@ void sprite_initialize() {
.size = sizeof(struct slice9_vert) * 100,
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.usage = SG_USAGE_STREAM,
.label = "slice9 buffer"
});
}
@ -198,7 +198,7 @@ transform2d sprite2t(sprite *s)
return (transform2d){
.pos = s->pos,
.scale = s->scale,
.angle = s->angle
.angle = HMM_TurnToRad*s->angle
};
}
@ -208,16 +208,20 @@ void sprite_draw(struct sprite *sprite) {
if (!sprite->go) t = t2d_unit;
else t = go2t(sprite->go);
t.pos.x += (cam_pos().x - (cam_pos().x/sprite->parallax));
t.pos.y += (cam_pos().y - (cam_pos().y/sprite->parallax));
t.pos.x += (campos.x - (campos.x/sprite->parallax));
t.pos.y += (campos.y - (campos.y/sprite->parallax));
HMM_Mat3 m = transform2d2mat(t);
HMM_Mat3 sm = transform2d2mat(sprite2t(sprite));
tex_draw(sprite->tex, HMM_MulM3(m,sm), sprite->frame, sprite->color, sprite->drawmode, (HMM_Vec2){0,0}, sprite->scale, sprite->emissive, sprite->parallax);
}
void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color) {
void gui_draw_img(texture *tex, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color) {
sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(hudproj));
struct texture *tex = texture_from_file(img);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(useproj));
tex_draw(tex, transform2d2mat(t), ST_UNIT, color, wrap, wrapoffset, (HMM_Vec2){wrapscale,wrapscale}, (struct rgba){0,0,0,0}, 0);
}
void slice9_draw(texture *tex, transform2d *t, HMM_Vec4 border, struct rgba color)
{
}

View file

@ -36,6 +36,6 @@ void sprite_draw(struct sprite *sprite);
void sprite_draw_all();
void sprite_flush();
void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color);
void gui_draw_img(texture *tex, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color);
#endif

21
source/engine/steam.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "steam.h"
#include <steam/steam_api.h>
#include <steam/steam_api_flat.h>
static JSValue js_steam_init(JSContext *js, JSValue this_v, int argc, JSValue *argv)
{
SteamAPI_Init();
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_steam_funcs[] = {
MIST_FUNC_DEF(steam, init, 1),
};
JSValue js_init_steam(JSContext *js)
{
JSValue steam = JS_NewObject(js);
JS_SetPropertyFunctionList(js, steam, js_steam_funcs, countof(js_steam_funcs));
return steam;
}

15
source/engine/steam.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef QJS_STEAM_H
#define QJS_STEAM_H
#include "jsffi.h"
#ifdef __cplusplus
extern "C" {
#endif
JSValue js_init_steam(JSContext *js);
#ifdef __cplusplus
}
#endif
#endif

View file

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

View file

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

View file

@ -4,7 +4,6 @@
#include "render.h"
#include "sokol/sokol_gfx.h"
#include <math.h>
#include <stb_ds.h>
#include <stb_image.h>
#include "resources.h"
@ -23,14 +22,6 @@
struct rect ST_UNIT = {0.f, 0.f, 1.f, 1.f};
static struct {
char *key;
struct texture *value;
} *texhash = NULL;
struct texture *tex_default;
struct texture *texture_notex() { return texture_from_file("icons/no_tex.gif"); }
unsigned int next_pow2(unsigned int v)
{
v--;
@ -70,31 +61,14 @@ int mip_wh(int w, int h, int *mw, int *mh, int lvl)
return 0;
}
int gif_nframes(const char *path)
{
struct texture *t = texture_from_file(path);
return t->frames;
}
int *gif_delays(const char *path)
{
struct texture *t = texture_from_file(path);
return t->delays;
}
/* If an empty string or null is put for path, loads default texture */
struct texture *texture_from_file(const char *path) {
if (!path) return texture_notex();
if (shlen(texhash) == 0) sh_new_arena(texhash);
int index = shgeti(texhash, path);
if (index != -1)
return texhash[index].value;
if (!path) return NULL;
size_t rawlen;
unsigned char *raw = slurp_file(path, &rawlen);
if (!raw) return texture_notex();
if (!raw) return NULL;
unsigned char *data;
@ -115,8 +89,7 @@ struct texture *texture_from_file(const char *path) {
int *dd = tex->delays;
tex->delays = NULL;
arrsetlen(tex->delays, tex->frames);
for (int i = 0; i < tex->frames;i++)
tex->delays[i] = dd[i];
for (int i = 0; i < tex->frames; i++) tex->delays[i] = dd[i];
free(dd);
tex->height *= tex->frames;
} else if (!strcmp(ext, ".svg")) {
@ -141,20 +114,19 @@ struct texture *texture_from_file(const char *path) {
}
free(raw);
if (data == NULL) {
YughError("STBI failed to load file %s with message: %s\nOpening default instead.", path, stbi_failure_reason());
return texture_notex();
}
if (data == NULL)
return NULL;
tex->data = data;
unsigned int nw = next_pow2(tex->width);
unsigned int nh = next_pow2(tex->height);
tex->data = data;
int filter = SG_FILTER_NEAREST;
sg_image_data sg_img_data;
sg_img_data.subimage[0][0] = (sg_range){.ptr = data, .size=tex->width*tex->height*4};
/*
int mips = mip_levels(tex->width, tex->height)+1;
YughInfo("Has %d mip levels, from wxh %dx%d, pow2 is %ux%u.", mips, tex->width, tex->height,nw,nh);
@ -170,7 +142,7 @@ struct texture *texture_from_file(const char *path) {
for (int i = 1; i < mips; i++) {
int w, h, mipw, miph;
mip_wh(tex->width, tex->height, &mipw, &miph, i-1); /* mipw miph are previous iteration */
mip_wh(tex->width, tex->height, &mipw, &miph, i-1); // mipw miph are previous iteration
mip_wh(tex->width, tex->height, &w, &h, i);
mipdata[i] = malloc(w * h * 4);
stbir_resize_uint8_linear(mipdata[i-1], mipw, miph, 0, mipdata[i], w, h, 0, 4);
@ -179,40 +151,29 @@ struct texture *texture_from_file(const char *path) {
mipw = w;
miph = h;
}
*/
tex->id = sg_make_image(&(sg_image_desc){
.type = SG_IMAGETYPE_2D,
.width = tex->width,
.height = tex->height,
.usage = SG_USAGE_IMMUTABLE,
.num_mipmaps = mips,
.data = sg_img_data
});
.type = SG_IMAGETYPE_2D,
.width = tex->width,
.height = tex->height,
.usage = SG_USAGE_IMMUTABLE,
//.num_mipmaps = mips,
.data = sg_img_data
});
shput(texhash, path, tex);
for (int i = 1; i < mips; i++)
free(mipdata[i]);
/*for (int i = 1; i < mips; i++)
free(mipdata[i]);*/
return tex;
}
void texture_sync(const char *path) { YughWarn("Need to implement texture sync."); }
void texture_free(texture *tex)
{
}
char *tex_get_path(struct texture *tex) {
for (int i = 0; i < shlen(texhash); i++) {
if (tex == texhash[i].value) {
YughSpam("Found key %s", texhash[i].key);
return texhash[i].key;
}
}
return "";
if (!tex) return;
free(tex->data);
if (tex->delays) arrfree(tex->delays);
sg_destroy_image(tex->id);
free(tex);
}
struct texture *texture_fromdata(void *raw, long size)
@ -222,10 +183,8 @@ struct texture *texture_fromdata(void *raw, long size)
int n;
void *data = stbi_load_from_memory(raw, size, &tex->width, &tex->height, &n, 4);
if (data == NULL) {
YughError("Given raw data not valid. Loading default instead.");
return texture_notex();
}
if (data == NULL)
NULL;
unsigned int nw = next_pow2(tex->width);
unsigned int nh = next_pow2(tex->height);
@ -276,14 +235,6 @@ struct texture *texture_fromdata(void *raw, long size)
return tex;
}
HMM_Vec2 tex_get_dimensions(struct texture *tex) {
if (!tex) return (HMM_Vec2){0,0};
HMM_Vec2 d;
d.x = tex->width;
d.y = tex->height;
return d;
}
static double fade (double t) { return t*t*t*(t*(t*6-15)+10); }
double grad (int hash, double x, double y, double z)
{

View file

@ -37,21 +37,10 @@ typedef struct img_sampler{
int mip_filter;
} img_sampler;
struct texture *texture_from_file(const char *path); // Create texture from image
struct texture *texture_fromdata(void *raw, long size);
texture *texture_from_file(const char *path);
void texture_free(texture *tex);
/* Hot reloads a texture, if needed */
void texture_sync(const char *path);
char * tex_get_path(struct texture *tex); // Get image path for texture
int gif_nframes(const char *path);
int *gif_delays(const char *path);
struct glrect tex_get_rect(struct texture *tex);
HMM_Vec2 tex_get_dimensions(struct texture *tex);
struct texture *texture_fromdata(void *raw, long size);
double perlin(double x, double y, double z);

View file

@ -1,15 +0,0 @@
name: build
on: [push, pull_request]
jobs:
generic:
strategy:
matrix:
os: [windows, ubuntu, macos]
name: ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
steps:
- uses: actions/checkout@v1
- name: test
run: cd test && python test_all.py

View file

@ -1 +0,0 @@
{"textures":[{"extensions":{"KHR_texture_basisu":{""}}:{""""},""}]}

Binary file not shown.

View file

@ -1,53 +0,0 @@
{
"scenes" : [
{
"nodes" : [ 0 ]
}
],
"nodes" : [
{
"mesh" : 0
}
],
"meshes" : [
{
"primitives" : [ {
"attributes" : {
"POSITION" : 0
}
} ]
}
],
"buffers" : [
{
"uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA",
"byteLength" : 36
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteOffset" : 0,
"byteLength" : 36,
"target" : 34962
}
],
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 3,
"type" : "VEC3",
"max" : [ 1.0, 1.0, 0.0 ],
"min" : [ 0.0, 0.0, 0.0 ]
}
],
"asset" : {
"version" : "2.0"
}
}

View file

@ -1,172 +0,0 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 2549,
"type": "VEC2"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 2549,
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 2549,
"type": "VEC4"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 2549,
"type": "VEC3",
"max": [
0.05445001,
0.130220339,
0.0544500239
],
"min": [
-0.05445001,
-0.130220339,
-0.0544500239
]
},
{
"bufferView": 4,
"componentType": 5123,
"count": 13530,
"type": "SCALAR"
}
],
"asset": {
"generator": "glTF Tools for Unity",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 20392
},
{
"buffer": 0,
"byteOffset": 20392,
"byteLength": 30588
},
{
"buffer": 0,
"byteOffset": 50980,
"byteLength": 40784
},
{
"buffer": 0,
"byteOffset": 91764,
"byteLength": 30588
},
{
"buffer": 0,
"byteOffset": 122352,
"byteLength": 27060
}
],
"buffers": [
{
"uri": "WaterBottle.bin",
"byteLength": 149412
}
],
"images": [
{
"uri": "WaterBottle_baseColor.png"
},
{
"uri": "WaterBottle_occlusionRoughnessMetallic.png"
},
{
"uri": "WaterBottle_normal.png"
},
{
"uri": "WaterBottle_emissive.png"
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"TEXCOORD_0": 0,
"NORMAL": 1,
"TANGENT": 2,
"POSITION": 3
},
"indices": 4,
"material": 0
}
],
"name": "WaterBottle"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicRoughnessTexture": {
"index": 1
}
},
"normalTexture": {
"index": 2
},
"occlusionTexture": {
"index": 1
},
"emissiveFactor": [
1.0,
1.0,
1.0
],
"emissiveTexture": {
"index": 3
},
"name": "BottleMat"
}
],
"nodes": [
{
"mesh": 0,
"rotation": [
0.0,
1.0,
0.0,
0.0
],
"name": "WaterBottle"
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"textures": [
{
"source": 0
},
{
"source": 1
},
{
"source": 2
},
{
"source": 3
}
]
}

View file

@ -1,224 +0,0 @@
#
# AFL dictionary for JSON
# -----------------------
#
# Just the very basics.
#
# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
#
"0"
",0"
":0"
"0:"
"-1.2e+3"
"true"
"false"
"null"
"\"\""
",\"\""
":\"\""
"\"\":"
"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"
"[]"
",[]"
":[]"
"[0]"
"[[]]"
"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"
"\"\":0"
"//"
"/**/"
#
# AFL dictionary for GLTF core
# -----------------------
"5120"
"5121"
"5122"
"5123"
"5125"
"5126"
"\"BLEND\""
"\"CUBICSPLINE\""
"\"LINEAR\""
"\"MASK\""
"\"MAT2\""
"\"MAT3\""
"\"MAT4\""
"\"OPAQUE\""
"\"SCALAR\""
"\"STEP\""
"\"VEC2\""
"\"VEC3\""
"\"VEC4\""
"\"accessor\""
"\"accessors\""
"\"alphaCutoff\""
"\"alphaMode\""
"\"animations\""
"\"aspectRatio\""
"\"asset\""
"\"attributes\""
"\"baseColorFactor\""
"\"baseColorTexture\""
"\"bufferView\""
"\"bufferViews\""
"\"buffer\""
"\"buffers\""
"\"byteLength\""
"\"byteOffset\""
"\"byteStride\""
"\"camera\""
"\"cameras\""
"\"channel\""
"\"channels\""
"\"children\""
"\"componentType\""
"\"copyright\""
"\"count\""
"\"doubleSided\""
"\"emissiveFactor\""
"\"emissiveTexture\""
"\"extensionsRequired\""
"\"extensionsUsed\""
"\"extensions\""
"\"extras\""
"\"generator\""
"\"image\""
"\"images\""
"\"index\""
"\"indices\""
"\"input\""
"\"interpolation\""
"\"inverseBindMatrices\""
"\"joints\""
"\"magFilter\""
"\"material\""
"\"materials\""
"\"matrix\""
"\"max\""
"\"mesh\""
"\"meshes\""
"\"metallicFactor\""
"\"metallicRoughnessTexture\""
"\"mimeType\""
"\"minFilter\""
"\"minVersion\""
"\"min\""
"\"mode\""
"\"name\""
"\"node\""
"\"nodes\""
"\"normalTextureInfo\""
"\"normalTexture\""
"\"normalized\""
"\"occlusionTextureInfo\""
"\"occlusionTexture\""
"\"orthographic\""
"\"output\""
"\"path\""
"\"pbrMetallicRoughness\""
"\"perspective\""
"\"primitive\""
"\"primitives\""
"\"rotation\""
"\"roughnessFactor\""
"\"sampler\""
"\"samplers\""
"\"scale\""
"\"scene\""
"\"scenes\""
"\"skeleton\""
"\"skin\""
"\"skins\""
"\"source\""
"\"sparse\""
"\"strength\""
"\"target\""
"\"targets\""
"\"texCoord\""
"\"textureInfo\""
"\"texture\""
"\"textures\""
"\"translation\""
"\"type\""
"\"uri\""
"\"values\""
"\"version\""
"\"weights\""
"\"wrapS\""
"\"wrapT\""
"\"xmag\""
"\"yfov\""
"\"ymag\""
"\"zfar\""
"\"znear\""
#
# AFL dictionary for GLTF extensions
# -----------------------
"\"KHR_materials_unlit\""
"\"KHR_texture_basisu\""
"\"KHR_materials_pbrSpecularGlossiness\""
"\"diffuseFactor\""
"\"diffuseTexture\""
"\"specularFactor\""
"\"glossinessFactor\""
"\"specularGlossinessTexture\""
"\"KHR_texture_transform\""
"\"offset\""
"\"rotation\""
"\"scale\""
"\"texCoord\""
"\"KHR_lights_punctual\""
"\"color\""
"\"intensity\""
"\"type\""
"\"range\""
"\"innerConeAngle\""
"\"outerConeAngle\""
"\"KHR_materials_transmission\""
"\"transmissionFactor\""
"\"transmissionTexture\""
"\"KHR_materials_volume\""
"\"thicknessFactor\""
"\"thicknessTexture\""
"\"attenuationColor\""
"\"attenuationDistance\""
"\"KHR_materials_sheen\""
"\"sheenColorFactor\""
"\"sheenColorTexture\""
"\"sheenRoughnessFactor\""
"\"sheenRoughnessTexture\""
"\"KHR_materials_emissive_strength\""
"\"emissiveStrength"\""

View file

@ -1,22 +0,0 @@
/* How to fuzz:
clang main.c -O2 -g -fsanitize=address,fuzzer -o fuzz
cp -r data temp
./fuzz temp/ -dict=gltf.dict -jobs=12 -workers=12
*/
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
cgltf_options options = {cgltf_file_type_invalid};
cgltf_data* data = NULL;
cgltf_result res = cgltf_parse(&options, Data, Size, &data);
if (res == cgltf_result_success)
{
cgltf_validate(data);
cgltf_free(data);
}
return 0;
}

View file

@ -1,68 +0,0 @@
cmake_minimum_required( VERSION 2.8 )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set( EXE_NAME cgltf_test )
add_executable( ${EXE_NAME} main.c )
set_property( TARGET ${EXE_NAME} PROPERTY C_STANDARD 99 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_conversion )
add_executable( ${EXE_NAME} test_conversion.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_write )
add_executable( ${EXE_NAME} test_write.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_math )
add_executable( ${EXE_NAME} test_math.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_strings )
add_executable( ${EXE_NAME} test_strings.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )

View file

@ -1,38 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <stdio.h>
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options;
memset(&options, 0, sizeof(cgltf_options));
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (result == cgltf_result_success)
result = cgltf_validate(data);
printf("Result: %d\n", result);
if (result == cgltf_result_success)
{
printf("Type: %u\n", data->file_type);
printf("Meshes: %u\n", (unsigned)data->meshes_count);
}
cgltf_free(data);
return result;
}

View file

@ -1,76 +0,0 @@
#!/usr/bin/env python
import os, sys
from sys import platform
num_tested = 0
num_errors = 0
def get_executable_path(name):
if platform == "win32":
return "build\\Debug\\" + name
else:
return "build/" + name
def is_ascii(s):
return all(ord(c) < 128 for c in s)
def collect_files(path, type, name):
global num_tested
global num_errors
exe = get_executable_path(name)
for the_file in os.listdir(path):
file_path = os.path.join(os.path.normpath(path), the_file)
if os.path.isfile(file_path):
if the_file.endswith(type):
num_tested = num_tested +1
if is_ascii(file_path):
print("### " + name + " " + file_path)
result = os.system("{0} \"{1}\"".format(exe, file_path))
print("### Result: " + str(result) + "\n")
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
elif os.path.isdir(file_path):
collect_files(file_path, type, name)
if __name__ == "__main__":
if not os.path.exists("build/"):
os.makedirs("build/")
os.chdir("build/")
os.system("cmake ..")
if os.system("cmake --build .") != 0:
print("Unable to build.")
exit(1)
os.chdir("..")
if not os.path.exists("glTF-Sample-Models/"):
os.system("git init glTF-Sample-Models")
os.chdir("glTF-Sample-Models")
os.system("git remote add origin https://github.com/KhronosGroup/glTF-Sample-Models.git")
os.system("git config core.sparsecheckout true")
f = open(".git/info/sparse-checkout", "w+")
f.write("2.0/*\n")
f.close()
os.system("git pull --depth=1 origin master")
os.chdir("..")
collect_files("glTF-Sample-Models/2.0/", ".glb", "cgltf_test")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "cgltf_test")
collect_files("glTF-Sample-Models/2.0/", ".glb", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_write")
result = os.system(get_executable_path("test_math"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
result = os.system(get_executable_path("test_strings"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
print("Tested files: " + str(num_tested))
print("Errors: " + str(num_errors))

View file

@ -1,92 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
static bool is_near(cgltf_float a, cgltf_float b)
{
return std::abs(a - b) < 10 * std::numeric_limits<cgltf_float>::min();
}
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options = {};
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (strstr(argv[1], "Draco"))
{
cgltf_free(data);
return 0;
}
if (result != cgltf_result_success)
return result;
//const cgltf_accessor* blobs = data->accessors;
cgltf_float element_float[16];
cgltf_uint element_uint[4];
for (cgltf_size blob_index = 0; blob_index < data->accessors_count; ++blob_index)
{
const cgltf_accessor* blob = data->accessors + blob_index;
if (blob->is_sparse)
{
cgltf_size nfloats = cgltf_num_components(blob->type) * blob->count;
cgltf_float* dense = (cgltf_float*) malloc(nfloats * sizeof(cgltf_float));
if (cgltf_accessor_unpack_floats(blob, dense, nfloats) < nfloats) {
printf("Unable to completely unpack a sparse accessor.\n");
return -1;
}
free(dense);
continue;
}
if (blob->has_max && blob->has_min)
{
cgltf_float min0 = std::numeric_limits<float>::max();
cgltf_float max0 = std::numeric_limits<float>::lowest();
for (cgltf_size index = 0; index < blob->count; index++)
{
cgltf_accessor_read_float(blob, index, element_float, 16);
min0 = std::min(min0, element_float[0]);
max0 = std::max(max0, element_float[0]);
}
if (!is_near(min0, blob->min[0]) || !is_near(max0, blob->max[0]))
{
printf("Computed [%f, %f] but expected [%f, %f]\n", min0, max0, blob->min[0], blob->max[0]);
return -1;
}
}
if (blob->has_max && blob->has_min && blob->component_type != cgltf_component_type_r_32f && blob->component_type != cgltf_component_type_invalid)
{
cgltf_uint min0 = std::numeric_limits<unsigned int>::max();
cgltf_uint max0 = std::numeric_limits<unsigned int>::lowest();
for ( cgltf_size index = 0; index < blob->count; index++ )
{
cgltf_accessor_read_uint( blob, index, element_uint, 4 );
min0 = std::min( min0, element_uint[0] );
max0 = std::max( max0, element_uint[0] );
}
if ( min0 != (unsigned int) blob->min[0] || max0 != (unsigned int) blob->max[0] )
{
printf( "Computed [%u, %u] but expected [%u, %u]\n", min0, max0, (unsigned int) blob->min[0], (unsigned int) blob->max[0] );
return -1;
}
}
}
cgltf_free(data);
return result;
}

View file

@ -1,52 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
// Performs matrix-vector multiplication, as in (4x4) * (4x1) = (4x1)
static void transform(const cgltf_float matrix[16], const cgltf_float source[4], cgltf_float target[4]) {
target[0] = matrix[0] * source[0] + matrix[4] * source[1] + matrix[ 8] * source[2] + matrix[12] * source[3];
target[1] = matrix[1] * source[0] + matrix[5] * source[1] + matrix[ 9] * source[2] + matrix[13] * source[3];
target[2] = matrix[2] * source[0] + matrix[6] * source[1] + matrix[10] * source[2] + matrix[14] * source[3];
target[3] = matrix[3] * source[0] + matrix[7] * source[1] + matrix[11] * source[2] + matrix[15] * source[3];
}
static void set(cgltf_float target[3], float x, float y, float z) {
target[0] = x;
target[1] = y;
target[2] = z;
}
static void check(cgltf_float target[3], float x, float y, float z) {
if (target[0] != x || target[1] != y || target[2] != z) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
cgltf_node node = {};
cgltf_float matrix[16];
cgltf_float source[4] = {1, 2, 3, 1};
cgltf_float target[4];
set(node.scale, 1, 1, 1);
set(node.translation, 1, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 2, 2, 3);
set(node.scale, 3, 1, 1);
set(node.translation, 0, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 3, 2, 3);
set(node.scale, 1, 3, 1);
set(node.translation, 1, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 2, 6, 3);
return 0;
}

View file

@ -1,57 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <cstring>
static void check(const char* a, const char* b, cgltf_size size) {
if (strcmp(a, b) != 0 || strlen(a) != size) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
char string[64];
cgltf_size size;
// cgltf_decode_string
strcpy(string, "");
size = cgltf_decode_string(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_string(string);
check(string, "nothing to replace", size);
strcpy(string, "\\\" \\/ \\\\ \\b \\f \\r \\n \\t \\u0030");
size = cgltf_decode_string(string);
check(string, "\" / \\ \b \f \r \n \t 0", size);
strcpy(string, "test \\u121b\\u130d\\u1294\\u1276\\u127d test");
size = cgltf_decode_string(string);
check(string, "test ማግኔቶች test", size);
// cgltf_decode_uri
strcpy(string, "");
size = cgltf_decode_uri(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_uri(string);
check(string, "nothing to replace", size);
strcpy(string, "%2F%D0%BA%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%D0%B8%D1%86%D0%B0");
size = cgltf_decode_uri(string);
check(string, "/кириллица", size);
strcpy(string, "test%20%E1%88%9B%E1%8C%8D%E1%8A%94%E1%89%B6%E1%89%BD%20test");
size = cgltf_decode_uri(string);
check(string, "test ማግኔቶች test", size);
strcpy(string, "%%2F%X%AX%%2F%%");
size = cgltf_decode_uri(string);
check(string, "%/%X%AX%/%%", size);
return 0;
}

View file

@ -1,45 +0,0 @@
#define CGLTF_IMPLEMENTATION
#define CGLTF_WRITE_IMPLEMENTATION
#include "../cgltf_write.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options = {};
cgltf_data* data0 = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data0);
// Silently skip over files that are unreadable since this is a writing test.
if (result != cgltf_result_success)
{
return cgltf_result_success;
}
result = cgltf_write_file(&options, "out.gltf", data0);
if (result != cgltf_result_success)
{
return result;
}
cgltf_data* data1 = NULL;
result = cgltf_parse_file(&options, "out.gltf", &data1);
if (result != cgltf_result_success)
{
return result;
}
if (data0->meshes_count != data1->meshes_count) {
return -1;
}
cgltf_free(data1);
cgltf_free(data0);
return cgltf_result_success;
}

View file

@ -1 +0,0 @@
github: mackron

View file

@ -1,12 +0,0 @@
docs
tests/testvectors/opus/*
!tests/testvectors/opus/DO_NOT_DELETE.md
tests/bin/*
!tests/bin/DO_NOT_DELETE.md
tests/build
tests/flac/include
tests/flac/lib
tests/testvectors/flac/tests/*
!tests/testvectors/flac/tests/DO_NOT_DELETE.md
todo
_private

View file

@ -1,3 +0,0 @@
[submodule "tests/external/miniaudio"]
path = tests/external/miniaudio
url = https://github.com/dr-soft/miniaudio.git

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,676 +0,0 @@
// Public Domain. See "unlicense" statement at the end of this file.
// NOTE: This is still very much work in progress and is only being updated as I need it. You don't want to be using this library
// in its current state.
// QUICK NOTES
// - This library does not use SSE for its basic types (vec4, etc.). Rationale: 1) It keeps things simple; 2) SSE is not always
// faster than the FPU(s) on modern CPUs; 3) The library can always implement functions that work on __m128 variables directly
// in the future if the need arises; 4) It doesn't work well with the pass-by-value API this library uses.
// - Use DISABLE_SSE to disable SSE optimized functions.
// - Angles are always specified in radians, unless otherwise noted. Rationale: Consistency with the standard library and most
// other math libraries.
// - Use radians() and degrees() to convert between the two.
#ifndef dr_math_h
#define dr_math_h
#include <math.h>
#if defined(_MSC_VER)
#define DR_MATHCALL static __forceinline
#else
#define DR_MATHCALL static inline
#endif
#define DR_PI 3.14159265358979323846
#define DR_PIF 3.14159265358979323846f
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
float x;
float y;
float z;
float w;
} vec4;
typedef struct
{
float x;
float y;
float z;
} vec3;
typedef struct
{
float x;
float y;
} vec2;
typedef struct
{
vec4 col[4];
} mat4;
typedef struct
{
float x;
float y;
float z;
float w;
} quat;
// Radians to degrees.
DR_MATHCALL float dr_degrees(float radians)
{
return radians * 57.29577951308232087685f;
}
// Degrees to radians.
DR_MATHCALL float dr_radians(float degrees)
{
return degrees * 0.01745329251994329577f;
}
///////////////////////////////////////////////
//
// VEC4
//
///////////////////////////////////////////////
DR_MATHCALL vec4 vec4f(float x, float y, float z, float w)
{
vec4 result;
result.x = x;
result.y = y;
result.z = z;
result.w = w;
return result;
}
DR_MATHCALL vec4 vec4v(const float* v)
{
return vec4f(v[0], v[1], v[2], v[3]);
}
DR_MATHCALL vec4 vec4_zero()
{
return vec4f(0, 0, 0, 0);
}
DR_MATHCALL vec4 vec4_one()
{
return vec4f(1, 1, 1, 1);
}
DR_MATHCALL vec4 vec4_add(vec4 a, vec4 b)
{
return vec4f(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
DR_MATHCALL vec4 vec4_sub(vec4 a, vec4 b)
{
return vec4f(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}
DR_MATHCALL vec4 vec4_mul(vec4 a, vec4 b)
{
return vec4f(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
DR_MATHCALL vec4 vec4_mul_1f(vec4 a, float x)
{
return vec4f(a.x * x, a.y * x, a.z * x, a.w * x);
}
DR_MATHCALL vec4 vec4_mul_mat4(vec4 v, mat4 m)
{
const vec4 m0 = m.col[0];
const vec4 m1 = m.col[1];
const vec4 m2 = m.col[2];
const vec4 m3 = m.col[3];
return vec4f(
m0.x*v.x + m0.y*v.y + m0.z*v.z + m0.w*v.w,
m1.x*v.x + m1.y*v.y + m1.z*v.z + m1.w*v.w,
m2.x*v.x + m2.y*v.y + m2.z*v.z + m2.w*v.w,
m3.x*v.x + m3.y*v.y + m3.z*v.z + m3.w*v.w
);
}
DR_MATHCALL vec4 vec4_div(vec4 a, vec4 b)
{
return vec4f(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
}
///////////////////////////////////////////////
//
// VEC3
//
///////////////////////////////////////////////
DR_MATHCALL vec3 vec3f(float x, float y, float z)
{
vec3 result;
result.x = x;
result.y = y;
result.z = z;
return result;
}
DR_MATHCALL vec3 vec3v(const float* v)
{
return vec3f(v[0], v[1], v[2]);
}
DR_MATHCALL vec3 vec3_zero()
{
return vec3f(0, 0, 0);
}
DR_MATHCALL vec3 vec3_one()
{
return vec3f(1, 1, 1);
}
DR_MATHCALL vec3 vec3_add(vec3 a, vec3 b)
{
return vec3f(a.x + b.x, a.y + b.y, a.z + b.z);
}
DR_MATHCALL vec3 vec3_sub(vec3 a, vec3 b)
{
return vec3f(a.x - b.x, a.y - b.y, a.z - b.z);
}
DR_MATHCALL vec3 vec3_mul(vec3 a, vec3 b)
{
return vec3f(a.x * b.x, a.y * b.y, a.z * b.z);
}
DR_MATHCALL vec3 vec3_mul_1f(vec3 a, float x)
{
return vec3f(a.x * x, a.y * x, a.z * x);
}
DR_MATHCALL vec3 vec3_div(vec3 a, vec3 b)
{
return vec3f(a.x / b.x, a.y / b.y, a.z / b.z);
}
DR_MATHCALL float vec3_dot(vec3 a, vec3 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
DR_MATHCALL float vec3_length2(vec3 a)
{
return vec3_dot(a, a);
}
DR_MATHCALL float vec3_length(vec3 a)
{
return sqrtf(vec3_length2(a));
}
DR_MATHCALL vec3 vec3_normalize(vec3 a)
{
float len = vec3_length(a);
return vec3f(
a.x / len,
a.y / len,
a.z / len
);
}
DR_MATHCALL vec3 vec3_cross(vec3 a, vec3 b)
{
return vec3f(
a.y*b.z - a.z*b.y,
a.z*b.x - a.x*b.z,
a.x*b.y - a.y*b.x
);
}
DR_MATHCALL vec3 vec3_triangle_normal(vec3 p1, vec3 p2, vec3 p3)
{
vec3 u = vec3_sub(p2, p1);
vec3 v = vec3_sub(p3, p1);
return vec3_normalize(vec3_cross(u, v));
}
///////////////////////////////////////////////
//
// VEC2
//
///////////////////////////////////////////////
DR_MATHCALL vec2 vec2f(float x, float y)
{
vec2 result;
result.x = x;
result.y = y;
return result;
}
DR_MATHCALL vec2 vec2v(const float* v)
{
return vec2f(v[0], v[1]);
}
DR_MATHCALL vec2 vec2_zero()
{
return vec2f(0, 0);
}
DR_MATHCALL vec2 vec2_one()
{
return vec2f(1, 1);
}
DR_MATHCALL vec2 vec2_add(vec2 a, vec2 b)
{
return vec2f(a.x + b.x, a.y + b.y);
}
DR_MATHCALL vec2 vec2_sub(vec2 a, vec2 b)
{
return vec2f(a.x - b.x, a.y - b.y);
}
DR_MATHCALL vec2 vec2_mul(vec2 a, vec2 b)
{
return vec2f(a.x * b.x, a.y * b.y);
}
DR_MATHCALL vec2 vec2_mul_1f(vec2 a, float x)
{
return vec2f(a.x * x, a.y * x);
}
DR_MATHCALL vec2 vec2_div(vec2 a, vec2 b)
{
return vec2f(a.x / b.x, a.y / b.y);
}
DR_MATHCALL float vec2_dot(vec2 a, vec2 b)
{
return a.x*b.x + a.y*b.y;
}
DR_MATHCALL float vec2_length2(vec2 a)
{
return vec2_dot(a, a);
}
DR_MATHCALL float vec2_length(vec2 a)
{
return sqrtf(vec2_length2(a));
}
DR_MATHCALL vec2 vec2_normalize(vec2 a)
{
float len = vec2_length(a);
return vec2f(
a.x / len,
a.y / len
);
}
DR_MATHCALL float vec2_angle(vec2 a, vec2 b)
{
return atanf(a.y / a.x) - atanf(b.y / b.x);
}
DR_MATHCALL vec2 vec2_rotate(vec2 a, float angleInRadians)
{
float c = cosf(angleInRadians);
float s = sinf(angleInRadians);
return vec2f(
a.x*c - a.y*s,
a.x*s + a.y*c
);
}
///////////////////////////////////////////////
//
// MAT4
//
///////////////////////////////////////////////
DR_MATHCALL mat4 mat4f(vec4 col0, vec4 col1, vec4 col2, vec4 col3)
{
mat4 result;
result.col[0] = col0;
result.col[1] = col1;
result.col[2] = col2;
result.col[3] = col3;
return result;
}
DR_MATHCALL mat4 mat4_identity()
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, 1, 0, 0);
result.col[2] = vec4f(0, 0, 1, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_ortho(float left, float right, float bottom, float top, float znear, float zfar)
{
float rml = right - left;
float tmb = top - bottom;
float fmn = zfar - znear;
float rpl = right + left;
float tpb = top + bottom;
float fpn = zfar + znear;
mat4 result;
result.col[0] = vec4f(2/rml, 0, 0, 0);
result.col[1] = vec4f(0, 2/tmb, 0, 0);
result.col[2] = vec4f(0, 0, -2/fmn, 0);
result.col[3] = vec4f(-(rpl/rml), -(tpb/tmb), -(fpn/fmn), 1);
return result;
}
DR_MATHCALL mat4 mat4_perspective(float fovy, float aspect, float znear, float zfar)
{
float f = (float)tan(DR_PI/2 - fovy/2);
mat4 result;
result.col[0] = vec4f(f / aspect, 0, 0, 0);
result.col[1] = vec4f(0, f, 0, 0);
result.col[2] = vec4f(0, 0, (zfar + znear) / (znear - zfar), -1);
result.col[3] = vec4f(0, 0, (2 * zfar * znear) / (znear - zfar), 0);
return result;
}
DR_MATHCALL mat4 mat4_vulkan_clip_correction()
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, -1, 0, 0);
result.col[2] = vec4f(0, 0, 0.5f, 0);
result.col[3] = vec4f(0, 0, 0.5f, 1);
return result;
}
DR_MATHCALL mat4 mat4_translate(vec3 translation)
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, 1, 0, 0);
result.col[2] = vec4f(0, 0, 1, 0);
result.col[3] = vec4f(translation.x, translation.y, translation.z, 1);
return result;
}
DR_MATHCALL mat4 mat4_rotate(float angleInRadians, vec3 axis)
{
float c = cosf(angleInRadians);
float s = sinf(angleInRadians);
float x = axis.x;
float y = axis.y;
float z = axis.z;
float xx = x*x;
float xy = x*y;
float xz = x*z;
float yy = y*y;
float yz = y*z;
float zz = z*z;
float xs = x*s;
float ys = y*s;
float zs = z*s;
mat4 result;
result.col[0] = vec4f(xx * (1 - c) + c, xy * (1 - c) - zs, xz * (1 - c) + ys, 0);
result.col[1] = vec4f(xy * (1 - c) + zs, yy * (1 - c) + c, yz * (1 - c) - xs, 0);
result.col[2] = vec4f(xz * (1 - c) - ys, yz * (1 - c) + xs, zz * (1 - c) + c, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_scale(vec3 scale)
{
mat4 result;
result.col[0] = vec4f(scale.x, 0, 0, 0);
result.col[1] = vec4f(0, scale.y, 0, 0);
result.col[2] = vec4f(0, 0, scale.z, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_mul(mat4 a, mat4 b)
{
const vec4 a0 = a.col[0];
const vec4 a1 = a.col[1];
const vec4 a2 = a.col[2];
const vec4 a3 = a.col[3];
const vec4 b0 = b.col[0];
const vec4 b1 = b.col[1];
const vec4 b2 = b.col[2];
const vec4 b3 = b.col[3];
mat4 result;
result.col[0] = vec4f(
a0.x*b0.x + a1.x*b0.y + a2.x*b0.z + a3.x*b0.w,
a0.y*b0.x + a1.y*b0.y + a2.y*b0.z + a3.y*b0.w,
a0.z*b0.x + a1.z*b0.y + a2.z*b0.z + a3.z*b0.w,
a0.w*b0.x + a1.w*b0.y + a2.w*b0.z + a3.w*b0.w
);
result.col[1] = vec4f(
a0.x*b1.x + a1.x*b1.y + a2.x*b1.z + a3.x*b1.w,
a0.y*b1.x + a1.y*b1.y + a2.y*b1.z + a3.y*b1.w,
a0.z*b1.x + a1.z*b1.y + a2.z*b1.z + a3.z*b1.w,
a0.w*b1.x + a1.w*b1.y + a2.w*b1.z + a3.w*b1.w
);
result.col[2] = vec4f(
a0.x*b2.x + a1.x*b2.y + a2.x*b2.z + a3.x*b2.w,
a0.y*b2.x + a1.y*b2.y + a2.y*b2.z + a3.y*b2.w,
a0.z*b2.x + a1.z*b2.y + a2.z*b2.z + a3.z*b2.w,
a0.w*b2.x + a1.w*b2.y + a2.w*b2.z + a3.w*b2.w
);
result.col[3] = vec4f(
a0.x*b3.x + a1.x*b3.y + a2.x*b3.z + a3.x*b3.w,
a0.y*b3.x + a1.y*b3.y + a2.y*b3.z + a3.y*b3.w,
a0.z*b3.x + a1.z*b3.y + a2.z*b3.z + a3.z*b3.w,
a0.w*b3.x + a1.w*b3.y + a2.w*b3.z + a3.w*b3.w
);
return result;
}
DR_MATHCALL vec4 mat4_mul_vec4(mat4 m, vec4 v)
{
const vec4 m0 = m.col[0];
const vec4 m1 = m.col[1];
const vec4 m2 = m.col[2];
const vec4 m3 = m.col[3];
return vec4f(
m0.x*v.x + m1.x*v.y + m2.x*v.z + m3.x*v.w,
m0.y*v.x + m1.y*v.y + m2.y*v.z + m3.y*v.w,
m0.z*v.x + m1.z*v.y + m2.z*v.z + m3.z*v.w,
m0.w*v.x + m1.w*v.y + m2.w*v.z + m3.w*v.w
);
}
///////////////////////////////////////////////
//
// QUAT
//
///////////////////////////////////////////////
DR_MATHCALL quat quatf(float x, float y, float z, float w)
{
quat result;
result.x = x;
result.y = y;
result.z = z;
result.w = w;
return result;
}
DR_MATHCALL quat quatv(const float* v)
{
return quatf(v[0], v[1], v[2], v[3]);
}
DR_MATHCALL quat quat_identity()
{
return quatf(0, 0, 0, 1);
}
///////////////////////////////////////////////
//
// TRANSFORM
//
///////////////////////////////////////////////
typedef struct
{
vec3 position;
quat rotation;
vec3 scale;
}transform_t;
DR_MATHCALL transform_t transform_init(vec3 position, quat rotation, vec3 scale)
{
transform_t result;
result.position = position;
result.rotation = rotation;
result.scale = scale;
return result;
}
DR_MATHCALL transform_t transform_identity()
{
transform_t result;
result.position = vec3_zero();
result.rotation = quat_identity();
result.scale = vec3_one();
return result;
}
DR_MATHCALL transform_t transform_translate(transform_t transform, vec3 offset)
{
transform_t result = transform;
result.position = vec3_add(transform.position, offset);
return result;
}
///////////////////////////////////////////////
//
// SSE IMPLEMENTATION
//
///////////////////////////////////////////////
// Not supporting SSE on x86/MSVC due to pass-by-value errors with aligned types.
#if (defined(_MSC_VER) && defined(_M_X64)) || defined(__SSE2__)
#define SUPPORTS_SSE
#endif
#if !defined(DISABLE_SSE) && defined(SUPPORTS_SSE)
#define ENABLE_SSE
#endif
#ifdef ENABLE_SSE
#if defined(__MINGW32__)
#include <intrin.h>
#endif
#include <emmintrin.h>
#endif
#ifdef __cplusplus
}
#endif
#endif //dr_math_h
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/

File diff suppressed because it is too large Load diff

View file

@ -1,793 +0,0 @@
// PCX image loader. Public domain. See "unlicense" statement at the end of this file.
// dr_pcx - v0.3.1 - 2018-09-11
//
// David Reid - mackron@gmail.com
// USAGE
//
// dr_pcx is a single-file library. To use it, do something like the following in one .c file.
// #define DR_PCX_IMPLEMENTATION
// #include "dr_pcx.h"
//
// You can then #include this file in other parts of the program as you would with any other header file. Do something like
// the following to load and decode an image:
//
// int width;
// int height;
// int components
// drpcx_uint8* pImageData = drpcx_load_file("my_image.pcx", DRPCX_FALSE, &width, &height, &components, 0);
// if (pImageData == NULL) {
// // Failed to load image.
// }
//
// ...
//
// drpcx_free(pImageData);
//
// The boolean parameter (second argument in the above example) is whether or not the image should be flipped upside down.
//
//
//
// OPTIONS
// #define these options before including this file.
//
// #define DR_PCX_NO_STDIO
// Disable drpcx_load_file().
//
//
//
// QUICK NOTES
// - 2-bpp/4-plane and 4-bpp/1-plane formats have not been tested.
#ifndef dr_pcx_h
#define dr_pcx_h
#include <stddef.h>
#if defined(_MSC_VER) && _MSC_VER < 1600
typedef signed char drpcx_int8;
typedef unsigned char drpcx_uint8;
typedef signed short drpcx_int16;
typedef unsigned short drpcx_uint16;
typedef signed int drpcx_int32;
typedef unsigned int drpcx_uint32;
typedef signed __int64 drpcx_int64;
typedef unsigned __int64 drpcx_uint64;
#else
#include <stdint.h>
typedef int8_t drpcx_int8;
typedef uint8_t drpcx_uint8;
typedef int16_t drpcx_int16;
typedef uint16_t drpcx_uint16;
typedef int32_t drpcx_int32;
typedef uint32_t drpcx_uint32;
typedef int64_t drpcx_int64;
typedef uint64_t drpcx_uint64;
#endif
typedef drpcx_uint8 drpcx_bool8;
typedef drpcx_uint32 drpcx_bool32;
#define DRPCX_TRUE 1
#define DRPCX_FALSE 0
#ifdef __cplusplus
extern "C" {
#endif
// Callback for when data is read. Return value is the number of bytes actually read.
typedef size_t (* drpcx_read_proc)(void* userData, void* bufferOut, size_t bytesToRead);
// Loads a PCX file using the given callbacks.
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
// Frees memory returned by drpcx_load() and family.
void drpcx_free(void* pReturnValueFromLoad);
#ifndef DR_PCX_NO_STDIO
// Loads an PCX file from an actual file.
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
#endif
// Helper for loading an PCX file from a block of memory.
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
#ifdef __cplusplus
}
#endif
#endif // dr_pcx_h
///////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION
//
///////////////////////////////////////////////////////////////////////////////
#ifdef DR_PCX_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef DR_PCX_NO_STDIO
#include <stdio.h>
static size_t drpcx__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
{
return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
}
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
FILE* pFile;
#ifdef _MSC_VER
if (fopen_s(&pFile, filename, "rb") != 0) {
return NULL;
}
#else
pFile = fopen(filename, "rb");
if (pFile == NULL) {
return NULL;
}
#endif
drpcx_uint8* pImageData = drpcx_load(drpcx__on_read_stdio, pFile, flipped, x, y, internalComponents, desiredComponents);
fclose(pFile);
return pImageData;
}
#endif // DR_PCX_NO_STDIO
typedef struct
{
// A pointer to the beginning of the data. We use a char as the type here for easy offsetting.
const unsigned char* data;
size_t dataSize;
size_t currentReadPos;
} drpcx_memory;
static size_t drpcx__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
{
drpcx_memory* memory = (drpcx_memory*)pUserData;
assert(memory != NULL);
assert(memory->dataSize >= memory->currentReadPos);
size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
if (bytesToRead > 0) {
memcpy(bufferOut, memory->data + memory->currentReadPos, bytesToRead);
memory->currentReadPos += bytesToRead;
}
return bytesToRead;
}
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
drpcx_memory memory;
memory.data = (const unsigned char*)data;
memory.dataSize = dataSize;
memory.currentReadPos = 0;
return drpcx_load(drpcx__on_read_memory, &memory, flipped, x, y, internalComponents, desiredComponents);
}
typedef struct
{
drpcx_uint8 header;
drpcx_uint8 version;
drpcx_uint8 encoding;
drpcx_uint8 bpp;
drpcx_uint16 left;
drpcx_uint16 top;
drpcx_uint16 right;
drpcx_uint16 bottom;
drpcx_uint16 hres;
drpcx_uint16 vres;
drpcx_uint8 palette16[48];
drpcx_uint8 reserved1;
drpcx_uint8 bitPlanes;
drpcx_uint16 bytesPerLine;
drpcx_uint16 paletteType;
drpcx_uint16 screenSizeH;
drpcx_uint16 screenSizeV;
drpcx_uint8 reserved2[54];
} drpcx_header;
typedef struct
{
drpcx_read_proc onRead;
void* pUserData;
drpcx_bool32 flipped;
drpcx_header header;
drpcx_uint32 width;
drpcx_uint32 height;
drpcx_uint32 components; // 3 = RGB; 4 = RGBA. Only 3 and 4 are supported.
drpcx_uint8* pImageData;
} drpcx;
static drpcx_uint8 drpcx__read_byte(drpcx* pPCX)
{
drpcx_uint8 byte = 0;
pPCX->onRead(pPCX->pUserData, &byte, 1);
return byte;
}
static drpcx_uint8* drpcx__row_ptr(drpcx* pPCX, drpcx_uint32 row)
{
drpcx_uint32 stride = pPCX->width * pPCX->components;
drpcx_uint8* pRow = pPCX->pImageData;
if (pPCX->flipped) {
pRow += (pPCX->height - row - 1) * stride;
} else {
pRow += row * stride;
}
return pRow;
}
static drpcx_uint8 drpcx__rle(drpcx* pPCX, drpcx_uint8* pRLEValueOut)
{
drpcx_uint8 rleCount;
drpcx_uint8 rleValue;
rleValue = drpcx__read_byte(pPCX);
if ((rleValue & 0xC0) == 0xC0) {
rleCount = rleValue & 0x3F;
rleValue = drpcx__read_byte(pPCX);
} else {
rleCount = 1;
}
*pRLEValueOut = rleValue;
return rleCount;
}
drpcx_bool32 drpcx__decode_1bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
switch (pPCX->header.bitPlanes)
{
case 1:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
drpcx_uint8 mask = (1 << (7 - bit));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
pRow[0] = paletteIndex * 255;
pRow[1] = paletteIndex * 255;
pRow[2] = paletteIndex * 255;
pRow += 3;
}
}
}
return DRPCX_TRUE;
} break;
case 2:
case 3:
case 4:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
drpcx_uint8 mask = (1 << (7 - bit));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
pRow[0] |= ((paletteIndex & 0x01) << c);
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
}
default: return DRPCX_FALSE;
}
}
drpcx_bool32 drpcx__decode_2bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
switch (pPCX->header.bitPlanes)
{
case 1:
{
drpcx_uint8 paletteCGA[48];
paletteCGA[ 0] = 0x00; paletteCGA[ 1] = 0x00; paletteCGA[ 2] = 0x00; // #000000
paletteCGA[ 3] = 0x00; paletteCGA[ 4] = 0x00; paletteCGA[ 5] = 0xAA; // #0000AA
paletteCGA[ 6] = 0x00; paletteCGA[ 7] = 0xAA; paletteCGA[ 8] = 0x00; // #00AA00
paletteCGA[ 9] = 0x00; paletteCGA[10] = 0xAA; paletteCGA[11] = 0xAA; // #00AAAA
paletteCGA[12] = 0xAA; paletteCGA[13] = 0x00; paletteCGA[14] = 0x00; // #AA0000
paletteCGA[15] = 0xAA; paletteCGA[16] = 0x00; paletteCGA[17] = 0xAA; // #AA00AA
paletteCGA[18] = 0xAA; paletteCGA[19] = 0x55; paletteCGA[20] = 0x00; // #AA5500
paletteCGA[21] = 0xAA; paletteCGA[22] = 0xAA; paletteCGA[23] = 0xAA; // #AAAAAA
paletteCGA[24] = 0x55; paletteCGA[25] = 0x55; paletteCGA[26] = 0x55; // #555555
paletteCGA[27] = 0x55; paletteCGA[28] = 0x55; paletteCGA[29] = 0xFF; // #5555FF
paletteCGA[30] = 0x55; paletteCGA[31] = 0xFF; paletteCGA[32] = 0x55; // #55FF55
paletteCGA[33] = 0x55; paletteCGA[34] = 0xFF; paletteCGA[35] = 0xFF; // #55FFFF
paletteCGA[36] = 0xFF; paletteCGA[37] = 0x55; paletteCGA[38] = 0x55; // #FF5555
paletteCGA[39] = 0xFF; paletteCGA[40] = 0x55; paletteCGA[41] = 0xFF; // #FF55FF
paletteCGA[42] = 0xFF; paletteCGA[43] = 0xFF; paletteCGA[44] = 0x55; // #FFFF55
paletteCGA[45] = 0xFF; paletteCGA[46] = 0xFF; paletteCGA[47] = 0xFF; // #FFFFFF
drpcx_uint8 cgaBGColor = pPCX->header.palette16[0] >> 4;
drpcx_uint8 i = (pPCX->header.palette16[3] & 0x20) >> 5;
drpcx_uint8 p = (pPCX->header.palette16[3] & 0x40) >> 6;
//drpcx_uint8 c = (pPCX->header.palette16[3] & 0x80) >> 7; // Color or monochrome. How is monochrome handled?
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; bit < 4; ++bit) {
if (x*4 + bit < pPCX->width) {
drpcx_uint8 mask = (3 << ((3 - bit) * 2));
drpcx_uint8 paletteIndex = (rleValue & mask) >> ((3 - bit) * 2);
drpcx_uint8 cgaIndex;
if (paletteIndex == 0) { // Background.
cgaIndex = cgaBGColor;
} else { // Foreground
cgaIndex = (((paletteIndex << 1) + p) + (i << 3));
}
pRow[0] = paletteCGA[cgaIndex*3 + 0];
pRow[1] = paletteCGA[cgaIndex*3 + 1];
pRow[2] = paletteCGA[cgaIndex*3 + 2];
pRow += 3;
}
}
}
}
// TODO: According to http://www.fysnet.net/pcxfile.htm, we should use the palette at the end of the file
// instead of the standard CGA palette if the version is equal to 5. With my test files the palette
// at the end of the file does not exist. Research this one.
if (pPCX->header.version == 5) {
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
if (paletteMarker == 0x0C) {
// TODO: Implement Me.
}
}
return DRPCX_TRUE;
};
case 4:
{
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
// TODO: Test Me.
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bitpair = 0; (bitpair < 4) && ((x*4 + bitpair) < pPCX->width); ++bitpair) {
drpcx_uint8 mask = (4 << (3 - bitpair));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (3 - bitpair);
pRow[0] |= ((paletteIndex & 0x03) << (c*2));
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
};
default: return DRPCX_FALSE;
}
}
drpcx_bool32 drpcx__decode_4bit(drpcx* pPCX)
{
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
// TODO: Test Me.
if (pPCX->header.bitPlanes > 1) {
return DRPCX_FALSE;
}
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int nibble = 0; (nibble < 2) && ((x*2 + nibble) < pPCX->width); ++nibble)
{
drpcx_uint8 mask = (4 << (1 - nibble));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (1 - nibble);
pRow[0] |= ((paletteIndex & 0x0F) << (c*4));
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
}
drpcx_bool32 drpcx__decode_8bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
drpcx_uint32 stride = pPCX->width * pPCX->components;
switch (pPCX->header.bitPlanes)
{
case 1:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
if (x < pPCX->width) {
pRow[0] = rleValue;
pRow[1] = rleValue;
pRow[2] = rleValue;
pRow += 3;
}
}
}
// At this point we can know if we are dealing with a palette or a grayscale image by checking the next byte. If it's equal to 0x0C, we
// need to do a simple palette lookup.
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
if (paletteMarker == 0x0C) {
// A palette is present - we need to do a second pass.
drpcx_uint8 palette256[768];
if (pPCX->onRead(pPCX->pUserData, palette256, sizeof(palette256)) != sizeof(palette256)) {
return DRPCX_FALSE;
}
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = pPCX->pImageData + (y * stride);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 index = pRow[0];
pRow[0] = palette256[index*3 + 0];
pRow[1] = palette256[index*3 + 1];
pRow[2] = palette256[index*3 + 2];
pRow += 3;
}
}
}
return DRPCX_TRUE;
}
case 3:
case 4:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
if (x < pPCX->width) {
pRow[c] = rleValue;
pRow += pPCX->components;
}
}
}
}
return DRPCX_TRUE;
}
}
return DRPCX_TRUE;
}
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
if (onRead == NULL) return NULL;
if (desiredComponents > 4) return NULL;
drpcx pcx;
pcx.onRead = onRead;
pcx.pUserData = pUserData;
pcx.flipped = flipped;
if (onRead(pUserData, &pcx.header, sizeof(pcx.header)) != sizeof(pcx.header)) {
return NULL; // Failed to read the header.
}
if (pcx.header.header != 10) {
return NULL; // Not a PCX file.
}
if (pcx.header.encoding != 1) {
return NULL; // Not supporting non-RLE encoding. Would assume a value of 0 indicates raw, unencoded, but that is apparently never used.
}
if (pcx.header.bpp != 1 && pcx.header.bpp != 2 && pcx.header.bpp != 4 && pcx.header.bpp != 8) {
return NULL; // Unsupported pixel format.
}
if (pcx.header.left > pcx.header.right) {
drpcx_uint16 temp = pcx.header.left;
pcx.header.left = pcx.header.right;
pcx.header.right = temp;
}
if (pcx.header.top > pcx.header.bottom) {
drpcx_uint16 temp = pcx.header.top;
pcx.header.top = pcx.header.bottom;
pcx.header.bottom = temp;
}
pcx.width = pcx.header.right - pcx.header.left + 1;
pcx.height = pcx.header.bottom - pcx.header.top + 1;
pcx.components = (pcx.header.bpp == 8 && pcx.header.bitPlanes == 4) ? 4 : 3;
size_t dataSize = pcx.width * pcx.height * pcx.components;
pcx.pImageData = (drpcx_uint8*)calloc(1, dataSize); // <-- Clearing to zero is important! Required for proper decoding.
if (pcx.pImageData == NULL) {
return NULL; // Failed to allocate memory.
}
drpcx_bool32 result = DRPCX_FALSE;
switch (pcx.header.bpp)
{
case 1:
{
result = drpcx__decode_1bit(&pcx);
} break;
case 2:
{
result = drpcx__decode_2bit(&pcx);
} break;
case 4:
{
result = drpcx__decode_4bit(&pcx);
} break;
case 8:
{
result = drpcx__decode_8bit(&pcx);
} break;
}
if (!result) {
free(pcx.pImageData);
return NULL;
}
// There's an annoying amount of branching when loading PCX files so for simplicity I'm doing the component conversion as
// a second pass.
if (desiredComponents == 0) desiredComponents = pcx.components;
if (desiredComponents != (int)pcx.components) {
drpcx_uint8* pNewImageData = (drpcx_uint8*)malloc(pcx.width * pcx.height * desiredComponents);
if (pNewImageData == NULL) {
free(pcx.pImageData);
return NULL;
}
drpcx_uint8* pSrcData = pcx.pImageData;
drpcx_uint8* pDstData = pNewImageData;
if (desiredComponents < (int)pcx.components) {
// We're reducing the number of components. Just drop the excess.
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
for (int c = 0; c < desiredComponents; ++c) {
pDstData[c] = pSrcData[c];
}
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else {
// We're increasing the number of components. Always ensure the alpha channel is set to 0xFF.
if (pcx.components == 1) {
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
for (int c = 0; c < desiredComponents; ++c) {
pDstData[c] = pSrcData[0];
}
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else if (pcx.components == 2) {
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
pDstData[0] = pSrcData[0];
pDstData[1] = pSrcData[1];
pDstData[2] = 0x00;
if (desiredComponents == 4) pDstData[3] = 0xFF;
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else {
assert(pcx.components == 3);
assert(desiredComponents == 4);
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
pDstData[0] = pSrcData[0];
pDstData[1] = pSrcData[1];
pDstData[2] = pSrcData[2];
pDstData[3] = 0xFF;
pSrcData += pcx.components;
pDstData += desiredComponents;
}
}
}
free(pcx.pImageData);
pcx.pImageData = pNewImageData;
}
if (x) *x = pcx.width;
if (y) *y = pcx.height;
if (internalComponents) *internalComponents = pcx.components;
return pcx.pImageData;
}
void drpcx_free(void* pReturnValueFromLoad)
{
free(pReturnValueFromLoad);
}
#endif // DR_PCX_IMPLEMENTATION
// REVISION HISTORY
//
// v0.3.1 - 2018-09-11
// - Styling fixes.
// - Fix a typo.
//
// v0.3 - 2018-02-08
// - API CHANGE: Rename dr_* types to drpcx_*.
//
// v0.2c - 2018-02-07
// - Fix a crash.
//
// v0.2b - 2018-02-02
// - Fix compilation error.
//
// v0.2a - 2017-07-16
// - Change underlying type for booleans to unsigned.
//
// v0.2 - 2016-10-28
// - API CHANGE: Add a parameter to drpcx_load() and family to control the number of output components.
// - Use custom sized types rather than built-in ones to improve support for older MSVC compilers.
//
// v0.1c - 2016-10-23
// - A minor change to drpcx_bool8 and drpcx_bool32 types.
//
// v0.1b - 2016-10-11
// - Use drpcx_bool32 instead of the built-in "bool" type. The reason for this change is that it helps maintain API/ABI consistency
// between C and C++ builds.
//
// v0.1a - 2016-09-18
// - Change date format to ISO 8601 (YYYY-MM-DD)
//
// v0.1 - 2016-05-04
// - Initial versioned release.
// TODO
// - Test 2-bpp/4-plane and 4-bpp/1-plane formats.
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/

View file

@ -1,49 +0,0 @@
Building
========
Move into this directory and run the build script for the relevant platform. Run from this directory.
clear && ./build_opus && ./bin/dr_opus_test_0
Alternatively you can compile a specific test manually:
clear && gcc ./opus/dr_opus_test_0 -o ./bin/dr_opus_test_0 && ./bin/dr_opus_test_0
Test vectors will be loaded from the "testvectors" folder, relative to this directory. Therefore, you need to run
each test program from this directory:
./bin/dr_opus_test_0
Building and Running WAV Tests
------------------------------
The WAV tests use libsndfile as a benchmark. The tests programs dynamically link to libsndfile at runtime which
means you don't need to link to it at compile time. However, you will need the headers installed in a standard
location. The batch files for the Windows build will allow you to customize the include path. On the Windows build
you will need to drop two versions of libsndfile-1.dll into the bin directory. For the 32-bit build you need to
name it libsndfile-1-x86.dll and for the 64-bit build you need to name it libsndfile-1-x64.dll.
Test Vectors
============
In order to run certain tests you will need to download test vectors for the relevant project and place them into the
"testvectors" folder.
Opus
----
- Download both the original and new test vectors from https://opus-codec.org/testvectors/ and place them into
the "testvectors/opus" folder.
- Download the Ogg Opus test vectors from https://wiki.xiph.org/OggOpus/testvectors and place them into the
"testvectors/opus" folder.
- The folder structure should like like the following:
- testvectors
- opus
- opus_testvectors
- opus_newvectors
- oggopus
- failure_cases
- opus_multichannel_examples
FLAC
----
- Download the FLAC testbench from https://wiki.hydrogenaud.io/index.php?title=FLAC_decoder_testbench and place
them into the "testvectors/flac/testbench" folder.

View file

@ -1,4 +0,0 @@
#!/bin/sh
gcc ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0 -std=c89 -ansi -pedantic -O3 -s -Wall -ldl
gcc ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl
gcc ./flac/dr_flac_seeking.c -o ./bin/dr_flac_seeking -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl

View file

@ -1,37 +0,0 @@
:: NOTES
::
:: These tests use libFLAC as a benchmark. Since Windows doesn't have good standard paths for library files, this
:: script will use "flac/include" as an additional search path for headers and "flac/lib/win32" as an additional
:: search path for libraries. You will need to place FLAC headers in the "flac/include/FLAC" directory and libogg
:: headers int the "flac/include/ogg" directory.
::
:: Examples are linked against "-lFLAC" and "-logg". These need to be placed in a standard directory or "flac/lib/win32".
@echo off
SET c_compiler=gcc
SET cpp_compiler=g++
:: Configure the "arch" option to test different instruction sets.
SET arch=
SET arch=-msse4.1
::SET arch=-mfpu=neon
:: libFLAC and libogg are required for benchmarking.
SET libFLAC=-I./flac/include -L./flac/lib/win32 -lFLAC -logg
:: C options
SET c_options=-std=c89 -ansi
:: C++ options
SET cpp_options=
SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libFLAC%
SET buildc=%c_compiler% %c_options%
SET buildcpp=%cpp_compiler% %cpp_options%
@echo on
%buildc% ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0.exe %options%
%buildcpp% ./flac/dr_flac_test_0.cpp -o ./bin/dr_flac_test_0_cpp.exe %options%
%buildc% ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding.exe %options%
%buildcpp% ./flac/dr_flac_decoding.cpp -o ./bin/dr_flac_decoding_cpp.exe %options%

View file

@ -1,2 +0,0 @@
gcc ./mp3/dr_mp3_test_0.c -o ./bin/dr_mp3_test_0.exe -std=c89 -ansi -pedantic -Wall
g++ ./mp3/dr_mp3_test_0.cpp -o ./bin/dr_mp3_test_0.exe -pedantic -Wall

Some files were not shown because too many files have changed in this diff Show more