Compare commits
No commits in common. "50386c0e040bf6313424d22138b14369edbdc486" and "32484ac4a075ed6fbaaa0d656bb01739cf857fcc" have entirely different histories.
50386c0e04
...
32484ac4a0
85
Makefile
85
Makefile
|
@ -1,5 +1,4 @@
|
|||
PROCS != nproc --all
|
||||
MAKEFLAGS = -j $(PROCS)
|
||||
MAKEFLAGS = --jobs=8
|
||||
UNAME != uname
|
||||
MAKEDIR != pwd
|
||||
# Options
|
||||
|
@ -17,13 +16,12 @@ LD = $(CC)
|
|||
STEAM = steam/sdk
|
||||
STEAMAPI =
|
||||
|
||||
LDFLAGS += -lstdc++
|
||||
|
||||
ifeq ($(CROSS)$(CC), emcc)
|
||||
LDFLAGS += --shell-file shell.html --closure 1
|
||||
CPPFLAGS += -Wbad-function-cast -Wcast-function-type -sSTACK_SIZE=1MB -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128MB
|
||||
NDEBUG = 1
|
||||
ARCH:= wasm
|
||||
OPT=small
|
||||
endif
|
||||
|
||||
ifdef NEDITOR
|
||||
|
@ -53,10 +51,6 @@ else
|
|||
INFO :=$(INFO)_dbg
|
||||
endif
|
||||
|
||||
ifdef NSTEAM
|
||||
CPPFLAGS += -DNSTEAM
|
||||
endif
|
||||
|
||||
ifdef LEAK
|
||||
CPPFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -DLEAK
|
||||
INFO := $(INFO)_leak
|
||||
|
@ -75,12 +69,16 @@ else
|
|||
CPPFLAGS += -O2
|
||||
endif
|
||||
|
||||
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" -Wno-narrowing #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
|
||||
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 --exclude="./*.a" --exclude="./obj" -czf $(DISTDIR)/$(DIST) .
|
||||
ZIP = .tar.gz
|
||||
UNZIP = cp $(DISTDIR)/$(DIST) $(DESTDIR) && tar xzf $(DESTDIR)/$(DIST) -C $(DESTDIR) && rm $(DESTDIR)/$(DIST)
|
||||
|
||||
INFO := $(INFO)_$(ARCH)
|
||||
|
||||
ifeq ($(OS), Windows_NT) # then WINDOWS
|
||||
|
@ -90,6 +88,9 @@ ifeq ($(OS), Windows_NT) # then WINDOWS
|
|||
LDFLAGS += -mwin32 -static
|
||||
CPPFLAGS += -mwin32
|
||||
LDLIBS += mingw32 kernel32 d3d11 user32 shell32 dxgi gdi32 ws2_32 ole32 winmm setupapi m pthread
|
||||
PKGCMD = zip -q -r $(MAKEDIR)/$(DISTDIR)/$(DIST) . -x \*.a ./obj/\*
|
||||
ZIP = .zip
|
||||
UNZIP = unzip -o -q $(DISTDIR)/$(DIST) -d $(DESTDIR)
|
||||
INFO :=$(INFO)_win
|
||||
EXT = .exe
|
||||
else ifeq ($(OS), IOS)
|
||||
|
@ -100,11 +101,11 @@ else ifeq ($(OS), IOS)
|
|||
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 -DIOS
|
||||
CFLAGS += -x objective-c
|
||||
INFO :=$(INFO)_ios
|
||||
else ifeq ($(OS), wasm) # Then WEB
|
||||
OS := Web
|
||||
LDFLAGS += -sUSE_WEBGPU
|
||||
LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
|
||||
CPPFLAGS += -DNSTEAM
|
||||
LDLIBS += GL openal c m dl
|
||||
STEAMAPI :=
|
||||
|
@ -115,9 +116,8 @@ else
|
|||
OS := Linux
|
||||
PLATFORM := linux64
|
||||
LDFLAGS += -pthread -rdynamic
|
||||
LDLIBS += pthread c m dl X11 Xi Xcursor EGL asound GL
|
||||
LDLIBS += GL pthread c m dl X11 Xi Xcursor EGL asound
|
||||
INFO :=$(INFO)_linux
|
||||
STEAMAPI := steam_api
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME), Darwin)
|
||||
|
@ -128,14 +128,9 @@ else
|
|||
CXXFLAGS += -std=c++11
|
||||
LDFLAGS += -framework Cocoa -framework QuartzCore -framework AudioToolbox -framework Metal -framework MetalKit
|
||||
INFO :=$(INFO)_macos
|
||||
STEAMAPI := steam_api
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NSTEAM
|
||||
STEAMAPI =
|
||||
endif
|
||||
|
||||
# All other sources
|
||||
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'
|
||||
|
@ -150,18 +145,16 @@ 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/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs source/engine/thirdparty/imgui
|
||||
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))
|
||||
|
||||
WARNING_FLAGS = -Wno-incompatible-function-pointer-types -Wno-incompatible-pointer-types
|
||||
|
||||
# For vanilla compilation, remove _
|
||||
ifeq ($(INFO),_)
|
||||
INFO :=
|
||||
endif
|
||||
|
||||
APP = prosperon
|
||||
NAME = $(APP)$(INFO)$(EXT)
|
||||
SEM != git describe --tags --abbrev=0
|
||||
|
@ -173,33 +166,22 @@ LDPATHS := $(STEAM)/redistributable_bin/$(PLATFORM)
|
|||
LDPATHS := $(addprefix -L, $(LDPATHS))
|
||||
|
||||
DEPENDS = $(OBJS:.o=.d)
|
||||
|
||||
ifndef VERBOSE
|
||||
.SILENT:
|
||||
endif
|
||||
|
||||
DEPFLAGS = -MT $(@:.d=.o) -MM -MG $< -o $@
|
||||
|
||||
%$(INFO).d: %.c
|
||||
@echo Making deps $@
|
||||
$(CROSS)$(CC) $(CPPFLAGS) $(DEPFLAGS)
|
||||
|
||||
%$(INFO).d: %.cpp
|
||||
@echo Making deps $@
|
||||
$(CROSS)$(CXX) $(CPPFLAGS) $(DEPFLAGS)
|
||||
|
||||
%$(INFO).d: %.m
|
||||
@echo Making deps $@
|
||||
$(CROSS)$(CC) $(CPPFLAGS) $(DEPFLAGS)
|
||||
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
include $(DEPENDS)
|
||||
endif
|
||||
-include $(DEPENDS)
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
all: $(NAME)
|
||||
cp -f $(NAME) $(APP)$(EXT)
|
||||
|
||||
SHADERS = $(shell ls source/shaders/*.sglsl)
|
||||
SHADERS := $(patsubst %.sglsl, %.sglsl.h, $(SHADERS))
|
||||
|
||||
prereqs: $(SHADERS) source/engine/core.cdb.h
|
||||
|
||||
DESTDIR ?= ~/.bin
|
||||
install: $(NAME)
|
||||
@echo Copying to destination
|
||||
cp -f $(NAME) $(DESTDIR)/$(APP)
|
||||
|
||||
$(NAME): $(OBJS) $(DEPS)
|
||||
@echo Linking $(NAME)
|
||||
$(CROSS)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@
|
||||
|
@ -217,7 +199,16 @@ $(NAME): $(OBJS) $(DEPS)
|
|||
@echo Making Objective-C object $@
|
||||
$(CROSS)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
CORE != (ls icons/* fonts/* shaders/*.cg scripts/*.js*)
|
||||
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 $@
|
||||
|
||||
SCRIPTS := $(shell ls scripts/*.js*)
|
||||
CORE != (ls icons/* fonts/*)
|
||||
CORE := $(CORE) $(SCRIPTS)
|
||||
|
||||
packer: tools/packer.c source/engine/miniz.c
|
||||
@echo Making packer
|
||||
|
@ -227,7 +218,7 @@ core.cdb: packer $(CORE)
|
|||
@echo Packing core.cdb
|
||||
./packer $@ $(CORE)
|
||||
|
||||
core.cdb.h: core.cdb
|
||||
source/engine/core.cdb.h: core.cdb
|
||||
@echo Making $@
|
||||
xxd -i $< > $@
|
||||
|
||||
|
@ -270,12 +261,12 @@ crosswin:
|
|||
make CROSS=x86_64-w64-mingw32- OS=Windows_NT CC=gcc
|
||||
|
||||
crossweb:
|
||||
make CC=emcc OS=wasm
|
||||
make CROSS=em OS=wasm
|
||||
mv $(APP).html index.html
|
||||
|
||||
clean:
|
||||
@echo Cleaning project
|
||||
rm -f core.cdb jso cdb packer TAGS source/engine/core.cdb.h tools/libcdb.a $(APP)* *.icns *.ico
|
||||
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
|
||||
|
||||
|
|
10
config.js
Normal file
10
config.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
console.stdout_lvl = 4;
|
||||
say(`config after std`);
|
||||
window.size = [600,600];
|
||||
window.rendersize = [200,200];
|
||||
say(`config after window size`);
|
||||
|
||||
globalThis.gamestate = {};
|
||||
gamestate.grid = 10;
|
||||
//window.title = "Accio!";
|
||||
say(`end of config`);
|
|
@ -1,16 +0,0 @@
|
|||
# Getting Started
|
||||
|
||||
## Installation
|
||||
### Linux & MacOS
|
||||
Install the most [[https://prosperon.dev/download][recent binaries]] into your `$PATH`.
|
||||
|
||||
### Windows
|
||||
Copy the executable into any folder and run it. If no game is deteced, it scaffolds for you.
|
||||
|
||||
### Building
|
||||
You will need `sokol-shdc` in the top of your directory. Then, `make shaders` and `make`. The command `make install` copies it to your home's `.bin` path.
|
||||
|
||||
## Playing your first game
|
||||
Download any of the completed example projects. Run `prosperon play` in the folder.
|
||||
|
||||
Poke around the example projects. You will find it refreshingly straight forward.
|
|
@ -1,11 +0,0 @@
|
|||
site_name: Prosperon Documentation
|
||||
repo_url: https://forge.pockle.world/john/prosperon
|
||||
theme:
|
||||
name: material
|
||||
|
||||
plugins:
|
||||
- search
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- tables
|
|
@ -1,13 +1,57 @@
|
|||
# Engine Tour
|
||||
#+title: Prosperon Documentation
|
||||
#+author: John Alanbrook
|
||||
#+options: html-postamble:nil
|
||||
#+DESCRIPTION: Prosperon documentation
|
||||
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css" />
|
||||
#+HTML_HEAD: <script defer data-domain="prosperon.dev" data-api="https://net.pockle.world/net/event" src="https://net.pockle.world/bat/script.js"></script>
|
||||
#+HTML_HEAD: <link rel="icon" href="orb.gif" type="image/gif">
|
||||
#+INFOJS_OPT: view:showall ltoc:above path:org-info.js toc:nil
|
||||
|
||||
@@html:
|
||||
<script src="https://kit.fontawesome.com/a87f68ad0a.js" crossorigin="anonymous"></script>
|
||||
<nav class="floathead">
|
||||
<a href="https://prosperon.dev">
|
||||
<img height=50px src="prosperon_orb_horizontal.gif">
|
||||
</a>
|
||||
<a href=#top><i class="fa-solid fa-bars"></i></a>
|
||||
<a href="https://github.com/johnalanbrook/prosperon"><i class="fa-brands fa-github"></i></a>
|
||||
<a href="https://x.com/pockleworld"><i class="fa-brands fa-x-twitter"></i></a>
|
||||
</nav>
|
||||
@@
|
||||
|
||||
* Getting Started
|
||||
|
||||
#+begin_scholium
|
||||
Press 'i' to see the table of contents.
|
||||
Press '?' for shortcuts on navigating this document!
|
||||
#+end_scholium
|
||||
|
||||
** Installation
|
||||
*** Linux & MacOS
|
||||
Install the most [[https://prosperon.dev/download][recent binaries]] into your ~$PATH~.
|
||||
|
||||
*** Windows
|
||||
Copy the executable into any folder and run it. If no game is deteced, it scaffolds for you.
|
||||
|
||||
*** Building
|
||||
You will need ~sokol-shdc~ in the top of your directory. Then, ~make shaders~ and ~make~. The command ~make install~ copies it to your home's ~.bin~ path.
|
||||
|
||||
** Playing your first game
|
||||
Download any of the completed example projects. Run ~prosperon play~ in the folder.
|
||||
|
||||
Poke around the example projects. You will find it refreshingly straight forward.
|
||||
|
||||
* Engine Tour
|
||||
Prosperon is built in a code-first fashion.
|
||||
|
||||
## Scripting
|
||||
The scripting language used in Prosperon is Javascript, with QuickJS. It is ES2023 compliant. It is fast.
|
||||
** Scripting
|
||||
The scripting language used in Prosperon is Javascript, with QuickJS. It is [[https://tc39.es/ecma262/2023/][ES2023]] compliant. It is fast, and has a number of features that make it well suited for a video game engine, like ref counting. Read more about it [[https://bellard.org/quickjs/][here]].
|
||||
|
||||
!!! scholium
|
||||
Javascript is used here mostly to set up objects and tear them down. Although computationally expensive tasks can be done using QuickJS, Prosperon makes it easy to extend with raw C.
|
||||
#+begin_scholium
|
||||
Javascript is used here mostly to set up objects and tear them down. Although computationally expensive tasks can be done using QuickJS, Prosperon makes it easy to extend with raw C.
|
||||
#+end_scholium
|
||||
|
||||
### How Prosperon games are structured
|
||||
*** How Prosperon games are structured
|
||||
Prosperon schews the CommonJS and ES module systems for a custom one suitable for a computer game actor model. It is more restricted than either system, while retaining their full power.
|
||||
|
||||
Prosperon games are structured into two types of source files:
|
||||
|
@ -16,34 +60,32 @@ Prosperon games are structured into two types of source files:
|
|||
|
||||
Scripts end with a return statement. A script can return a function, an object of functions, or any other sort of value.
|
||||
|
||||
!!! scholium
|
||||
It is a common requirement to add some amount of functionality to an object. It can be easily done with a script file.
|
||||
#+begin_scholium
|
||||
It is a common requirement to add some amount of functionality to an object. It can be easily done with a script file.
|
||||
#+begin_src
|
||||
*script.js*
|
||||
function hello() { say("hello"); };
|
||||
return hello;
|
||||
#+end_src
|
||||
#+begin_src
|
||||
var o = {};
|
||||
o.use("hello.js");
|
||||
o.hello();
|
||||
#+end_src
|
||||
The ~use~ function on any object loads a module, and ~assign~s its return to the object.
|
||||
#+end_scholium
|
||||
|
||||
```
|
||||
*script.js*
|
||||
function hello() { say("hello"); };
|
||||
return hello;
|
||||
```
|
||||
Scripts are loaded into memory only once. Further ~use~ statements only generate references to the statements. Because *scripts* are executed in a lambda environment, any ~var~ declared inside the script files are effectively private variables, persistent variables.
|
||||
|
||||
```
|
||||
var o = {};
|
||||
o.use("hello.js");
|
||||
o.hello();
|
||||
```
|
||||
In a *script*, ~this~ refers to ~undefined~. It is nothng.
|
||||
|
||||
The `use` function on any object loads a module, and `assign`s its return to the object.
|
||||
In an *actor* source file, ~this~ refers to the actor. Actors do not end in a ~return~ statement. *actor* source is intended to set up a new agent in your game. Set up the new entity by loading modules and assigning functions to ~this~.
|
||||
|
||||
Scripts are loaded into memory only once. Further `use` statements only generate references to the statements. Because *scripts* are executed in a lambda environment, any `var` declared inside the script files are effectively private variables, persistent variables.
|
||||
|
||||
In a *script*, `this` refers to `undefined`. It is nothng.
|
||||
|
||||
In an *actor* source file, `this` refers to the actor. Actors do not end in a `return` statement. *actor* source is intended to set up a new agent in your game. Set up the new entity by loading modules and assigning functions to `this`.
|
||||
|
||||
### Script entry points
|
||||
*** Script entry points
|
||||
The first way you can customize Prosperon is by adding scripts to the folder you're running it from. Any file ending with *.js* is a *script* which can be ran by Prosperon.
|
||||
|
||||
| script | When called |
|
||||
|-----------------|---------------------------------------------|
|
||||
|-----------------+---------------------------------------------|
|
||||
| config.js | First script on Prosperon load |
|
||||
| game.js | Entry point for running the game |
|
||||
| editorconfig.js | Entry point for running the editor |
|
||||
|
@ -51,96 +93,98 @@ The first way you can customize Prosperon is by adding scripts to the folder you
|
|||
| debug.js | Debug set up |
|
||||
| dbgret.js | After stopping debug mode, used for cleanup |
|
||||
|
||||
!!! scholium
|
||||
Try it out. Add a script called `config.js` to your folder, with this.
|
||||
#+begin_scholium
|
||||
Try it out. Add a script called ~config.js~ to your folder, with this.
|
||||
|
||||
```
|
||||
console.log("Hello world!");
|
||||
Game.quit();
|
||||
```
|
||||
Run `prosperon`. You will see "Hello world!" in the console, and it shuts down.
|
||||
#+begin_src
|
||||
console.log("Hello world!");
|
||||
Game.quit();
|
||||
#+end_src
|
||||
Run ~prosperon~. You will see "Hello world!" in the console, and it shuts down.
|
||||
#+end_scholium
|
||||
|
||||
Using ~config.js~ and ~game.js~, you can write an entire game, without reaching any further. When you want to populate a world with independent actors, entities are what you will reach for.
|
||||
|
||||
Using `config.js` and `game.js`, you can write an entire game, without reaching any further. When you want to populate a world with independent actors, entities are what you will reach for.
|
||||
|
||||
## Actors
|
||||
** Actors
|
||||
The fundamental tool for building in Prosperon is the actor system. Actors run independently from each other. Actors are defined by a combination of code and data. All actors have a *master* which controls certain properties of the actor.
|
||||
|
||||
The most masterful actor is the *Empyrean*. The first actor you create will have the Empyrean as its master. Subsequent actors can use any other actor as its master.
|
||||
|
||||
| fn | description |
|
||||
|---------------------|----------------------------------------------------------|
|
||||
|---------------------+----------------------------------------------------------|
|
||||
| spawn(text, config) | Creates an actor as the padawan of this one, using text |
|
||||
| kill() | Kills an actor |
|
||||
| delay(fn, seconds) | Calls 'fn' after 'seconds' with the context of the actor |
|
||||
|
||||
### Actor Lifetime
|
||||
*** Actor Lifetime
|
||||
When an actor dies, all of the actors that have it as their master will die as well.
|
||||
|
||||
### Turns
|
||||
*** Turns
|
||||
Actors get fragments of time called a *turn*. Actors which belong to different systems can have different lengths of turns.
|
||||
|
||||
### Actor files
|
||||
*** Actor files
|
||||
Actor files end with the extension *.jso*[fn::"Javascript object".]. They list a series of functions to call on a newly formed actor. Actors have a number of useful functions which are called as defined.
|
||||
|
||||
| function | call time |
|
||||
|----------|----------------------------------------------------------|
|
||||
|----------+----------------------------------------------------------|
|
||||
| start | The first function called when the actor is in the world |
|
||||
| update | Called once per turn |
|
||||
| gui | Called on GUI draw |
|
||||
| stop | Called when the actor is killed |
|
||||
| gizmo | Called by the editor when the entity is selected |
|
||||
|
||||
!!! scholium
|
||||
Create a new actor, then kill it.
|
||||
```
|
||||
var act_die_call = function() {
|
||||
#+begin_scholium
|
||||
Create a new actor, then kill it.
|
||||
#+begin_src
|
||||
var act_die_call = function() {
|
||||
console.log(`Actor ${this.id} has died.`);
|
||||
}
|
||||
var act1 = Empyrean.spawn();
|
||||
var act2 = actor1.spawn();
|
||||
act1.stop = act_die_call;
|
||||
act2.stop = act_die_call;
|
||||
Empyrean.kill(); /* Error: The Empyrean cannot be killed */
|
||||
act1.kill();
|
||||
act2.kill(); /* Error: act2 has been killed because act1 was */
|
||||
```
|
||||
}
|
||||
var act1 = Empyrean.spawn();
|
||||
var act2 = actor1.spawn();
|
||||
act1.stop = act_die_call;
|
||||
act2.stop = act_die_call;
|
||||
Empyrean.kill(); /* Error: The Empyrean cannot be killed */
|
||||
act1.kill();
|
||||
act2.kill(); /* Error: act2 has been killed because act1 was */
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
!!! scholium
|
||||
Now simplify by putting the code into a file named *hello.jso*.
|
||||
```
|
||||
this.stop = function() {
|
||||
#+begin_scholium
|
||||
Now simplify by putting the code into a file named *hello.jso*.
|
||||
#+begin_src
|
||||
this.stop = function() {
|
||||
console.log(`Actor ${this.id} has died.`);
|
||||
}
|
||||
```
|
||||
Now spawn two actors using it.
|
||||
```
|
||||
var act1 = Empyrean.spawn("hello.jso");
|
||||
var act2 = act1.spawn("hello.jso");
|
||||
```
|
||||
}
|
||||
#+end_src
|
||||
Now spawn two actors using it.
|
||||
#+begin_src
|
||||
var act1 = Empyrean.spawn("hello.jso");
|
||||
var act2 = act1.spawn("hello.jso");
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
|
||||
### Actor configuration
|
||||
*** Actor configuration
|
||||
Actors can be created using an optional configuration file. A configuration file is one of any accepted data types. Currently, JSON or [[https://www.crockford.com/nota.html][Nota]]. Configuration files are loaded after an actor's script file, overwriting any defined values on it.
|
||||
|
||||
!!! scholium
|
||||
Add a name for the actor to take on using a configuration file named *hello.json*.
|
||||
```
|
||||
{
|
||||
#+begin_scholium
|
||||
Add a name for the actor to take on using a configuration file named *hello.json*.
|
||||
#+begin_src
|
||||
{
|
||||
"name": "Actor 1"
|
||||
}
|
||||
```
|
||||
Now create *hello.jso* to use it.
|
||||
```
|
||||
this.start = function() { console.log(`I, ${this.name}, have been created.`); }
|
||||
```
|
||||
}
|
||||
#+end_src
|
||||
Now create *hello.jso* to use it.
|
||||
#+begin_src
|
||||
this.start = function() { console.log(`I, ${this.name}, have been created.`); }
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
|
||||
## Entities
|
||||
** Entities
|
||||
Game worlds are made of entities. Entities are a type of actor with a number of useful properties. Entities can only be created on the actor named *Primum*[fn::See the Primum Mobile]. The Primum is the outermost actor with a physical space. While Actors are more abstract, Entities exist in a definite space, with a position, rotation, and so on. Entities can respond to physics and play sounds. Anything which can be thought of as having a position in space should be an entitiy.
|
||||
|
||||
!!! scholium
|
||||
The first and most masterful entity is the Primum. The Primum has no components, and its rotation and position are zero. It defines the center of the game.
|
||||
#+begin_scholium
|
||||
The first and most masterful entity is the Primum. The Primum has no components, and its rotation and position are zero. It defines the center of the game.
|
||||
#+end_scholium
|
||||
|
||||
In editor mode, when an entity moves, all of its *padawans* also move.
|
||||
|
||||
|
@ -148,26 +192,26 @@ When the game is actively simulating, this only holds if there are physical cons
|
|||
|
||||
Prosperon automatically generates physical pin constraints between objects with the appropriate physical properties.
|
||||
|
||||
### Adding Components
|
||||
*** Adding Components
|
||||
Entities can have *components*. Components are essentially javascript wrappers over C code into the engine. Scripting is done to set the components up on entities, after which most of the work is done by the C plugin.
|
||||
|
||||
!!! scholium
|
||||
For example, to render an image, set up a *sprite* component on an entity and point its path to an image on your harddrive.
|
||||
```
|
||||
var ent = Empyrean.spawn();
|
||||
var spr = ent.add_component(component.sprite);
|
||||
spr.path = "image.png";
|
||||
```
|
||||
Put that into your config file and run `prosperon`. You should see the contents of "image.png" on the screen.
|
||||
|
||||
Try using an animated gif. Prosperon has native support for gif animations!
|
||||
#+begin_scholium
|
||||
For example, to render an image, set up a *sprite* component on an entity and point its path to an image on your harddrive.
|
||||
#+begin_src
|
||||
var ent = Empyrean.spawn();
|
||||
var spr = ent.add_component(component.sprite);
|
||||
spr.path = "image.png";
|
||||
#+end_src
|
||||
Put that into your config file and run ~prosperon~. You should see the contents of "image.png" on the screen.
|
||||
|
||||
Try using an animated gif. Prosperon has native support for gif animations!
|
||||
#+end_scholium
|
||||
|
||||
Components only work in the context of an entity. They have no meaning outside of a physical object in the world. They have no inherent scripting capabilities.
|
||||
|
||||
While components can be added via scripting, it is easier to add them via the editor, as we will later see.
|
||||
|
||||
### Ur system
|
||||
*** 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* 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.
|
||||
|
@ -181,13 +225,14 @@ An ur can also be defined by a json file. If an ur is found, it takes predecent
|
|||
|
||||
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").
|
||||
|
||||
!!! scholium
|
||||
Create an ur from the *hello* files above, and then spawn it.
|
||||
```
|
||||
ur.create("hello", "hello.jso", "hello.json");
|
||||
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.
|
||||
#+begin_scholium
|
||||
Create an ur from the *hello* files above, and then spawn it.
|
||||
#+begin_src
|
||||
ur.create("hello", "hello.jso", "hello.json");
|
||||
Primum.spawn(ur.hello);
|
||||
#+end_src
|
||||
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.
|
||||
|
||||
|
@ -195,67 +240,68 @@ If an entity is created without an ur, is ur is defined as its given text and da
|
|||
|
||||
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
|
||||
*** Urs in game
|
||||
|
||||
Each ur has the following fields.
|
||||
|
||||
| field | description |
|
||||
|-----------|-------------------------------------------------------------|
|
||||
|-----------+-------------------------------------------------------------|
|
||||
| instances | An array of instances of this ur |
|
||||
| name | Name of the ur |
|
||||
| text | Path to the script file |
|
||||
| data | Object to write to a newly generated actor |
|
||||
| proto | An object that looks like a freshly made entity from the ur |
|
||||
|
||||
An *ur* has a full path given like `ur.goblin.big`. `goblin` and `big` can both possibly have a *.jso* script as well as a *data* file.
|
||||
An *ur* has a full path given like ~ur.goblin.big~. ~goblin~ and ~big~ can both possibly have a *.jso* script as well as a *data* file.
|
||||
|
||||
When `goblin.big` is created, the new object has the `goblin` script run on it, followed by the `big` script. The `data` fields consist of objects prototyped from each other, so that the `__proto__` of `big.data` is `goblin.data`. All fields of this objects are assigned to the `big goblin`.
|
||||
When ~goblin.big~ is created, the new object has the ~goblin~ script run on it, followed by the ~big~ script. The ~data~ fields consist of objects prototyped from each other, so that the ~__proto__~ of ~big.data~ is ~goblin.data~. All fields of this objects are assigned to the ~big goblin~.
|
||||
|
||||
The unaltered form of every ur-based-entity is saved in the ur's `proto` field. As you edit objects, the differences between how your object is now, compared to its `ur.proto` is a list of differences. These differences can be rolled into the `ur`, or saved as a subtype.
|
||||
The unaltered form of every ur-based-entity is saved in the ur's ~proto~ field. As you edit objects, the differences between how your object is now, compared to its ~ur.proto~ is a list of differences. These differences can be rolled into the ~ur~, or saved as a subtype.
|
||||
|
||||
### Prototyping Entities
|
||||
*** Prototyping Entities
|
||||
Ur types are the prototype of created entities. This makes it trivial to change huge swathes of the game, or make tiny adjustments to single objects, in a natural and intuitive way. When a value is changed on an entity, it is private. When a value is changed on an ur, it propogates to all entities. Values cannot be added or removed in subtypes.
|
||||
|
||||
Entities all have the following functions to assist with this:
|
||||
|
||||
| function | use |
|
||||
|---------------|---------------------------------------------|
|
||||
|---------------+---------------------------------------------|
|
||||
| clone(parent) | Create an entity prototyped from the parent |
|
||||
| dup(parent) | Create an exact duplicate of the parent |
|
||||
| revert() | Removes all local changes on the entity |
|
||||
|
||||
Speaking of practical experience, is best for ur prototype chains to be shallow.
|
||||
|
||||
### Spawning
|
||||
*** Spawning
|
||||
Actor data and ur types can remember which entities were contained in it when saving. They are stored in the *objects* field. When an entity with an *objects* field is spawned, it spawns all of the objects listed in turn.
|
||||
|
||||
When an entity is spawned, it is addressable directly through its master entity. Its name is generated from its file or ur type name.
|
||||
|
||||
!!! scholium
|
||||
Let's take a simple RPG game.
|
||||
```
|
||||
Primum
|
||||
#+begin_scholium
|
||||
Let's take a simple RPG game.
|
||||
#+begin_src
|
||||
Primum
|
||||
level1
|
||||
orc
|
||||
goblin
|
||||
human
|
||||
sword
|
||||
ui
|
||||
```
|
||||
The orc, for example, is addressable by `Primum.level1.orc`. The `human` has a `sword` spawned underneath it. When he is killed, his sword also disappears.
|
||||
#+end_src
|
||||
The orc, for example, is addressable by ~Primum.level1.orc~. The ~human~ has a ~sword~ spawned underneath it. When he is killed, his sword also disappears.
|
||||
#+end_scholium
|
||||
|
||||
### Resources
|
||||
Assets can generally be used simply with their filename. Assets can be modified with a sidecar file named *filename.asset*, so, a file `ball.png` can have additional parameters through its `ball.png.asset` file.
|
||||
*** Resources
|
||||
Assets can generally be used simply with their filename. Assets can be modified with a sidecar file named *filename.asset*, so, a file ~ball.png~ can have additional parameters through its ~ball.png.asset~ file.
|
||||
|
||||
| sigil | meaning |
|
||||
|--------|------------------------|
|
||||
|--------+------------------------|
|
||||
| \slash | root of project |
|
||||
| @ | root of save directory |
|
||||
| # | root of link |
|
||||
|
||||
Resources can be referenced in a relative manner by actor scripts. When it comes to actors using assets, relative filepaths are useful and encouraged.
|
||||
|
||||
```
|
||||
#+begin_src
|
||||
/
|
||||
score.wav
|
||||
/bumper
|
||||
|
@ -264,21 +310,22 @@ Resources can be referenced in a relative manner by actor scripts. When it comes
|
|||
/ball
|
||||
hit.wav
|
||||
ball.jso
|
||||
```
|
||||
#+end_src
|
||||
|
||||
Path resolution occurs during actor creation. In effect, a reference to *hit.wav* in *bumper.jso* will resolve to the absolute path */bumper/hit.wav*.
|
||||
|
||||
If the asset is not found, it is searched for until the project root is reached. The bumper can reference *score.wav* and have the path resolution take place. Later, if the it is decided for the bumper to have a unique score sound, a new /score.wav/ can be placed in its folder and it will work without changing any code.
|
||||
|
||||
!!! caution
|
||||
Because the path is resolved during object load, you will need to fresh the bumper's ur or spawn a new bumper for it to use the newly placed /score.wav/.
|
||||
#+begin_scholium
|
||||
Caution! Because the path is resolved during object load, you will need to fresh the bumper's ur or spawn a new bumper for it to use the newly placed /score.wav/.
|
||||
#+end_scholium
|
||||
|
||||
###* Links
|
||||
Links can be specified using the "#" sign. These are shortcuts you can specify for large projects. Specify them in the array `Resources.links`.
|
||||
**** Links
|
||||
Links can be specified using the "#" sign. These are shortcuts you can specify for large projects. Specify them in the array ~Resources.links~.
|
||||
|
||||
An example is of the form `trees:/world/assets/nature/trees`. Links are called with `#`, so you can now make a "fern" with `Primum.spawn("#trees/fern.jso")`.
|
||||
An example is of the form ~trees:/world/assets/nature/trees~. Links are called with ~#~, so you can now make a "fern" with ~Primum.spawn("#trees/fern.jso")~.
|
||||
|
||||
### Ur auto creation
|
||||
*** Ur auto creation
|
||||
Instead of coding all the ur type creation by hand, Prosperon can automatically search your project's folder and create the ur types for you. Any /[name].jso/ file is converted into an ur with the name. Any /[name].json/ file is then applied over it, should it exist. If there is a /.json/ file without a corresponding /.jso/, it can still be turned into an ur, if it is a valid ur format.
|
||||
|
||||
Folders and files beginning with a '.' (hidden) or a '_' will be ignored for ur creation.
|
||||
|
@ -287,7 +334,7 @@ The folder hierarchy of your file system determines the ur prototype chain. /.js
|
|||
|
||||
Only one ur of any name can be created.
|
||||
|
||||
```
|
||||
#+begin_src
|
||||
@/
|
||||
flipper.js
|
||||
flipper/
|
||||
|
@ -298,50 +345,50 @@ Only one ur of any name can be created.
|
|||
flipper.js
|
||||
left/
|
||||
left.js
|
||||
```
|
||||
#+end_src
|
||||
|
||||
`prototypes.generate_ur(path)` will generate all ur-types for a given path. You can preload specific levels this way, or the entire game using `prototypes.generate_ur('.')`. If your game is small enough, this can have a massive runtime improvement.
|
||||
~prototypes.generate_ur(path)~ will generate all ur-types for a given path. You can preload specific levels this way, or the entire game using ~prototypes.generate_ur('.')~. If your game is small enough, this can have a massive runtime improvement.
|
||||
|
||||
## Input
|
||||
** Input
|
||||
Input is done in a highly generic and customizable manner. *players* can take control of any object (actor or otherwise) in Prosperon, after which it is referred to as a *pawn* of a player. If the object has a defined *input* object, it is a valid pawn. One player can have many pawns, but each pawn may have only one player.
|
||||
|
||||
Pawns are added as a stack, with the newest ones getting priority, and handled first. It is possible for pawns to block input to lower pawns on the stack.
|
||||
|
||||
```
|
||||
#+begin_src
|
||||
/newest/
|
||||
car <== When a key is pressed, this is the first pawn to handle input
|
||||
player
|
||||
ui <== /block/ is set to true here, so editor recieves no input!
|
||||
editor
|
||||
/oldest/
|
||||
```
|
||||
#+end_src
|
||||
|
||||
The default player can be obtained with `Player.players[0]`. Players are all local, and the highest number is determined by platform.
|
||||
The default player can be obtained with ~Player.players[0]~. Players are all local, and the highest number is determined by platform.
|
||||
|
||||
The *input* object defines a number of keys or actions, with their values being functions.
|
||||
|
||||
### Editor input
|
||||
*** Editor input
|
||||
The editor input style defines keystrokes. It is good for custom editors, or any sort of game that requires many hotkeys. Keystrokes are case sensitive and can be augmented with auxiliary keys.
|
||||
|
||||
| symbol | key |
|
||||
|--------|-------|
|
||||
|--------+-------|
|
||||
| C | ctrl |
|
||||
| M | alt |
|
||||
| S | super |
|
||||
|
||||
```
|
||||
#+begin_src
|
||||
var orc = Primum.spawn(ur.orc);
|
||||
orc.inputs = {};
|
||||
orc.inputs.a = function() { ... };
|
||||
orc.inputs.A = function() { ... }; /* This is only called with a capital A! */
|
||||
orc.inputs['C-a'] = function() { ... }; /* Control-a */
|
||||
Player.players[0].control(orc); /* player 0 is now in control of the orc */
|
||||
```
|
||||
#+end_src
|
||||
|
||||
The input object can be modified to customize how it handles input.
|
||||
|
||||
| property | type | effect |
|
||||
|----------------|----------|--------------------------------------|
|
||||
|----------------+----------+--------------------------------------|
|
||||
| post | function | called after any input is processed |
|
||||
| =release_post= | function | called after any input is released |
|
||||
| fallthru | bool | false if input should stop with this |
|
||||
|
@ -350,27 +397,27 @@ The input object can be modified to customize how it handles input.
|
|||
The input can be modified by setting properties on the associated function.
|
||||
|
||||
| property | type | effect |
|
||||
|----------|----------|--------------------------------------------------------|
|
||||
|----------+----------+--------------------------------------------------------|
|
||||
| released | function | Called when the input is released |
|
||||
| rep | bool | true if holding the input should repeatedly trigger it |
|
||||
| down | function | called while the input is down |
|
||||
|
||||
## GUI
|
||||
** GUI
|
||||
Game GUIs are written by registering an entity's *gui* property to a function.
|
||||
|
||||
The GUI system which ships with Prosperon is called *MUM*. MUM is a declarative, immediate mode interface system. Immediate to eliminate the issue of data synchronization in the game.
|
||||
|
||||
All GUI objects derive from MUM. MUM has a list of properties, used for rendering. Mum also has functions which cause drawing to appear on the screen.
|
||||
|
||||
## Physics
|
||||
** Physics
|
||||
Prospeorn comes with the [[https://chipmunk-physics.net][Chipmunk]] physics engine built in. It is a fast, stable physics solution. All entities are assumed to be physics based objects, and components can be added to them to enable more physics features.
|
||||
|
||||
* Editor Tour
|
||||
Prosperon's visual editor is an assistant for the creation and editing of your game entities and actors. In the editor, all ur types are loaded, and assets are constantly monitored for changes for hot reloading.
|
||||
|
||||
To initiate it, execute `prosperon`.
|
||||
To initiate it, execute ~prosperon~.
|
||||
|
||||
## Editing entities
|
||||
** Editing entities
|
||||
The desktop is the topmost entity that exists in the editor. Instead of editing specific files, you simply load them into your desktop, and go from there. This makes it easier to see two different entities simultaneously so you can ensure changes to one are congruous with the vision for the others.
|
||||
|
||||
The main editor view is made up of entities. Each entity can have a number of components attached to it. When an entity is selected, its name, position, and list of components are listed.
|
||||
|
@ -379,46 +426,47 @@ Basic use of the editor involves spawning new entities, or ones from already mad
|
|||
|
||||
Assign the entity's *gizmo* property to a function to have that function called each gui rendering frame.
|
||||
|
||||
## The REPL[fn::Read-eval-print loop]
|
||||
** The REPL[fn::Read-eval-print loop]
|
||||
The REPL lets you poke around in the game. It makes iteration and experimentation fast, fun, and easy.
|
||||
|
||||
The symbol `$` references the current REPL entity. If no entity is selected, the REPL entity is the currently edited one. Otherwise, it is the selected entity, or group of entities, as an array.
|
||||
The symbol ~$~ references the current REPL entity. If no entity is selected, the REPL entity is the currently edited one. Otherwise, it is the selected entity, or group of entities, as an array.
|
||||
|
||||
!!! scholium
|
||||
Easily run commands on multiple entities using Javascript functions like for each.
|
||||
```
|
||||
$.forEach(e => console.log(e.pos));
|
||||
```
|
||||
#+begin_scholium
|
||||
Easily run commands on multiple entities using Javascript functions like for each.
|
||||
#+begin_src
|
||||
$.forEach(e => console.log(e.pos));
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
The REPL is a powerful tool for editing your game. Arbitrary code can be ran in it, meaning any esoteric activity you need done for your game can be done easily. Commonly used functions should be copied into your /editorconfig.js/ to be called and used at will.
|
||||
|
||||
## Playing the game
|
||||
** Playing the game
|
||||
Playing the game involves running the game from a special /debug.js/ file, or from the beginning, as if the game were packaged and shipped.
|
||||
|
||||
| key | action |
|
||||
|-------|-----------------------------------------------------|
|
||||
|-------+-----------------------------------------------------|
|
||||
| f5 | Play the game, starting with entry point /debug.js/ |
|
||||
| f6 | Play the game from the beginning |
|
||||
|
||||
While playing the game, a limited editor is available that allows for simple debugging tasks.
|
||||
|
||||
| key | action |
|
||||
|-----|-----------------------------|
|
||||
|-----+-----------------------------|
|
||||
| C-p | Pause |
|
||||
| M-p | One time step |
|
||||
| C-q | Quit play, return to editor |
|
||||
|
||||
## Script Editor
|
||||
** Script Editor
|
||||
Prosperon comes with an in-engine script editor. It implements a subset of emacs, and adds a few engine specific features.
|
||||
|
||||
### Syntax coloring? ... nope!
|
||||
*** Syntax coloring? ... nope!
|
||||
The editor that ships with Prosperon has *context coloring*, which is a good deal more useful than syntax coloring.
|
||||
|
||||
## Debugging
|
||||
** Debugging
|
||||
Debugging functions are mapped to the F buttons, and are available in any debug build of the game. Pressing the specified key toggles the feature; pressing it with /alt/ shows a legend for that feature.
|
||||
|
||||
| key | description |
|
||||
|-----|----------------------------|
|
||||
|-----+----------------------------|
|
||||
| F1 | Draw physics info |
|
||||
| F3 | Draw bounding boxes |
|
||||
| F12 | Draw gui info |
|
||||
|
@ -430,7 +478,7 @@ Prosperon is a multiplatform engine. Bundling your game for these platforms esse
|
|||
- Conversion of assets
|
||||
- Packing into a CDB[fn::Constant database]
|
||||
|
||||
To distribute your game for a given platform, run `prosperon build {platform}`.
|
||||
To distribute your game for a given platform, run ~prosperon build {platform}~.
|
||||
|
||||
| platform |
|
||||
|----------|
|
||||
|
@ -440,7 +488,7 @@ To distribute your game for a given platform, run `prosperon build {platform}`.
|
|||
|
||||
You will find your game ready to go. Rename the executable to the name of your game and run it to play. Congratulations!
|
||||
|
||||
## Building static content
|
||||
** Building static content
|
||||
Static content creation involves any number of optimizations.
|
||||
|
||||
- Bitmap font creation
|
||||
|
@ -448,13 +496,13 @@ Static content creation involves any number of optimizations.
|
|||
|
||||
Creation of these assets is invisible. Prosperon updates its understanding of how to pull assets based on the existance of these packed ones.
|
||||
|
||||
## Converting assets
|
||||
** Converting assets
|
||||
Images, videos, and sounds, are converted to assets most suitable for the target platform. This may be for speed or simple compatability. *You do not need to do anything*. Use your preferred asset types during production.
|
||||
|
||||
## Packing into a CDB
|
||||
** Packing into a CDB
|
||||
A *cdb* is known as a "constant database". It is a write once type of database, with extremely fast retrieval times. Packing your game into a cdb means to create a database with key:value pairs of the filenames of your game. The Prosperon executable is already packed with a core cdb. Your game assets are packed alongside it as the game cdb.
|
||||
|
||||
You can create your game's cdb by running `prosperon -b`. You will find a *game.cdb* in the root directory.
|
||||
You can create your game's cdb by running ~prosperon -b~. You will find a *game.cdb* in the root directory.
|
||||
|
||||
* Modding & Patching
|
||||
When an asset is requested in Prosperon, it is searched for in the following manner.
|
||||
|
@ -465,13 +513,13 @@ When an asset is requested in Prosperon, it is searched for in the following man
|
|||
|
||||
Game modification is trivial using this described system. By putting an asset in the same path as the asset's location in the game cdb, when that asset is requested it will be pulled from the file system instead of the game cdb.
|
||||
|
||||
Given a Prosperon-built game, you can unpack its content into a directory by running `prosperon unpack {game}`.
|
||||
Given a Prosperon-built game, you can unpack its content into a directory by running ~prosperon unpack {game}~.
|
||||
|
||||
## Shipping
|
||||
Once a game's assets are modified, it may be desirable to ship them. Run `prosperon patch create {game}` to create a /patch.cdb/ filled only with the files that are different compared to those found in the /game.cdb/ in the /game/.
|
||||
** Shipping
|
||||
Once a game's assets are modified, it may be desirable to ship them. Run ~prosperon patch create {game}~ to create a /patch.cdb/ filled only with the files that are different compared to those found in the /game.cdb/ in the /game/.
|
||||
|
||||
To update /game/ to use the new patch, run `prosperon patch apply {patch}`, replacing /patch/ with the name of the cdb file generated above.
|
||||
To update /game/ to use the new patch, run ~prosperon patch apply {patch}~, replacing /patch/ with the name of the cdb file generated above.
|
||||
|
||||
Many patches can be bundled by running `prosperon patch bundle {list of patches}`. This creates a patch that will update the game as if the user had updated each patch in order.
|
||||
Many patches can be bundled by running ~prosperon patch bundle {list of patches}~. This creates a patch that will update the game as if the user had updated each patch in order.
|
||||
|
||||
Mods can be distributed with the same idea.
|
|
@ -2,6 +2,7 @@ var actor = {};
|
|||
|
||||
actor.spawn = function(script, config, callback){
|
||||
if (typeof script !== 'string') return undefined;
|
||||
console.info(`spawning actor with script ${script}`);
|
||||
var padawan = Object.create(actor);
|
||||
use(script, padawan);
|
||||
|
||||
|
|
132
scripts/base.js
132
scripts/base.js
|
@ -358,6 +358,13 @@ Object.readonly = function(o, name, msg)
|
|||
Object.defineProperty(o, name, prop);
|
||||
}
|
||||
|
||||
Object.extend = function(from)
|
||||
{
|
||||
var n = {};
|
||||
Object.mixin(n, from);
|
||||
return n;
|
||||
}
|
||||
|
||||
Object.mixin = function(target, source)
|
||||
{
|
||||
if (typeof source !== 'object') return target;
|
||||
|
@ -535,8 +542,10 @@ Object.unhide = function(obj, ...props)
|
|||
{
|
||||
for (var prop of props) {
|
||||
var p = Object.getOwnPropertyDescriptor(obj,prop);
|
||||
if (!p)
|
||||
continue;
|
||||
if (!p) {
|
||||
console.warn(`No property of name ${prop}.`);
|
||||
return;
|
||||
}
|
||||
p.enumerable = true;
|
||||
Object.defineProperty(obj, prop, p);
|
||||
}
|
||||
|
@ -801,13 +810,6 @@ Object.defineProperty(String.prototype, 'name', {
|
|||
return this.fromlast('/').tolast('.'); }
|
||||
});
|
||||
|
||||
Object.defineProperty(String.prototype, 'set_name', {
|
||||
value: function(name) {
|
||||
var dir = this.dir();
|
||||
return this.dir() + name + "." + this.ext();
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(String.prototype, 'base', {
|
||||
value: function() { return this.fromlast('/'); }
|
||||
});
|
||||
|
@ -818,12 +820,6 @@ Object.defineProperty(String.prototype, 'splice', {
|
|||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(String.prototype, 'sub', {
|
||||
value: function(index, str) {
|
||||
return this.slice(0,index) + str + this.slice(index+str.length);
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(String.prototype, 'rm', {
|
||||
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
|
||||
});
|
||||
|
@ -1167,6 +1163,11 @@ Object.defineProperty(Array.prototype, 'anyjs', {
|
|||
});
|
||||
|
||||
/* Return true if array contains x */
|
||||
/*Object.defineProperty(Array.prototype, 'includes', {
|
||||
value: function(x) {
|
||||
return this.some(e => e === x);
|
||||
}});
|
||||
*/
|
||||
Object.defineProperty(Array.prototype, 'empty', {
|
||||
get: function() { return this.length === 0; },
|
||||
});
|
||||
|
@ -1184,9 +1185,6 @@ Object.defineProperty(Array.prototype, 'unique', {
|
|||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(Array.prototype, 'unduped', {
|
||||
value: function() { return [... new Set(this)]; }
|
||||
});
|
||||
|
||||
Object.defineProperty(Array.prototype, 'findIndex', {
|
||||
value: function(fn) {
|
||||
|
@ -1219,15 +1217,6 @@ Object.defineProperty(Array.prototype, 'find', {
|
|||
return ret;
|
||||
}});
|
||||
|
||||
Object.defineProperty(Array.prototype, 'search', {
|
||||
value: function(val) {
|
||||
for (var i = 0; i < this.length; i++)
|
||||
if (this[i] === val) return i;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(Array.prototype, 'last', {
|
||||
value: function() { return this[this.length-1]; },
|
||||
});
|
||||
|
@ -1347,19 +1336,16 @@ Math.snap = function(val, grid) {
|
|||
}
|
||||
|
||||
Math.angledist = function (a1, a2) {
|
||||
a1 = a1%1;
|
||||
a2 = a2%1;
|
||||
a1 = Math.turn2deg(a1);
|
||||
a2 = Math.turn2deg(a2);
|
||||
var dist = a2 - a1;
|
||||
if (dist == 0) return dist;
|
||||
var wrap = dist >= 0 ? dist+360 : dist-360;
|
||||
wrap %= 360;
|
||||
|
||||
if (dist > 0) {
|
||||
if (dist > 0.5) return dist-1;
|
||||
return dist;
|
||||
}
|
||||
if (Math.abs(dist) < Math.abs(wrap))
|
||||
return Math.deg2turn(dist);
|
||||
|
||||
if (dist < -0.5) return dist+1;
|
||||
|
||||
return dist;
|
||||
return Math.deg2turn(wrap);
|
||||
};
|
||||
Math.angledist.doc = "Find the shortest angle between two angles.";
|
||||
Math.TAU = Math.PI*2;
|
||||
|
@ -1506,16 +1492,6 @@ Vector.random = function() {
|
|||
return Vector.norm(vec);
|
||||
}
|
||||
|
||||
Vector.angle_between = function(a,b)
|
||||
{
|
||||
var dot = Vector.dot(a,b);
|
||||
var am = Vector.length(a);
|
||||
var bm = Vector.length(b);
|
||||
var cos_a = dot / (am*bm);
|
||||
var angle = Math.acos(cos_a);
|
||||
return Math.rad2turn(angle);
|
||||
}
|
||||
|
||||
Vector.angle = function(v) { return Math.rad2turn(Math.atan2(v.y, v.x)); }
|
||||
Vector.rotate = function(v,angle) {
|
||||
var r = Vector.length(v);
|
||||
|
@ -1567,74 +1543,18 @@ Math.sortpointsccw = function(points)
|
|||
var cm = points2cm(points);
|
||||
var cmpoints = points.map(function(x) { return x.sub(cm); });
|
||||
var ccw = cmpoints.sort(function(a,b) {
|
||||
var aatan = Math.atan2(a.y, a.x);
|
||||
var batan = Math.atan2(b.y, b.x);
|
||||
aatan = Math.atan2(a.y, a.x);
|
||||
batan = Math.atan2(b.y, b.x);
|
||||
return aatan - batan;
|
||||
});
|
||||
|
||||
return ccw.map(function(x) { return x.add(cm); });
|
||||
}
|
||||
|
||||
var yaml = {};
|
||||
yaml.tojson = function(yaml)
|
||||
{
|
||||
// Replace key value pairs that are strings with quotation marks around them
|
||||
yaml = yaml.replace(/(\w+):/g, '"$1":');
|
||||
yaml = yaml.replace(/: ([\w\.\/]+)/g, ': "$1"'); // TODO: make this more general
|
||||
|
||||
yaml = yaml.split("\n");
|
||||
var cont = {};
|
||||
var cur = 0;
|
||||
for (var i = 0; i < yaml.length; i++) {
|
||||
var line = yaml[i];
|
||||
var indent = line.search(/\S/);
|
||||
|
||||
if (indent > cur) {
|
||||
if (line[indent] == "-") {
|
||||
cont[indent] = "array";
|
||||
yaml[i] = line.sub(indent, '[');
|
||||
} else {
|
||||
cont[indent] = "obj";
|
||||
yaml[i] = line.sub(indent-1, '{');
|
||||
}
|
||||
}
|
||||
|
||||
if (indent < cur) {
|
||||
while (cur > indent) {
|
||||
if (cont[cur] === "obj")
|
||||
yaml[i-1] = yaml[i-1] + "}";
|
||||
else if (cont[cur] === "array")
|
||||
yaml[i-1] = yaml[i-1] + "]";
|
||||
|
||||
delete cont[cur];
|
||||
cur--;
|
||||
}
|
||||
}
|
||||
|
||||
if (indent === cur) {
|
||||
if (yaml[i][indent] === '-')
|
||||
yaml[i] = yaml[i].sub(indent,',');
|
||||
else
|
||||
yaml[i-1] = yaml[i-1] + ',';
|
||||
}
|
||||
|
||||
cur = indent;
|
||||
}
|
||||
yaml = "{" + yaml.join("\n") + "}";
|
||||
yaml = yaml.replace(/\s/g, '');
|
||||
yaml = yaml.replace(/,}/g, '}');
|
||||
yaml = yaml.replace(/,]/g, ']');
|
||||
yaml = yaml.replace(/,"[^"]+"\:,/g, ',');
|
||||
return yaml;
|
||||
}
|
||||
|
||||
Math.sign = function(n) { return n >= 0 ? 1 : -1; }
|
||||
|
||||
return {
|
||||
convert,
|
||||
time,
|
||||
json,
|
||||
Vector,
|
||||
bbox,
|
||||
yaml
|
||||
bbox
|
||||
};
|
||||
|
|
|
@ -13,10 +13,6 @@ this.view2world = function(pos) {
|
|||
pos = pos.scale(this.zoom);
|
||||
pos = pos.add(this.pos);
|
||||
}
|
||||
if (window.mode === window.modetypes.expand) {
|
||||
pos = pos.sub(window.size.scale(0.5));
|
||||
pos = pos.scale([window.rendersize.x/window.size.x, window.rendersize.y/window.size.y]);
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
this.world2view = function(pos) {
|
||||
|
@ -29,9 +25,6 @@ this.world2view = function(pos) {
|
|||
pos = pos.sub(this.pos);
|
||||
pos = pos.scale(1/this.zoom);
|
||||
pos = pos.add(window.size.scale(0.5));
|
||||
}
|
||||
if (window.mode === window.modetypes.expand) {
|
||||
|
||||
}
|
||||
return pos;
|
||||
};
|
||||
|
|
|
@ -95,8 +95,6 @@ Color.normalize = function(c) {
|
|||
continue;
|
||||
}
|
||||
|
||||
c[p][3] = 255;
|
||||
|
||||
for (var color of c[p]) {
|
||||
if (color > 1) {
|
||||
fmt = "8b";
|
||||
|
|
|
@ -1,4 +1,44 @@
|
|||
var component = {};
|
||||
var component = {
|
||||
components: [],
|
||||
toString() {
|
||||
if ('gameobject' in this)
|
||||
return this.name + " on " + this.gameobject;
|
||||
else
|
||||
return this.name;
|
||||
},
|
||||
name: "component",
|
||||
component: true,
|
||||
enabled: true,
|
||||
enable() { this.enabled = true; },
|
||||
disable() { this.enabled = false; },
|
||||
|
||||
isComponent(c) {
|
||||
if (typeof c !== 'object') return false;
|
||||
if (typeof c.toString !== 'function') return false;
|
||||
if (typeof c.make !== 'function') return false;
|
||||
return (typeof component[c.toString()] === 'object');
|
||||
},
|
||||
|
||||
make(go) {
|
||||
var nc = Object.create(this);
|
||||
nc.gameobject = go;
|
||||
Object.mixin(nc, this._enghook(go, nc));
|
||||
assign_impl(nc,this.impl);
|
||||
Object.hide(nc, 'gameobject', 'id');
|
||||
nc.post();
|
||||
nc.make = undefined;
|
||||
return nc;
|
||||
},
|
||||
|
||||
kill() { console.info("Kill not created for this component yet"); },
|
||||
sync(){},
|
||||
post(){},
|
||||
gui(){},
|
||||
gizmo(){},
|
||||
|
||||
finish_center() {},
|
||||
extend(spec) { return Object.copy(this, spec); },
|
||||
};
|
||||
|
||||
var make_point_obj = function(o, p)
|
||||
{
|
||||
|
@ -13,8 +53,41 @@ var make_point_obj = function(o, p)
|
|||
}
|
||||
}
|
||||
|
||||
var sprite = {
|
||||
var assign_impl = function(obj, impl)
|
||||
{
|
||||
var tmp = {};
|
||||
for (var key of Object.keys(impl))
|
||||
if (typeof obj[key] !== 'undefined' && typeof obj[key] !== 'function')
|
||||
tmp[key] = obj[key];
|
||||
|
||||
Object.mixin(obj, impl);
|
||||
|
||||
for (var key in tmp)
|
||||
obj[key] = tmp[key];
|
||||
}
|
||||
|
||||
function json_from_whitelist(whitelist)
|
||||
{
|
||||
return function() {
|
||||
var o = {};
|
||||
for (var p of whitelist)
|
||||
o[p] = this[p];
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
Object.mixin(os.sprite(true), {
|
||||
loop: true,
|
||||
toJSON:json_from_whitelist([
|
||||
"path",
|
||||
"pos",
|
||||
"scale",
|
||||
"angle",
|
||||
"color",
|
||||
"emissive",
|
||||
"parallax",
|
||||
"frame"
|
||||
]),
|
||||
anim:{},
|
||||
playing: 0,
|
||||
play(str = 0) {
|
||||
|
@ -37,7 +110,6 @@ var sprite = {
|
|||
if (!self.gameobject) return;
|
||||
//self.path = playing.path;
|
||||
self.frame = playing.frames[f].rect;
|
||||
self.rect = [self.frame.x, self.frame.y, self.frame.w, self.frame.h];
|
||||
f = (f+1)%playing.frames.length;
|
||||
if (f === 0) {
|
||||
self.anim_done?.();
|
||||
|
@ -45,7 +117,7 @@ var sprite = {
|
|||
}
|
||||
stop = self.gameobject.delay(advance, playing.frames[f].time);
|
||||
}
|
||||
|
||||
this.tex(game.texture(playing.path));
|
||||
advance();
|
||||
},
|
||||
stop() {
|
||||
|
@ -61,9 +133,7 @@ var sprite = {
|
|||
this._p = p;
|
||||
this.del_anim?.();
|
||||
this.texture = game.texture(p);
|
||||
|
||||
this.diffuse = this.texture;
|
||||
this.rect = [0,0,1,1];
|
||||
this.tex(this.texture);
|
||||
|
||||
var anim = SpriteAnim.make(p);
|
||||
if (!anim) return;
|
||||
|
@ -71,7 +141,6 @@ var sprite = {
|
|||
this.play();
|
||||
|
||||
this.pos = this.dimensions().scale(this.anchor);
|
||||
|
||||
},
|
||||
get path() {
|
||||
return this._p;
|
||||
|
@ -81,8 +150,8 @@ var sprite = {
|
|||
this.anim = undefined;
|
||||
this.gameobject = undefined;
|
||||
this.anim_done = undefined;
|
||||
delete allsprites[this.guid];
|
||||
},
|
||||
toString() { return "sprite"; },
|
||||
move(d) { this.pos = this.pos.add(d); },
|
||||
grow(x) {
|
||||
this.scale = this.scale.scale(x);
|
||||
|
@ -106,8 +175,19 @@ var sprite = {
|
|||
},
|
||||
width() { return this.dimensions().x; },
|
||||
height() { return this.dimensions().y; },
|
||||
};
|
||||
globalThis.allsprites = {};
|
||||
});
|
||||
|
||||
os.sprite(true).make = function(go)
|
||||
{
|
||||
var sp = os.sprite();
|
||||
sp.go = go;
|
||||
sp.gameobject = go;
|
||||
return sp;
|
||||
}
|
||||
|
||||
component.sprite = os.sprite(true);
|
||||
|
||||
var sprite = component.sprite;
|
||||
|
||||
sprite.doc = {
|
||||
path: "Path to the texture.",
|
||||
|
@ -143,18 +223,7 @@ sprite.inputs.kp3 = function() { this.setanchor("ur"); }
|
|||
sprite.inputs.kp2 = function() { this.setanchor("um"); }
|
||||
sprite.inputs.kp1 = function() { this.setanchor("ul"); }
|
||||
|
||||
component.sprite = function(obj) {
|
||||
var sp = Object.create(sprite);
|
||||
sp.gameobject = obj;
|
||||
sp.guid = prosperon.guid();
|
||||
allsprites[sp.guid] = sp;
|
||||
return sp;
|
||||
}
|
||||
sprite.shade = [1,1,1,1];
|
||||
|
||||
Object.mixin(os.make_seg2d(), {
|
||||
sync() { this.set_endpoints(this.points[0], this.points[1]); }
|
||||
});
|
||||
Object.seal(sprite);
|
||||
|
||||
/* sprite anim returns a data structure for the given file path
|
||||
frames: array of frames
|
||||
|
@ -182,11 +251,13 @@ SpriteAnim.make = function(path) {
|
|||
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++) {
|
||||
|
@ -205,6 +276,7 @@ SpriteAnim.gif = function(path) {
|
|||
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};
|
||||
|
@ -257,7 +329,7 @@ SpriteAnim.aseprite = function(path) {
|
|||
var anims = {};
|
||||
var frames = Array.isArray(data.frames) ? data.frames : Object.values(data.frames);
|
||||
var f = 0;
|
||||
if (!data.meta.frameTags || data.meta.frameTags.length === 0) {
|
||||
if (data.meta.frameTags.length === 0) {
|
||||
anims[0] = aseframeset2anim(frames, data.meta);
|
||||
return anims;
|
||||
}
|
||||
|
@ -288,7 +360,18 @@ SpriteAnim.strip.doc = 'Given a path and number of frames, converts a horizontal
|
|||
SpriteAnim.aseprite.doc = 'Given an aseprite json metadata, returns an object of animations defined in the aseprite file.';
|
||||
SpriteAnim.find.doc = 'Given a path, find the relevant animation for the file.';
|
||||
|
||||
var collider2d = {};
|
||||
/* For all colliders, "shape" is a pointer to a phys2d_shape, "id" is a pointer to the shape data */
|
||||
var collider2d = Object.copy(component, {
|
||||
impl: {
|
||||
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); }
|
||||
},
|
||||
});
|
||||
|
||||
Object.hide(collider2d.impl, 'enabled');
|
||||
|
||||
collider2d.inputs = {};
|
||||
collider2d.inputs['M-s'] = function() { this.sensor = !this.sensor; }
|
||||
collider2d.inputs['M-s'].doc = "Toggle if this collider is a sensor.";
|
||||
|
@ -296,11 +379,27 @@ collider2d.inputs['M-s'].doc = "Toggle if this collider is a sensor.";
|
|||
collider2d.inputs['M-t'] = function() { this.enabled = !this.enabled; }
|
||||
collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled.";
|
||||
|
||||
Object.mix(os.make_poly2d(), {
|
||||
component.polygon2d = Object.copy(collider2d, {
|
||||
toJSON:json_from_whitelist([
|
||||
'points',
|
||||
'sensor'
|
||||
]),
|
||||
toString() { return "polygon2d"; },
|
||||
flipx: false,
|
||||
flipy: false,
|
||||
|
||||
boundingbox() {
|
||||
return bbox.frompoints(this.spoints());
|
||||
},
|
||||
|
||||
hides: ['id', 'shape', 'gameobject'],
|
||||
_enghook: os.make_poly2d,
|
||||
points:[],
|
||||
setpoints(points) {
|
||||
this.points = points;
|
||||
this.sync();
|
||||
},
|
||||
|
||||
/* EDITOR */
|
||||
spoints() {
|
||||
var spoints = this.points.slice();
|
||||
|
@ -346,71 +445,63 @@ function pointscaler(x) {
|
|||
this.points = this.points.map(p => p.mult(x));
|
||||
}
|
||||
|
||||
Object.mixin(os.make_poly2d(), {
|
||||
sync() {
|
||||
this.setverts(this.points);
|
||||
},
|
||||
component.polygon2d.impl = Object.mix(collider2d.impl, {
|
||||
sync() { poly2d.setverts(this.id,this.spoints()); },
|
||||
query() { return physics.shape_query(this.shape); },
|
||||
grow: pointscaler,
|
||||
});
|
||||
|
||||
var polyinputs = Object.create(collider2d.inputs);
|
||||
os.make_poly2d().inputs = polyinputs;
|
||||
var polygon2d = component.polygon2d;
|
||||
|
||||
polyinputs = {};
|
||||
polyinputs.f10 = function() {
|
||||
polygon2d.inputs = {};
|
||||
//polygon2d.inputs.post = function() { this.sync(); };
|
||||
polygon2d.inputs.f10 = function() {
|
||||
this.points = Math.sortpointsccw(this.points);
|
||||
};
|
||||
polyinputs.f10.doc = "Sort all points to be CCW order.";
|
||||
polygon2d.inputs.f10.doc = "Sort all points to be CCW order.";
|
||||
|
||||
polyinputs['C-lm'] = function() {
|
||||
polygon2d.inputs['C-lm'] = function() {
|
||||
this.points.push(this.gameobject.world2this(input.mouse.worldpos()));
|
||||
};
|
||||
polyinputs['C-lm'].doc = "Add a point to location of mouse.";
|
||||
polyinputs.lm = function(){};
|
||||
polyinputs.lm.released = function(){};
|
||||
polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse.";
|
||||
polygon2d.inputs.lm = function(){};
|
||||
polygon2d.inputs.lm.released = function(){};
|
||||
|
||||
polyinputs['C-M-lm'] = function() {
|
||||
polygon2d.inputs['C-M-lm'] = function() {
|
||||
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);
|
||||
};
|
||||
polyinputs['C-M-lm'].doc = "Remove point under mouse.";
|
||||
polygon2d.inputs['C-M-lm'].doc = "Remove point under mouse.";
|
||||
|
||||
polyinputs['C-b'] = function() {
|
||||
polygon2d.inputs['C-b'] = function() {
|
||||
this.points = this.spoints;
|
||||
this.flipx = false;
|
||||
this.flipy = false;
|
||||
};
|
||||
polyinputs['C-b'].doc = "Freeze mirroring in place.";
|
||||
polygon2d.inputs['C-b'].doc = "Freeze mirroring in place.";
|
||||
|
||||
var edge2d = {
|
||||
component.edge2d = Object.copy(collider2d, {
|
||||
toJSON:json_from_whitelist([
|
||||
'sensor',
|
||||
'thickness',
|
||||
'points',
|
||||
'hollow',
|
||||
'hollowt',
|
||||
'angle',
|
||||
]),
|
||||
dimensions:2,
|
||||
thickness:1,
|
||||
thickness:0,
|
||||
/* if type === -1, point to point */
|
||||
type: Spline.type.catmull,
|
||||
C: 1, /* when in bezier, continuity required. 0, 1 or 2. */
|
||||
looped: false,
|
||||
angle: 0.5, /* smaller for smoother bezier */
|
||||
elasticity: 0,
|
||||
friction: 0,
|
||||
sync() {
|
||||
var ppp = this.sample();
|
||||
this.segs ??= [];
|
||||
var count = ppp.length-1;
|
||||
this.segs.length = count;
|
||||
for (var i = 0; i < count; i++) {
|
||||
this.segs[i] ??= os.make_seg2d(this.body);
|
||||
this.segs[i].set_endpoints(ppp[i],ppp[i+1]);
|
||||
this.segs[i].set_neighbors(ppp[i],ppp[i+1]);
|
||||
this.segs[i].radius = this.thickness;
|
||||
this.segs[i].elasticity = this.elasticity;
|
||||
this.segs[i].friction = this.friction;
|
||||
this.segs[i].collide = this.collide;
|
||||
}
|
||||
},
|
||||
|
||||
flipx: false,
|
||||
flipy: false,
|
||||
points:[],
|
||||
toString() { return "edge2d"; },
|
||||
|
||||
hollow: false,
|
||||
hollowt: 0,
|
||||
|
@ -449,12 +540,14 @@ var edge2d = {
|
|||
return arr1.concat(arr2.reverse());
|
||||
}
|
||||
|
||||
if (this.looped)
|
||||
spoints = spoints.wrapped(1);
|
||||
|
||||
return spoints;
|
||||
},
|
||||
|
||||
setpoints(points) {
|
||||
this.points = points;
|
||||
// this.sync();
|
||||
},
|
||||
|
||||
post() {
|
||||
this.points = [];
|
||||
},
|
||||
|
@ -485,6 +578,9 @@ var edge2d = {
|
|||
|
||||
boundingbox() { return bbox.frompoints(this.points.map(x => x.scale(this.gameobject.scale))); },
|
||||
|
||||
hides: ['gameobject', 'id', 'shape'],
|
||||
_enghook: os.make_edge2d,
|
||||
|
||||
/* EDITOR */
|
||||
gizmo() {
|
||||
if (this.type === Spline.type.catmull || this.type === -1) {
|
||||
|
@ -592,64 +688,71 @@ var edge2d = {
|
|||
this.points.forEach(x =>picks.push(make_point_obj(this,x)));
|
||||
return picks;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
component.edge2d = function(obj) {
|
||||
if (!obj.body) obj.rigidify();
|
||||
var edge = Object.create(edge2d);
|
||||
edge.body = obj.body;
|
||||
return edge;
|
||||
}
|
||||
component.edge2d.impl = Object.mix(collider2d.impl, {
|
||||
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;
|
||||
edge2d.setverts(this.id,points);
|
||||
this.sensor = sensor;
|
||||
},
|
||||
});
|
||||
|
||||
edge2d.spoints.doc = "Returns the controls points after modifiers are applied, such as it being hollow or mirrored on its axises.";
|
||||
edge2d.inputs = {};
|
||||
edge2d.inputs.h = function() { this.hollow = !this.hollow; };
|
||||
edge2d.inputs.h.doc = "Toggle hollow.";
|
||||
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.h = function() { this.hollow = !this.hollow; };
|
||||
bucket.inputs.h.doc = "Toggle hollow.";
|
||||
|
||||
edge2d.inputs['C-g'] = function() { if (this.hollowt > 0) this.hollowt--; };
|
||||
edge2d.inputs['C-g'].doc = "Thin the hollow thickness.";
|
||||
edge2d.inputs['C-g'].rep = true;
|
||||
bucket.inputs['C-g'] = function() { if (this.hollowt > 0) this.hollowt--; };
|
||||
bucket.inputs['C-g'].doc = "Thin the hollow thickness.";
|
||||
bucket.inputs['C-g'].rep = true;
|
||||
|
||||
edge2d.inputs['C-f'] = function() { this.hollowt++; };
|
||||
edge2d.inputs['C-f'].doc = "Increase the hollow thickness.";
|
||||
edge2d.inputs['C-f'].rep = true;
|
||||
bucket.inputs['C-f'] = function() { this.hollowt++; };
|
||||
bucket.inputs['C-f'].doc = "Increase the hollow thickness.";
|
||||
bucket.inputs['C-f'].rep = true;
|
||||
|
||||
edge2d.inputs['M-v'] = function() { if (this.thickness > 0) this.thickness--; };
|
||||
edge2d.inputs['M-v'].doc = "Decrease spline thickness.";
|
||||
edge2d.inputs['M-v'].rep = true;
|
||||
bucket.inputs['M-v'] = function() { if (this.thickness > 0) this.thickness--; };
|
||||
bucket.inputs['M-v'].doc = "Decrease spline thickness.";
|
||||
bucket.inputs['M-v'].rep = true;
|
||||
|
||||
edge2d.inputs['C-y'] = function() {
|
||||
bucket.inputs['C-y'] = function() {
|
||||
this.points = this.spoints();
|
||||
this.flipx = false;
|
||||
this.flipy = false;
|
||||
this.hollow = false;
|
||||
};
|
||||
edge2d.inputs['C-y'].doc = "Freeze mirroring,";
|
||||
edge2d.inputs['M-b'] = function() { this.thickness++; };
|
||||
edge2d.inputs['M-b'].doc = "Increase spline thickness.";
|
||||
edge2d.inputs['M-b'].rep = true;
|
||||
bucket.inputs['C-y'].doc = "Freeze mirroring,";
|
||||
bucket.inputs['M-b'] = function() { this.thickness++; };
|
||||
bucket.inputs['M-b'].doc = "Increase spline thickness.";
|
||||
bucket.inputs['M-b'].rep = true;
|
||||
|
||||
edge2d.inputs.plus = function() {
|
||||
bucket.inputs.plus = function() {
|
||||
if (this.angle <= 1) {
|
||||
this.angle = 1;
|
||||
return;
|
||||
}
|
||||
this.angle *= 0.9;
|
||||
};
|
||||
edge2d.inputs.plus.doc = "Increase the number of samples of this spline.";
|
||||
edge2d.inputs.plus.rep = true;
|
||||
bucket.inputs.plus.doc = "Increase the number of samples of this spline.";
|
||||
bucket.inputs.plus.rep = true;
|
||||
|
||||
edge2d.inputs.minus = function() { this.angle *= 1.1; };
|
||||
edge2d.inputs.minus.doc = "Decrease the number of samples on this spline.";
|
||||
edge2d.inputs.minus.rep = true;
|
||||
bucket.inputs.minus = function() { this.angle *= 1.1; };
|
||||
bucket.inputs.minus.doc = "Decrease the number of samples on this spline.";
|
||||
bucket.inputs.minus.rep = true;
|
||||
|
||||
edge2d.inputs['C-r'] = function() { this.points = this.points.reverse(); };
|
||||
edge2d.inputs['C-r'].doc = "Reverse the order of the spline's points.";
|
||||
bucket.inputs['C-r'] = function() { this.points = this.points.reverse(); };
|
||||
bucket.inputs['C-r'].doc = "Reverse the order of the spline's points.";
|
||||
|
||||
edge2d.inputs['C-l'] = function() { this.looped = !this.looped};
|
||||
edge2d.inputs['C-l'].doc = "Toggle spline being looped.";
|
||||
bucket.inputs['C-l'] = function() { this.looped = !this.looped};
|
||||
bucket.inputs['C-l'].doc = "Toggle spline being looped.";
|
||||
|
||||
edge2d.inputs['C-c'] = function() {
|
||||
bucket.inputs['C-c'] = function() {
|
||||
switch(this.type) {
|
||||
case Spline.type.bezier:
|
||||
this.points = Spline.bezier2catmull(this.points);
|
||||
|
@ -658,9 +761,9 @@ edge2d.inputs['C-c'] = function() {
|
|||
this.type = Spline.type.catmull;
|
||||
};
|
||||
|
||||
edge2d.inputs['C-c'].doc = "Set type of spline to catmull-rom.";
|
||||
bucket.inputs['C-c'].doc = "Set type of spline to catmull-rom.";
|
||||
|
||||
edge2d.inputs['C-b'] = function() {
|
||||
bucket.inputs['C-b'] = function() {
|
||||
switch(this.type) {
|
||||
case Spline.type.catmull:
|
||||
this.points = Spline.catmull2bezier(Spline.catmull_caps(this.points));
|
||||
|
@ -669,10 +772,10 @@ edge2d.inputs['C-b'] = function() {
|
|||
this.type = Spline.type.bezier;
|
||||
};
|
||||
|
||||
edge2d.inputs['C-o'] = function() { this.type = -1; };
|
||||
edge2d.inputs['C-o'].doc = "Set spline to linear.";
|
||||
bucket.inputs['C-o'] = function() { this.type = -1; };
|
||||
bucket.inputs['C-o'].doc = "Set spline to linear.";
|
||||
|
||||
edge2d.inputs['C-M-lm'] = function() {
|
||||
bucket.inputs['C-M-lm'] = function() {
|
||||
if (Spline.is_catmull(this.type)) {
|
||||
var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
|
||||
if (idx === -1) return;
|
||||
|
@ -682,12 +785,12 @@ edge2d.inputs['C-M-lm'] = function() {
|
|||
|
||||
this.points = this.points.newfirst(idx);
|
||||
};
|
||||
edge2d.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
|
||||
bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
|
||||
|
||||
edge2d.inputs['C-lm'] = function() { this.add_node(input.mouse.worldpos()); }
|
||||
edge2d.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
|
||||
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.";
|
||||
|
||||
edge2d.inputs['C-M-lm'] = function() {
|
||||
bucket.inputs['C-M-lm'] = function() {
|
||||
var idx = -1;
|
||||
if (Spline.is_catmull(this.type))
|
||||
idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
|
||||
|
@ -699,12 +802,12 @@ edge2d.inputs['C-M-lm'] = function() {
|
|||
|
||||
this.rm_node(idx);
|
||||
};
|
||||
edge2d.inputs['C-M-lm'].doc = "Remove point from the spline.";
|
||||
bucket.inputs['C-M-lm'].doc = "Remove point from the spline.";
|
||||
|
||||
edge2d.inputs.lm = function(){};
|
||||
edge2d.inputs.lm.released = function(){};
|
||||
bucket.inputs.lm = function(){};
|
||||
bucket.inputs.lm.released = function(){};
|
||||
|
||||
edge2d.inputs.lb = function() {
|
||||
bucket.inputs.lb = function() {
|
||||
var np = [];
|
||||
|
||||
this.points.forEach(function(c) {
|
||||
|
@ -713,10 +816,10 @@ edge2d.inputs.lb = function() {
|
|||
|
||||
this.points = np;
|
||||
};
|
||||
edge2d.inputs.lb.doc = "Rotate the points CCW.";
|
||||
edge2d.inputs.lb.rep = true;
|
||||
bucket.inputs.lb.doc = "Rotate the points CCW.";
|
||||
bucket.inputs.lb.rep = true;
|
||||
|
||||
edge2d.inputs.rb = function() {
|
||||
bucket.inputs.rb = function() {
|
||||
var np = [];
|
||||
|
||||
this.points.forEach(function(c) {
|
||||
|
@ -725,27 +828,37 @@ edge2d.inputs.rb = function() {
|
|||
|
||||
this.points = np;
|
||||
};
|
||||
edge2d.inputs.rb.doc = "Rotate the points CW.";
|
||||
edge2d.inputs.rb.rep = true;
|
||||
bucket.inputs.rb.doc = "Rotate the points CW.";
|
||||
bucket.inputs.rb.rep = true;
|
||||
|
||||
/* CIRCLE */
|
||||
component.circle2d = Object.copy(collider2d, {
|
||||
radius:10,
|
||||
offset:[0,0],
|
||||
toString() { return "circle2d"; },
|
||||
|
||||
function shape_maker(maker) {
|
||||
return function(obj) {
|
||||
if (!obj.body) obj.rigidify();
|
||||
return maker(obj.body);
|
||||
}
|
||||
}
|
||||
component.circle2d = shape_maker(os.make_circle2d);
|
||||
component.poly2d = shape_maker(os.make_poly2d);
|
||||
component.seg2d = shape_maker(os.make_seg2d);
|
||||
boundingbox() {
|
||||
return bbox.fromcwh([0,0], [this.radius,this.radius]);
|
||||
},
|
||||
|
||||
Object.mix(os.make_circle2d(), {
|
||||
boundingbox() { return bbox.fromcwh(this.offset, [this.radius,this.radius]); },
|
||||
hides: ['gameobject', 'id', 'shape', 'scale'],
|
||||
_enghook: os.make_circle2d,
|
||||
});
|
||||
|
||||
component.circle2d.impl = Object.mix({
|
||||
toJSON:json_from_whitelist([
|
||||
"pos",
|
||||
"radius",
|
||||
]),
|
||||
|
||||
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) { circle2d.set_offset(this.id,x); circle2d.sync(this.id); },
|
||||
get offset() { circle2d.get_offset(this.id); },
|
||||
|
||||
get pos() { return this.offset; },
|
||||
set pos(x) { this.offset = x; },
|
||||
|
||||
|
@ -753,6 +866,7 @@ Object.mix(os.make_circle2d(), {
|
|||
if (typeof x === 'number') this.scale *= x;
|
||||
else if (typeof x === 'object') this.scale *= x[0];
|
||||
},
|
||||
});
|
||||
|
||||
}, collider2d.impl);
|
||||
|
||||
return {component, SpriteAnim};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
var debug = {};
|
||||
|
||||
debug.fn_break = function(fn,obj = globalThis) {
|
||||
if (typeof fn !== 'function') return;
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
|
||||
game.loadurs();
|
||||
|
||||
console.info(`window size: ${window.size}, render size: ${window.rendersize}`);
|
||||
|
||||
player[0].control(debug);
|
||||
render.clear_color([35,60,92,255].map(x => x/255));
|
||||
|
||||
var show_frame = true;
|
||||
|
||||
|
@ -341,7 +344,7 @@ var editor = {
|
|||
root = root ? root + "." : root;
|
||||
Object.entries(obj.objects).forEach(function(x) {
|
||||
var p = root + x[0];
|
||||
render.text(p, x[1].this2screen(), 1, editor.color_depths[depth]);
|
||||
render.text(p, x[1].screenpos(), 1, editor.color_depths[depth]);
|
||||
editor.draw_objects_names(x[1], p, depth+1);
|
||||
});
|
||||
},
|
||||
|
@ -388,8 +391,8 @@ var editor = {
|
|||
|
||||
gui() {
|
||||
/* Clean out killed objects */
|
||||
// if (show_frame)
|
||||
/// render.line(shape.box(window.rendersize.x, window.rendersize.y).wrapped(1).map(p => game.camera.world2view(p)), Color.yellow);
|
||||
if (show_frame)
|
||||
render.line(shape.box(window.rendersize.x, window.rendersize.y).wrapped(1).map(p => game.camera.world2view(p)), Color.yellow);
|
||||
|
||||
render.text([0,0], game.camera.world2view([0,0]));
|
||||
|
||||
|
@ -399,7 +402,7 @@ var editor = {
|
|||
if (this.comp_info && this.sel_comp)
|
||||
render.text(input.print_pawn_kbm(this.sel_comp,false), [100,700],1);
|
||||
|
||||
render.cross(editor.edit_level.this2screen(),3,Color.blue);
|
||||
render.cross(editor.edit_level.screenpos(),3,Color.blue);
|
||||
|
||||
var thiso = editor.get_this();
|
||||
var clvl = thiso;
|
||||
|
@ -444,16 +447,16 @@ var editor = {
|
|||
render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
|
||||
|
||||
this.selectlist.forEach(function(x) {
|
||||
render.text(x.urstr(), x.this2screen().add([0, render.font.linegap*2]), 1, Color.editor.ur);
|
||||
render.text(x.pos.map(function(x) { return Math.round(x); }), x.this2screen());
|
||||
render.cross(x.this2screen(), 10, Color.blue);
|
||||
render.text(x.urstr(), x.screenpos().add([0, render.font.linegap*2]), 1, Color.editor.ur);
|
||||
render.text(x.pos.map(function(x) { return Math.round(x); }), x.screenpos());
|
||||
render.cross(x.screenpos(), 10, Color.blue);
|
||||
});
|
||||
|
||||
Object.entries(thiso.objects).forEach(function(x) {
|
||||
var p = x[1].namestr();
|
||||
render.text(p, x[1].this2screen().add([0,render.font.linegap]),1,editor.color_depths[depth]);
|
||||
render.point(x[1].this2screen(),5,Color.blue.alpha(0.3));
|
||||
render.point(x[1].this2screen(), 1, Color.red);
|
||||
render.text(p, x[1].screenpos().add([0,render.font.linegap]),1,editor.color_depths[depth]);
|
||||
render.point(x[1].screenpos(),5,Color.blue.alpha(0.3));
|
||||
render.point(x[1].screenpos(), 1, Color.red);
|
||||
});
|
||||
|
||||
var mg = physics.pos_query(input.mouse.worldpos());
|
||||
|
@ -471,7 +474,7 @@ var editor = {
|
|||
for (var key in this.selectlist[0].components) {
|
||||
var selected = this.sel_comp === this.selectlist[0].components[key];
|
||||
var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]";
|
||||
render.text(str, this.selectlist[0].this2screen().add([0,-render.font.linegap*(i++)]));
|
||||
render.text(str, this.selectlist[0].screenpos().add([0,-render.font.linegap*(i++)]));
|
||||
}
|
||||
|
||||
if (this.sel_comp) {
|
||||
|
@ -876,7 +879,7 @@ editor.inputs['C-s'] = function() {
|
|||
}
|
||||
|
||||
var savejs = saveobj.json_obj();
|
||||
var tur = saveobj.ur;
|
||||
var tur = saveobj.get_ur();
|
||||
if (!tur) {
|
||||
console.warn(`Can't save object because it has no ur.`);
|
||||
return;
|
||||
|
|
|
@ -1,97 +1,107 @@
|
|||
"use math";
|
||||
|
||||
Object.defineProperty(String.prototype, "tolast", {
|
||||
value: function (val) {
|
||||
Object.defineProperty(String.prototype, 'tolast', {
|
||||
value: function(val) {
|
||||
var idx = this.lastIndexOf(val);
|
||||
if (idx === -1) return this.slice();
|
||||
return this.slice(0, idx);
|
||||
},
|
||||
return this.slice(0,idx);
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(String.prototype, "dir", {
|
||||
value: function () {
|
||||
if (!this.includes("/")) return "";
|
||||
return this.tolast("/");
|
||||
},
|
||||
Object.defineProperty(String.prototype, 'dir', {
|
||||
value: function() {
|
||||
if (!this.includes('/')) return "";
|
||||
return this.tolast('/');
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(String.prototype, "folder", {
|
||||
value: function () {
|
||||
Object.defineProperty(String.prototype, 'folder', {
|
||||
value: function() {
|
||||
var dir = this.dir();
|
||||
if (!dir) return "";
|
||||
else return dir + "/";
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
globalThis.Resources = {};
|
||||
|
||||
Resources.replpath = function (str, path) {
|
||||
Resources.replpath = function(str, path)
|
||||
{
|
||||
if (!str) return str;
|
||||
if (str[0] === "/") return str.rm(0);
|
||||
if (str[0] === "/")
|
||||
return str.rm(0);
|
||||
|
||||
if (str[0] === "@") return os.prefpath() + "/" + 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;
|
||||
var tr = stem + "/" +str;
|
||||
if (io.exists(tr)) return tr;
|
||||
stem = stem.updir();
|
||||
}
|
||||
|
||||
return str;
|
||||
};
|
||||
}
|
||||
|
||||
Resources.replstrs = function (path) {
|
||||
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) {
|
||||
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) {
|
||||
json.encode = function(value, replacer, space = 1)
|
||||
{
|
||||
return JSON.stringify(value, replacer, space);
|
||||
};
|
||||
}
|
||||
|
||||
json.decode = function (text, reviver) {
|
||||
json.decode = function(text, reviver)
|
||||
{
|
||||
if (!text) return undefined;
|
||||
return JSON.parse(text, reviver);
|
||||
};
|
||||
return JSON.parse(text,reviver);
|
||||
}
|
||||
|
||||
json.readout = function (obj) {
|
||||
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];
|
||||
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.",
|
||||
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) {
|
||||
Resources.sounds = ["wav", 'flac', 'mp3', "qoa"];
|
||||
Resources.is_image = function(path) {
|
||||
var ext = path.ext();
|
||||
return Resources.images.any((x) => x === ext);
|
||||
};
|
||||
return Resources.images.any(x => x === ext);
|
||||
}
|
||||
|
||||
function find_ext(file, ext) {
|
||||
function find_ext(file, ext)
|
||||
{
|
||||
if (io.exists(file)) return file;
|
||||
for (var e of ext) {
|
||||
var nf = `${file}.${e}`;
|
||||
|
@ -100,48 +110,46 @@ function find_ext(file, ext) {
|
|||
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);
|
||||
};
|
||||
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";
|
||||
profile.best_t = function(t) {
|
||||
var qq = 'ns';
|
||||
if (t > 1000) {
|
||||
t /= 1000;
|
||||
qq = "us";
|
||||
qq = 'us';
|
||||
if (t > 1000) {
|
||||
t /= 1000;
|
||||
qq = "ms";
|
||||
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.report = function(start, msg = "[undefined report]")
|
||||
{
|
||||
console.info(`${msg} in ${profile.best_t(profile.now()-start)}`);
|
||||
}
|
||||
|
||||
profile.addreport = function (cache, line, start) {
|
||||
profile.addreport = function(cache, line, start)
|
||||
{
|
||||
cache[line] ??= [];
|
||||
cache[line].push(profile.now() - start);
|
||||
};
|
||||
cache[line].push(profile.now()-start);
|
||||
}
|
||||
|
||||
profile.printreport = function (cache, name) {
|
||||
profile.printreport = function(cache, name)
|
||||
{
|
||||
var report = name + "\n";
|
||||
for (var i in cache)
|
||||
report += `${i} ${profile.best_t(cache[i].reduce((a, b) => a + b) / cache[i].length)}\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) {
|
||||
console.say = function(msg) {
|
||||
msg += "\n";
|
||||
console.print(msg);
|
||||
console.transcript += msg;
|
||||
|
@ -150,14 +158,16 @@ console.log = console.say;
|
|||
globalThis.say = console.say;
|
||||
globalThis.print = console.print;
|
||||
|
||||
console.pprint = function (msg, lvl = 0) {
|
||||
if (typeof msg === "object") msg = JSON.stringify(msg, null, 2);
|
||||
console.pprint = function(msg,lvl = 0) {
|
||||
|
||||
if (typeof msg === 'object')
|
||||
msg = JSON.stringify(msg, null, 2);
|
||||
|
||||
var file = "nofile";
|
||||
var line = 0;
|
||||
console.rec(0, msg, file, line);
|
||||
console.rec(0,msg,file,line);
|
||||
|
||||
var caller = new Error().stack.split("\n")[2];
|
||||
var caller = (new Error()).stack.split('\n')[2];
|
||||
if (caller) {
|
||||
var md = caller.match(/\((.*)\:/);
|
||||
var m = md ? md[1] : "SCRIPT";
|
||||
|
@ -170,33 +180,19 @@ console.pprint = function (msg, lvl = 0) {
|
|||
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) {
|
||||
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");
|
||||
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.stack = function(skip = 0) { console.log(console.stackstr(skip+1)); };
|
||||
|
||||
console.stdout_lvl = 1;
|
||||
console.trace = console.stack;
|
||||
|
@ -211,14 +207,15 @@ console.doc = {
|
|||
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.",
|
||||
clear: "Clear console."
|
||||
};
|
||||
|
||||
globalThis.global = globalThis;
|
||||
|
||||
var profcache = {};
|
||||
|
||||
function use(file, env = {}, script) {
|
||||
function use(file, env = {}, script)
|
||||
{
|
||||
file = Resources.find_script(file);
|
||||
var st = profile.now();
|
||||
|
||||
|
@ -231,7 +228,7 @@ function use(file, env = {}, script) {
|
|||
}
|
||||
script ??= Resources.replstrs(file);
|
||||
script = `(function() { var self = this; ${script}; })`;
|
||||
var fn = os.eval(file, script);
|
||||
var fn = os.eval(file,script);
|
||||
use.cache[file] = fn;
|
||||
var ret = fn.call(env);
|
||||
profile.addreport(profcache, file, st);
|
||||
|
@ -240,181 +237,158 @@ function use(file, env = {}, script) {
|
|||
|
||||
use.cache = {};
|
||||
|
||||
global.check_registers = function (obj) {
|
||||
if (typeof obj.update === "function")
|
||||
global.check_registers = function(obj)
|
||||
{
|
||||
if (typeof obj.update === 'function')
|
||||
obj.timers.push(Register.update.register(obj.update.bind(obj)));
|
||||
|
||||
if (typeof obj.physupdate === "function")
|
||||
if (typeof obj.physupdate === 'function')
|
||||
obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj)));
|
||||
|
||||
if (typeof obj.draw === "function")
|
||||
if (typeof obj.draw === 'function')
|
||||
obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj));
|
||||
|
||||
if (typeof obj.debug === "function")
|
||||
if (typeof obj.debug === 'function')
|
||||
obj.timers.push(Register.debug.register(obj.debug.bind(obj)));
|
||||
|
||||
if (typeof obj.gui === "function")
|
||||
if (typeof obj.gui === 'function')
|
||||
obj.timers.push(Register.gui.register(obj.gui.bind(obj)));
|
||||
|
||||
if (typeof obj.screengui === "function")
|
||||
if (typeof obj.screengui === 'function')
|
||||
obj.timers.push(Register.screengui.register(obj.screengui.bind(obj)));
|
||||
|
||||
for (var k in obj) {
|
||||
if (!k.startswith("on_")) continue;
|
||||
var signal = k.fromfirst("on_");
|
||||
Event.observe(signal, obj, obj[k]);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Object.assign(global, use("scripts/base"));
|
||||
global.obscure("global");
|
||||
global.obscure('global');
|
||||
global.mixin("scripts/render");
|
||||
global.mixin("scripts/debug");
|
||||
|
||||
var frame_t = profile.secs(profile.now());
|
||||
var phys_step = 1/240;
|
||||
|
||||
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";
|
||||
};
|
||||
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'; }
|
||||
|
||||
var physlag = 0;
|
||||
|
||||
var gggstart = game.engine_start;
|
||||
game.engine_start = function (s) {
|
||||
game.engine_start = function(s) {
|
||||
game.startengine = 1;
|
||||
gggstart(
|
||||
function () {
|
||||
gggstart(function() {
|
||||
global.mixin("scripts/sound.js");
|
||||
world_start();
|
||||
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");
|
||||
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();
|
||||
|
||||
shape.quad = {
|
||||
pos: os.make_buffer([0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], 0),
|
||||
verts: 4,
|
||||
uv: os.make_buffer([0, 1, 1, 1, 0, 0, 1, 0], 2),
|
||||
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
|
||||
count: 6,
|
||||
};
|
||||
|
||||
shape.triangle = {
|
||||
pos: os.make_buffer([0, 0, 0, 0.5, 1, 0, 1, 0, 0], 0),
|
||||
uv: os.make_buffer([0, 0, 0.5, 1, 1, 0], 2),
|
||||
verts: 3,
|
||||
count: 3,
|
||||
index: os.make_buffer([0, 2, 1], 1),
|
||||
};
|
||||
|
||||
render.init();
|
||||
},
|
||||
process,
|
||||
window.size.x,
|
||||
window.size.y,
|
||||
);
|
||||
};
|
||||
}, process);
|
||||
}
|
||||
|
||||
game.startengine = 0;
|
||||
var frames = [];
|
||||
|
||||
function process() {
|
||||
var startframe = profile.now();
|
||||
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();
|
||||
|
||||
if (sim.mode === "play" || sim.mode === "step") {
|
||||
prosperon.update(dt * game.timescale);
|
||||
if (sim.mode === "step") sim.pause();
|
||||
prosperon.update(dt*game.timescale);
|
||||
if (sim.mode === "step")
|
||||
sim.pause();
|
||||
|
||||
physlag += dt;
|
||||
|
||||
while (physlag > physics.delta) {
|
||||
physlag -= physics.delta;
|
||||
var st = profile.now();
|
||||
prosperon.phys2d_step(physics.delta * game.timescale);
|
||||
prosperon.physupdate(physics.delta * game.timescale);
|
||||
profile.addreport(profcache, "physics step", st);
|
||||
while (physlag > phys_step) {
|
||||
physlag -= phys_step;
|
||||
prosperon.phys2d_step(phys_step*game.timescale);
|
||||
prosperon.physupdate(phys_step*game.timescale);
|
||||
}
|
||||
}
|
||||
var st = profile.now();
|
||||
prosperon.window_render(window.size);
|
||||
prosperon.draw();
|
||||
prosperon.debug();
|
||||
prosperon.gui();
|
||||
prosperon.screengui();
|
||||
prosperon.hookend?.();
|
||||
profile.addreport(profcache, "render frame", st);
|
||||
frames.push(profile.secs(profile.now() - startframe));
|
||||
if (frames.length > 20) frames.shift();
|
||||
}
|
||||
|
||||
globalThis.fps = function () {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < frames.length; i++) sum += frames[i];
|
||||
return frames.length / sum;
|
||||
};
|
||||
if (!game.camera)
|
||||
prosperon.window_render(world, 1);
|
||||
else
|
||||
prosperon.window_render(game.camera, game.camera.zoom);
|
||||
|
||||
render.set_camera();
|
||||
render.sprites(); // blits all sprites
|
||||
render.emitters(); // blits emitters
|
||||
prosperon.draw(); // draw calls
|
||||
debug.draw(); // calls needed debugs
|
||||
render.flush();
|
||||
|
||||
prosperon.hook3d?.();
|
||||
|
||||
render.hud_res(window.rendersize);
|
||||
prosperon.gui();
|
||||
render.flush();
|
||||
render.hud_res(window.size);
|
||||
prosperon.screengui();
|
||||
render.flush();
|
||||
|
||||
render.end_pass();
|
||||
}
|
||||
|
||||
game.timescale = 1;
|
||||
|
||||
var eachobj = function (obj, fn) {
|
||||
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);
|
||||
val = eachobj(obj.objects[o],fn);
|
||||
if (val) return val;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
game.all_objects = function (fn, startobj = world) {
|
||||
return eachobj(startobj, fn);
|
||||
};
|
||||
game.find_object = function (fn, startobj = world) {};
|
||||
game.all_objects = function(fn, startobj = world) { return eachobj(startobj,fn); };
|
||||
game.find_object = function(fn, startobj = world) {
|
||||
|
||||
}
|
||||
|
||||
game.tags = {};
|
||||
game.tag_add = function (tag, obj) {
|
||||
game.tag_add = function(tag, obj) {
|
||||
game.tags[tag] ??= {};
|
||||
game.tags[tag][obj.guid] = obj;
|
||||
};
|
||||
}
|
||||
|
||||
game.tag_rm = function (tag, 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.tag_clear_guid = function(guid)
|
||||
{
|
||||
for (var tag in game.tags)
|
||||
delete game.tags[tag][guid];
|
||||
}
|
||||
|
||||
game.objects_with_tag = function (tag) {
|
||||
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.";
|
||||
|
@ -422,85 +396,86 @@ game.doc.pause = "Pause game simulation.";
|
|||
game.doc.play = "Resume or start game simulation.";
|
||||
game.doc.camera = "Current camera.";
|
||||
|
||||
game.texture = function (path, force = false) {
|
||||
if (force && game.texture.cache[path]) return game.texture.cache[path];
|
||||
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);
|
||||
} 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) {
|
||||
v = v.split(".");
|
||||
range = range.split(".");
|
||||
prosperon.semver.valid = function(v, range)
|
||||
{
|
||||
v = v.split('.');
|
||||
range = range.split('.');
|
||||
if (v.length !== 3) return undefined;
|
||||
if (range.length !== 3) return undefined;
|
||||
|
||||
if (range[0][0] === "^") {
|
||||
if (range[0][0] === '^') {
|
||||
range[0] = range[0].slice(1);
|
||||
if (parseInt(v[0]) >= parseInt(range[0])) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (range[0] === "~") {
|
||||
if (range[0] === '~') {
|
||||
range[0] = range[0].slice(1);
|
||||
for (var i = 0; i < 2; i++)
|
||||
if (parseInt(v[i]) < parseInt(range[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return prosperon.semver.cmp(v.join("."), range.join(".")) === 0;
|
||||
};
|
||||
return prosperon.semver.cmp(v.join('.'), range.join('.')) === 0;
|
||||
}
|
||||
|
||||
prosperon.semver.cmp = function (v1, v2) {
|
||||
var ver1 = v1.split(".");
|
||||
var ver2 = v2.split(".");
|
||||
prosperon.semver.cmp = function(v1, v2)
|
||||
{
|
||||
var ver1 = v1.split('.');
|
||||
var ver2 = v2.split('.');
|
||||
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var n1 = parseInt(ver1[i]);
|
||||
var n2 = parseInt(ver2[i]);
|
||||
if (n1 > n2) return 1;
|
||||
else if (n1 < n2) return -1;
|
||||
if (n1 > n2)
|
||||
return 1;
|
||||
else if (n1 < n2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
prosperon.semver.doc =
|
||||
"Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.";
|
||||
prosperon.semver.cmp.doc =
|
||||
"Compare two semantic version numbers, given like X.X.X.";
|
||||
prosperon.semver.doc = "Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.";
|
||||
prosperon.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X.";
|
||||
prosperon.semver.valid.doc = `Test if semantic version v is valid, given a range.
|
||||
Range is given by a semantic versioning number, prefixed with nothing, a ~, or a ^.
|
||||
~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid.
|
||||
^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`;
|
||||
|
||||
prosperon.iconified = function (icon) {};
|
||||
prosperon.focus = function (focus) {};
|
||||
prosperon.resize = function (dimensions) {
|
||||
window.size.x = dimensions.x;
|
||||
window.size.y = dimensions.y;
|
||||
};
|
||||
prosperon.suspended = function (sus) {};
|
||||
prosperon.mouseenter = function () {};
|
||||
prosperon.mouseleave = function () {};
|
||||
prosperon.touchpress = function (touches) {};
|
||||
prosperon.touchrelease = function (touches) {};
|
||||
prosperon.touchmove = function (touches) {};
|
||||
prosperon.clipboardpaste = function (str) {};
|
||||
prosperon.quit = function () {
|
||||
prosperon.iconified = function(icon) {};
|
||||
prosperon.focus = function(focus) {};
|
||||
prosperon.resize = function(dimensions) {};
|
||||
prosperon.suspended = function(sus) {};
|
||||
prosperon.mouseenter = function(){};
|
||||
prosperon.mouseleave = function(){};
|
||||
prosperon.touchpress = function(touches){};
|
||||
prosperon.touchrelease = function(touches){};
|
||||
prosperon.touchmove = function(touches){};
|
||||
prosperon.clipboardpaste = function(str){};
|
||||
prosperon.quit = function(){
|
||||
say(profile.printreport(profcache, "USE REPORT"));
|
||||
say(profile.printreport(entityreport, "ENTITY REPORT"));
|
||||
|
||||
console.info("QUITTING");
|
||||
for (var i in debug.log.time)
|
||||
say(debug.log.time[i].map((x) => profile.ms(x)));
|
||||
say(debug.log.time[i].map(x=>profile.ms(x)));
|
||||
};
|
||||
|
||||
global.mixin("scripts/input");
|
||||
|
@ -551,21 +526,18 @@ var Register = {
|
|||
var n = {};
|
||||
var fns = [];
|
||||
|
||||
n.register = function (fn, obj) {
|
||||
if (typeof fn !== "function") return;
|
||||
if (typeof obj === "object") fn = fn.bind(obj);
|
||||
n.register = function(fn, obj) {
|
||||
if (typeof fn !== 'function') return;
|
||||
if (typeof obj === 'object')
|
||||
fn = fn.bind(obj);
|
||||
fns.push(fn);
|
||||
return function () {
|
||||
return function() {
|
||||
fns.remove(fn);
|
||||
};
|
||||
};
|
||||
prosperon[name] = function (...args) {
|
||||
fns.forEach((x) => x(...args));
|
||||
};
|
||||
}
|
||||
prosperon[name] = function(...args) { fns.forEach(x => x(...args)); }
|
||||
prosperon[name].fns = fns;
|
||||
n.clear = function () {
|
||||
fns = [];
|
||||
};
|
||||
n.clear = function() { fns = []; }
|
||||
|
||||
Register[name] = n;
|
||||
Register.registries.push(n);
|
||||
|
@ -591,32 +563,33 @@ var Event = {
|
|||
},
|
||||
|
||||
unobserve(name, obj) {
|
||||
this.events[name] = this.events[name].filter((x) => x[0] !== obj);
|
||||
this.events[name] = this.events[name].filter(x => x[0] !== obj);
|
||||
},
|
||||
|
||||
rm_obj(obj) {
|
||||
Object.keys(this.events).forEach((name) => Event.unobserve(name, obj));
|
||||
Object.keys(this.events).forEach(name => Event.unobserve(name,obj));
|
||||
},
|
||||
|
||||
notify(name, ...args) {
|
||||
if (!this.events[name]) return;
|
||||
this.events[name].forEach(function (x) {
|
||||
this.events[name].forEach(function(x) {
|
||||
x[1].call(x[0], ...args);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// window.rendersize is the resolution the game renders at
|
||||
// window.size is the physical size of the window on the desktop
|
||||
// set to one of the following
|
||||
// stretch render to fill window
|
||||
// keep render exact dimensions, with no stretching
|
||||
// width keep render at width
|
||||
// height keep render at height
|
||||
// expand width or height
|
||||
// full expand out beyond window
|
||||
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.mode = "keep";
|
||||
|
||||
window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
|
||||
|
||||
|
@ -624,26 +597,19 @@ global.mixin("scripts/spline");
|
|||
global.mixin("scripts/components");
|
||||
|
||||
window.doc = {};
|
||||
window.doc.dimensions =
|
||||
"Window width and height packaged in an array [width,height]";
|
||||
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.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
|
||||
|
||||
global.mixin("scripts/actor");
|
||||
global.mixin("scripts/entity");
|
||||
|
||||
function world_start() {
|
||||
globalThis.world = Object.create(entity);
|
||||
world.transform = os.make_transform();
|
||||
globalThis.world = os.make_gameobject();
|
||||
world.objects = {};
|
||||
world.toString = function () {
|
||||
return "world";
|
||||
};
|
||||
world.toString = function() { return "world"; };
|
||||
world.ur = "world";
|
||||
world.kill = function () {
|
||||
this.clear();
|
||||
};
|
||||
world.kill = function() { this.clear(); };
|
||||
world.phys = 2;
|
||||
world.zoom = 1;
|
||||
world._ed = { selectable: false };
|
||||
|
@ -658,4 +624,9 @@ global.mixin("scripts/widget");
|
|||
globalThis.mum = app.spawn("scripts/mum");
|
||||
|
||||
window.title = `Prosperon v${prosperon.version}`;
|
||||
window.size = [500, 500];
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -12,18 +12,7 @@ function obj_unique_name(name, obj) {
|
|||
return n;
|
||||
}
|
||||
|
||||
function unique_name(list, name = "new_object") {
|
||||
var str = name.replaceAll('.', '_');
|
||||
var n = 1;
|
||||
var t = str;
|
||||
while (list.indexOf(t) !== -1) {
|
||||
t = str + n;
|
||||
n++;
|
||||
}
|
||||
return t;
|
||||
};
|
||||
|
||||
var entity = {
|
||||
var gameobject = {
|
||||
get_comp_by_name(name) {
|
||||
var comps = [];
|
||||
for (var c of Object.values(this.components))
|
||||
|
@ -33,10 +22,78 @@ var entity = {
|
|||
return undefined;
|
||||
},
|
||||
|
||||
rigidify() {
|
||||
this.body = os.make_body(this.transform);
|
||||
check_dirty() {
|
||||
this._ed.urdiff = this.json_obj();
|
||||
this._ed.dirty = !Object.empty(this._ed.urdiff);
|
||||
return; // TODO: IMPLEMENT
|
||||
var lur = this.master.ur;
|
||||
if (!lur) return;
|
||||
var lur = lur.objects[this.toString()];
|
||||
var d = ediff(this._ed.urdiff, lur);
|
||||
if (!d || Object.empty(d))
|
||||
this._ed.inst = true;
|
||||
else
|
||||
this._ed.inst = false;
|
||||
},
|
||||
|
||||
namestr() {
|
||||
var s = this.toString();
|
||||
if (this._ed?.dirty)
|
||||
if (this._ed.inst) s += "#";
|
||||
else s += "*";
|
||||
return s;
|
||||
},
|
||||
|
||||
urstr() {
|
||||
var str = this.ur.name;
|
||||
if (this._ed.dirty) str = "*" + str;
|
||||
return str;
|
||||
},
|
||||
|
||||
full_path() {
|
||||
return this.path_from(world);
|
||||
},
|
||||
/* pin this object to the to object */
|
||||
pin(to) {
|
||||
var p = joint.pin(this,to);
|
||||
},
|
||||
slide(to, a = [0,0], b = [0,0], min = 0, max = 50) {
|
||||
var p = joint.slide(this, to, a, b, min, max);
|
||||
p.max_force = 500;
|
||||
p.break();
|
||||
},
|
||||
pivot(to, piv = this.pos) {
|
||||
var p = joint.pivot(this, to, piv);
|
||||
},
|
||||
/* groove is on to, from local points a and b, anchored to this at local anchor */
|
||||
groove(to, a, b, anchor = [0,0]) {
|
||||
var p = joint.groove(to, this, a, b, anchor);
|
||||
},
|
||||
damped_spring(to, length = Vector.length(this.pos,to.pos), stiffness = 1, damping = 1) {
|
||||
var dc = 2 * Math.sqrt(stiffness * this.mass);
|
||||
var p = joint.damped_spring(this, to, [0, 0], [0, 0], stiffness, damping * dc);
|
||||
},
|
||||
damped_rotary_spring(to, angle = 0, stiffness = 1, damping = 1) {
|
||||
/* calculate actual damping value from the damping ratio */
|
||||
/* damping = 1 is critical */
|
||||
var dc = 2 * Math.sqrt(stiffness * this.get_moi()); /* critical damping number */
|
||||
/* zeta = actual/critical */
|
||||
var p = joint.damped_rotary(this, to, angle, stiffness, damping * dc);
|
||||
},
|
||||
rotary_limit(to, min, max) {
|
||||
var p = joint.rotary(this, to, Math.turn2rad(min), Math.turn2rad(max));
|
||||
},
|
||||
ratchet(to, ratch) {
|
||||
var phase = this.angle - to.angle;
|
||||
var p = joint.ratchet(this, to, phase, Math.turn2rad(ratch));
|
||||
},
|
||||
gear(to, ratio = 1, phase = 0) {
|
||||
var phase = this.angle - to.angle;
|
||||
var p = joint.gear(this, to, phase, ratio);
|
||||
},
|
||||
motor(to, rate) {
|
||||
var p = joint.motor(this, to, rate);
|
||||
},
|
||||
|
||||
path_from(o) {
|
||||
var p = this.toString();
|
||||
|
@ -49,10 +106,6 @@ var entity = {
|
|||
return p;
|
||||
},
|
||||
|
||||
drawlayer: 0,
|
||||
|
||||
full_path() { return this.path_from(world); },
|
||||
|
||||
clear() {
|
||||
for (var k in this.objects) {
|
||||
this.objects[k].kill();
|
||||
|
@ -60,11 +113,6 @@ var entity = {
|
|||
this.objects = {};
|
||||
},
|
||||
|
||||
sync() {
|
||||
this.components.forEach(function(x) { x.sync?.(); });
|
||||
this.objects.forEach(function(x) { x.sync(); });
|
||||
},
|
||||
|
||||
delay(fn, seconds) {
|
||||
var timers = this.timers;
|
||||
var stop = function() {
|
||||
|
@ -97,60 +145,110 @@ var entity = {
|
|||
|
||||
cry(file) { return audio.cry(file); },
|
||||
|
||||
get pos() { return this.transform.pos; },
|
||||
set pos(x) { this.transform.pos = x; },
|
||||
get angle() { return this.transform.angle; },
|
||||
set angle(x) { this.transform.angle = x; },
|
||||
get scale() { return this.transform.scale; },
|
||||
set scale(x) { this.transform.scale = x; },
|
||||
set pos(x) { this.set_pos(x); },
|
||||
get pos() { return this.rpos; },
|
||||
set angle(x) { this.set_angle(x); },
|
||||
get angle() { return this.rangle; },
|
||||
set scale(x) { this.set_scale(x); },
|
||||
get scale() { return this.rscale; },
|
||||
|
||||
move(vec) { this.pos = this.pos.add(vec); },
|
||||
rotate(x) { this.transform.rotate(x, [0,0,-1]); },
|
||||
set_pos(x, relative = world) {
|
||||
var newpos = relative.this2world(x);
|
||||
var move = newpos.sub(this.pos);
|
||||
this.rpos = newpos;
|
||||
this.objects.forEach(x => x.move(move));
|
||||
},
|
||||
|
||||
set_angle(x, relative = world) {
|
||||
var newangle = relative.angle + x;
|
||||
var diff = newangle - this.angle;
|
||||
this.rangle = newangle;
|
||||
this.objects.forEach(obj => {
|
||||
obj.rotate(diff);
|
||||
obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master);
|
||||
});
|
||||
},
|
||||
|
||||
set_scale(x, relative = world) {
|
||||
if (typeof x === 'number') x = [x,x,x];
|
||||
var newscale = relative.scale.map((s,i) => x[i]*s);
|
||||
var pct = this.scale.map((s,i) => newscale[i]/s);
|
||||
this.rscale = newscale;
|
||||
this.objects.forEach(obj => {
|
||||
obj.grow(pct);
|
||||
obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master);
|
||||
});
|
||||
},
|
||||
|
||||
get_pos(relative = world) {
|
||||
if (relative === world) return this.pos;
|
||||
return relative.world2this(this.pos);
|
||||
//return this.pos.sub(relative.pos);
|
||||
},
|
||||
|
||||
get_angle(relative = world) {
|
||||
if (relative === world) return this.angle;
|
||||
return this.angle - relative.angle;
|
||||
},
|
||||
|
||||
get_scale(relative = world) {
|
||||
if (relative === world) return this.scale;
|
||||
var masterscale = relative.scale;
|
||||
return this.scale.map((x,i) => x/masterscale[i]);
|
||||
},
|
||||
|
||||
/* Moving, rotating, scaling functions, world relative */
|
||||
move(vec) { this.set_pos(this.pos.add(vec)); },
|
||||
rotate(x) { this.set_angle(this.angle + x); },
|
||||
grow(vec) {
|
||||
if (typeof vec === 'number') vec = [vec,vec];
|
||||
this.scale = this.scale.map((x,i) => x*vec[i]);
|
||||
if (typeof vec === 'number') vec = [vec,vec,vec];
|
||||
this.set_scale(this.scale.map((x, i) => x * vec[i]));
|
||||
},
|
||||
|
||||
/* Reparent 'this' to be 'parent's child */
|
||||
reparent(parent) {
|
||||
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
|
||||
console.spam(`parenting ${this.toString()} to ${parent.toString()}`);
|
||||
if (this.master === parent) {
|
||||
console.warn("not reparenting ...");
|
||||
console.warn(`${this.master} is the same as ${parent}`);
|
||||
return;
|
||||
}
|
||||
screenpos() { return game.camera.world2view(this.pos); },
|
||||
|
||||
var name = unique_name(Object.keys(parent), this.name);
|
||||
this.name = name;
|
||||
|
||||
this.master?.remove_obj(this);
|
||||
this.master = parent;
|
||||
parent.objects[this.guid] = this;
|
||||
parent[name] = this;
|
||||
Object.hide(parent, name);
|
||||
},
|
||||
|
||||
remove_obj(obj) {
|
||||
delete this.objects[obj.guid];
|
||||
delete this[obj.name];
|
||||
Object.unhide(this, obj.name);
|
||||
},
|
||||
get_ur() { return this.ur; },
|
||||
|
||||
/* spawn an entity
|
||||
text can be:
|
||||
the file path of a script
|
||||
an ur object
|
||||
nothing
|
||||
*/
|
||||
spawn(text, config, callback) {
|
||||
var st = profile.now();
|
||||
var ent = Object.create(entity);
|
||||
ent.transform = os.make_transform();
|
||||
|
||||
var ent = os.make_gameobject();
|
||||
ent.guid = prosperon.guid();
|
||||
|
||||
ent.components = {};
|
||||
ent.objects = {};
|
||||
ent.timers = [];
|
||||
|
||||
if (!text)
|
||||
ent.ur = emptyur;
|
||||
else if (typeof text === 'object' && text) {// assume it's an ur
|
||||
Object.mixin(ent, {
|
||||
set category(n) {
|
||||
if (n === 0) {
|
||||
this.categories = n;
|
||||
return;
|
||||
}
|
||||
var cat = (1 << (n-1));
|
||||
this.categories = cat;
|
||||
},
|
||||
get category() {
|
||||
if (this.categories === 0) return 0;
|
||||
var pos = 0;
|
||||
var num = this.categories;
|
||||
while (num > 0) {
|
||||
if (num & 1) {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
num >>>= 1;
|
||||
}
|
||||
|
||||
return pos+1;
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof text === 'object' && text) {// assume it's an ur
|
||||
ent.ur = text;
|
||||
text = ent.ur.text;
|
||||
config = [ent.ur.data, config].filter(x => x).flat();
|
||||
|
@ -183,8 +281,9 @@ var entity = {
|
|||
for (var [prop, p] of Object.entries(ent)) {
|
||||
if (!p) continue;
|
||||
if (typeof p !== 'object') continue;
|
||||
if (component.isComponent(p)) continue;
|
||||
if (!p.comp) continue;
|
||||
ent[prop] = component[p.comp](ent);
|
||||
ent[prop] = component[p.comp].make(ent);
|
||||
Object.merge(ent[prop], p);
|
||||
ent.components[prop] = ent[prop];
|
||||
};
|
||||
|
@ -195,7 +294,7 @@ var entity = {
|
|||
if (sim.playing())
|
||||
if (typeof ent.start === 'function') ent.start();
|
||||
|
||||
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master');
|
||||
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master', 'categories');
|
||||
|
||||
ent._ed = {
|
||||
selectable: true,
|
||||
|
@ -231,21 +330,63 @@ var entity = {
|
|||
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
|
||||
|
||||
profile.addreport(entityreport, ent.ur.name, st);
|
||||
|
||||
return ent;
|
||||
},
|
||||
|
||||
disable() { this.components.forEach(function(x) { x.disable(); }); },
|
||||
enable() { this.components.forEach(function(x) { x.enable(); }); },
|
||||
/* Reparent 'this' to be 'parent's child */
|
||||
reparent(parent) {
|
||||
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
|
||||
console.spam(`parenting ${this.toString()} to ${parent.toString()}`);
|
||||
if (this.master === parent) {
|
||||
console.warn("not reparenting ...");
|
||||
console.warn(`${this.master} is the same as ${parent}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.master?.remove_obj(this);
|
||||
|
||||
this.master = parent;
|
||||
|
||||
function unique_name(list, name = "new_object") {
|
||||
var str = name.replaceAll('.', '_');
|
||||
var n = 1;
|
||||
var t = str;
|
||||
while (list.indexOf(t) !== -1) {
|
||||
t = str + n;
|
||||
n++;
|
||||
}
|
||||
return t;
|
||||
};
|
||||
|
||||
var name = unique_name(Object.keys(parent.objects), this.ur.name);
|
||||
|
||||
parent.objects[name] = this;
|
||||
parent[name] = this;
|
||||
Object.hide(parent, name);
|
||||
this.toString = function() { return name; };
|
||||
},
|
||||
|
||||
remove_obj(obj) {
|
||||
delete this.objects[obj.toString()];
|
||||
delete this[obj.toString()];
|
||||
Object.unhide(this, obj.toString());
|
||||
},
|
||||
|
||||
components: {},
|
||||
objects: {},
|
||||
master: undefined,
|
||||
|
||||
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
|
||||
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
|
||||
|
||||
/* Make a unique object the same as its prototype */
|
||||
revert() { Object.merge(this, this.ur.fresh); },
|
||||
in_air() { return this.in_air(); },
|
||||
|
||||
hide() { this.components.forEach(x => x.hide?.());
|
||||
this.objects.forEach(x => x.hide?.()); },
|
||||
|
||||
show() { this.components.forEach(function(x) { x.show?.(); });
|
||||
this.objects.forEach(function(x) { x.show?.(); }); },
|
||||
|
||||
name: "new_object",
|
||||
toString() { return this.name; },
|
||||
width() {
|
||||
var bb = this.boundingbox();
|
||||
return bb.r - bb.l;
|
||||
|
@ -256,11 +397,19 @@ var entity = {
|
|||
return bb.t - bb.b;
|
||||
},
|
||||
|
||||
/* Make a unique object the same as its prototype */
|
||||
revert() { Object.merge(this, this.ur.fresh); },
|
||||
|
||||
toString() { return "new_object"; },
|
||||
|
||||
flipx() { return this.scale.x < 0; },
|
||||
flipy() { return this.scale.y < 0; },
|
||||
|
||||
mirror(plane) { this.scale = Vector.reflect(this.scale, plane); },
|
||||
|
||||
disable() { this.components.forEach(function(x) { x.disable(); }); },
|
||||
enable() { this.components.forEach(function(x) { x.enable(); }); },
|
||||
|
||||
/* Bounding box of the object in world dimensions */
|
||||
boundingbox() {
|
||||
var boxes = [];
|
||||
|
@ -327,7 +476,22 @@ var entity = {
|
|||
return t;
|
||||
},
|
||||
|
||||
dup(diff) {
|
||||
/* Velocity and angular velocity of the object */
|
||||
phys_obj() {
|
||||
var phys = {};
|
||||
phys.velocity = this.velocity;
|
||||
phys.angularvelocity = this.angularvelocity;
|
||||
return phys;
|
||||
},
|
||||
|
||||
phys_mat() {
|
||||
return {
|
||||
friction: this.friction,
|
||||
elasticity: this.elasticity
|
||||
}
|
||||
},
|
||||
|
||||
dup(diff) {
|
||||
var n = this.master.spawn(this.ur);
|
||||
Object.totalmerge(n, this.transform());
|
||||
return n;
|
||||
|
@ -351,8 +515,9 @@ dup(diff) {
|
|||
for (var key in this.components) {
|
||||
this.components[key].kill?.();
|
||||
this.components[key].gameobject = undefined;
|
||||
this.components[key].enabled = false;
|
||||
this[key].enabled = false;
|
||||
delete this.components[key];
|
||||
delete this[key];
|
||||
}
|
||||
delete this.components;
|
||||
|
||||
|
@ -367,6 +532,10 @@ dup(diff) {
|
|||
}
|
||||
},
|
||||
|
||||
up() { return [0, 1].rotate(this.angle); },
|
||||
down() { return [0, -1].rotate(this.angle); },
|
||||
right() { return [1, 0].rotate(this.angle); },
|
||||
left() { return [-1, 0].rotate(this.angle); },
|
||||
|
||||
make_objs(objs) {
|
||||
for (var prop in objs) {
|
||||
|
@ -396,168 +565,29 @@ dup(diff) {
|
|||
return this.objects[newname];
|
||||
},
|
||||
|
||||
add_component(comp, data) {
|
||||
var name = prosperon.guid();
|
||||
this.components[name] = comp(this);
|
||||
if (data) {
|
||||
Object.assign(this.components[name], data);
|
||||
this.components[name].sync?.();
|
||||
}
|
||||
return this.components[name];
|
||||
add_component(comp, data, name = comp.toString()) {
|
||||
if (typeof comp.make !== 'function') return;
|
||||
name = obj_unique_name(name, this);
|
||||
this[name] = comp.make(this);
|
||||
this[name].comp = comp.toString();
|
||||
this.components[name] = this[name];
|
||||
if (data)
|
||||
Object.assign(this[name], data);
|
||||
return this[name];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
var gameobject = {
|
||||
check_dirty() {
|
||||
this._ed.urdiff = this.json_obj();
|
||||
this._ed.dirty = !Object.empty(this._ed.urdiff);
|
||||
return; // TODO: IMPLEMENT
|
||||
var lur = this.master.ur;
|
||||
if (!lur) return;
|
||||
var lur = lur.objects[this.toString()];
|
||||
var d = ediff(this._ed.urdiff, lur);
|
||||
if (!d || Object.empty(d))
|
||||
this._ed.inst = true;
|
||||
else
|
||||
this._ed.inst = false;
|
||||
},
|
||||
|
||||
namestr() {
|
||||
var s = this.toString();
|
||||
if (this._ed?.dirty)
|
||||
if (this._ed.inst) s += "#";
|
||||
else s += "*";
|
||||
return s;
|
||||
},
|
||||
|
||||
urstr() {
|
||||
var str = this.ur.name;
|
||||
if (this._ed.dirty) str = "*" + str;
|
||||
return str;
|
||||
},
|
||||
|
||||
/* pin this object to the to object */
|
||||
pin(to) {
|
||||
var p = joint.pin(this,to);
|
||||
},
|
||||
slide(to, a = [0,0], b = [0,0], min = 0, max = 50) {
|
||||
var p = joint.slide(this, to, a, b, min, max);
|
||||
p.max_force = 500;
|
||||
p.break();
|
||||
},
|
||||
pivot(to, piv = this.pos) {
|
||||
var p = joint.pivot(this, to, piv);
|
||||
},
|
||||
/* groove is on to, from local points a and b, anchored to this at local anchor */
|
||||
groove(to, a, b, anchor = [0,0]) {
|
||||
var p = joint.groove(to, this, a, b, anchor);
|
||||
},
|
||||
damped_spring(to, length = Vector.length(this.pos,to.pos), stiffness = 1, damping = 1) {
|
||||
var dc = 2 * Math.sqrt(stiffness * this.mass);
|
||||
var p = joint.damped_spring(this, to, [0, 0], [0, 0], stiffness, damping * dc);
|
||||
},
|
||||
damped_rotary_spring(to, angle = 0, stiffness = 1, damping = 1) {
|
||||
/* calculate actual damping value from the damping ratio */
|
||||
/* damping = 1 is critical */
|
||||
var dc = 2 * Math.sqrt(stiffness * this.get_moi()); /* critical damping number */
|
||||
/* zeta = actual/critical */
|
||||
var p = joint.damped_rotary(this, to, angle, stiffness, damping * dc);
|
||||
},
|
||||
rotary_limit(to, min, max) {
|
||||
var p = joint.rotary(this, to, Math.turn2rad(min), Math.turn2rad(max));
|
||||
},
|
||||
ratchet(to, ratch) {
|
||||
var phase = this.angle - to.angle;
|
||||
var p = joint.ratchet(this, to, phase, Math.turn2rad(ratch));
|
||||
},
|
||||
gear(to, ratio = 1, phase = 0) {
|
||||
var phase = this.angle - to.angle;
|
||||
var p = joint.gear(this, to, phase, ratio);
|
||||
},
|
||||
motor(to, rate) {
|
||||
var p = joint.motor(this, to, rate);
|
||||
},
|
||||
|
||||
set_pos(x, relative = world) {
|
||||
var newpos = relative.this2world(x);
|
||||
var move = newpos.sub(this.pos);
|
||||
this.rpos = newpos;
|
||||
this.objects.forEach(x => x.move(move));
|
||||
},
|
||||
|
||||
set_angle(x, relative = world) {
|
||||
var newangle = relative.angle + x;
|
||||
var diff = newangle - this.angle;
|
||||
this.rangle = newangle;
|
||||
this.objects.forEach(obj => {
|
||||
obj.rotate(diff);
|
||||
obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master);
|
||||
});
|
||||
},
|
||||
|
||||
set_scale(x, relative = world) {
|
||||
if (typeof x === 'number') x = [x,x,x];
|
||||
var newscale = relative.scale.map((s,i) => x[i]*s);
|
||||
var pct = this.scale.map((s,i) => newscale[i]/s);
|
||||
this.rscale = newscale;
|
||||
this.objects.forEach(obj => {
|
||||
obj.grow(pct);
|
||||
obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master);
|
||||
});
|
||||
},
|
||||
|
||||
get_pos(relative = world) {
|
||||
if (relative === world) return this.pos;
|
||||
return relative.world2this(this.pos);
|
||||
//return this.pos.sub(relative.pos);
|
||||
},
|
||||
|
||||
get_angle(relative = world) {
|
||||
if (relative === world) return this.angle;
|
||||
return this.angle - relative.angle;
|
||||
},
|
||||
|
||||
get_scale(relative = world) {
|
||||
if (relative === world) return this.scale;
|
||||
var masterscale = relative.scale;
|
||||
return this.scale.map((x,i) => x/masterscale[i]);
|
||||
},
|
||||
|
||||
in_air() { return this.in_air(); },
|
||||
|
||||
/* Velocity and angular velocity of the object */
|
||||
phys_obj() {
|
||||
var phys = {};
|
||||
phys.velocity = this.velocity;
|
||||
phys.angularvelocity = this.angularvelocity;
|
||||
return phys;
|
||||
},
|
||||
|
||||
set category(n) {
|
||||
if (n === 0) {
|
||||
this.categories = n;
|
||||
return;
|
||||
}
|
||||
var cat = (1 << (n-1));
|
||||
this.categories = cat;
|
||||
},
|
||||
get category() {
|
||||
if (this.categories === 0) return 0;
|
||||
var pos = 0;
|
||||
var num = this.categories;
|
||||
while (num > 0) {
|
||||
if (num & 1) {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
num >>>= 1;
|
||||
}
|
||||
|
||||
return pos+1;
|
||||
function go_init() {
|
||||
var gop = os.make_gameobject().__proto__;
|
||||
Object.mixin(gop, gameobject);
|
||||
gop.sync = function() {
|
||||
this.selfsync();
|
||||
this.components.forEach(function(x) { x.sync?.(); });
|
||||
this.objects.forEach(function(x) { x.sync?.(); });
|
||||
}
|
||||
}
|
||||
|
||||
entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
|
||||
gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
|
||||
|
||||
gameobject.doc = {
|
||||
doc: "All objects in the game created through spawning have these attributes.",
|
||||
|
@ -665,18 +695,8 @@ function apply_ur(u, ent) {
|
|||
}
|
||||
}
|
||||
|
||||
var emptyur = {
|
||||
name: "empty"
|
||||
}
|
||||
|
||||
var getur = function(text, data)
|
||||
{
|
||||
if (!text && !data) {
|
||||
console.info('empty ur');
|
||||
return {
|
||||
name: "empty"
|
||||
};
|
||||
}
|
||||
var urstr = text + "+" + data;
|
||||
if (!ur[urstr]) {
|
||||
ur[urstr] = {
|
||||
|
@ -727,7 +747,6 @@ game.loadurs = function() {
|
|||
}
|
||||
}
|
||||
|
||||
return;
|
||||
for (var file of io.glob("**.json").filter(f => !ur[f.name()])) {
|
||||
if (file[0] === '.' || file[0] === '_') continue;
|
||||
var newur = ur_from_file(file);
|
||||
|
@ -765,4 +784,4 @@ game.ur.save = function(str)
|
|||
}
|
||||
}
|
||||
|
||||
return { entity }
|
||||
return { go_init }
|
|
@ -1,9 +1,4 @@
|
|||
var shape = {};
|
||||
shape.box = {};
|
||||
shape.box.points = function(ll, ur)
|
||||
{
|
||||
return [ll, ll.add([ur.x-ll.x,0]), ur, ll.add([0,ur.y-ll.y])];
|
||||
}
|
||||
shape.sphere = {};
|
||||
shape.circle = {};
|
||||
shape.sphere.volume = function(r) { return Math.pi*r*r*r*4/3; };
|
||||
|
|
|
@ -164,16 +164,6 @@ Mum.button = Mum.text._int.extend({
|
|||
action() { console.warn("Button has no action."); },
|
||||
});
|
||||
|
||||
var mumcam = {};
|
||||
mumcam.transform = os.make_transform();
|
||||
mumcam.ortho = true;
|
||||
mumcam.near = 0;
|
||||
mumcam.far = 1000;
|
||||
mumcam.transform.pos = [100,100,-100];
|
||||
mumcam.app = true;
|
||||
|
||||
var textssbo = render.text_ssbo();
|
||||
|
||||
Mum.window = Mum.extend({
|
||||
start() {
|
||||
this.wh = [this.width, this.height];
|
||||
|
@ -183,6 +173,7 @@ Mum.window = Mum.extend({
|
|||
var p = cursor.sub(this.wh.scale(this.anchor)).add(this.padding);
|
||||
render.window(p,this.wh, this.color);
|
||||
this.bb = bbox.blwh(p, this.wh);
|
||||
gui.flush();
|
||||
this.max_width = this.width;
|
||||
if (this.selectable) gui.controls.check_bb(this);
|
||||
var pos = [this.bb.l, this.bb.t].add(this.padding);
|
||||
|
@ -190,12 +181,7 @@ Mum.window = Mum.extend({
|
|||
if (item.hide) return;
|
||||
item.draw(pos.slice(),this);
|
||||
}, this);
|
||||
render.set_camera(mumcam);
|
||||
render.setpipeline(render.textshader.pipe);
|
||||
render.shader_apply_material(render.textshader);
|
||||
var bind = render.sg_bind(render.textshader, shape.quad, {text:render.font.texture}, textssbo);
|
||||
bind.inst = render.flushtext();
|
||||
render.spdraw(bind);
|
||||
gui.flush();
|
||||
gui.scissor_win();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -119,7 +119,6 @@ prosperon.textinput = function(c){
|
|||
};
|
||||
prosperon.mousemove = function(pos, dx){
|
||||
mousepos = pos;
|
||||
mousepos.y = window.size.y - mousepos.y;
|
||||
player[0].mouse_input("move", pos, dx);
|
||||
};
|
||||
prosperon.mousescroll = function(dx){
|
||||
|
@ -300,20 +299,15 @@ var Player = {
|
|||
|
||||
raw_input(cmd, state, ...args) {
|
||||
for (var pawn of this.pawns.reversed()) {
|
||||
if (!pawn.inputs) {
|
||||
console.error(`pawn no longer has inputs object.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof pawn.inputs.any === 'function') {
|
||||
if (typeof pawn.inputs?.any === 'function') {
|
||||
pawn.inputs.any(cmd);
|
||||
|
||||
if (!pawn.inputs.fallthru)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pawn.inputs[cmd]) {
|
||||
if (pawn.inputs.block) return;
|
||||
if (!pawn.inputs?.[cmd]) {
|
||||
if (pawn.inputs?.block) return;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -335,7 +329,6 @@ var Player = {
|
|||
|
||||
if (typeof fn === 'function') {
|
||||
fn.call(pawn, ... args);
|
||||
if (!pawn.inputs) continue; // TODO: OK? Checking if the call uncontrolled the pawn
|
||||
pawn.inputs.post?.call(pawn);
|
||||
}
|
||||
|
||||
|
@ -376,10 +369,6 @@ var Player = {
|
|||
pawns: [],
|
||||
|
||||
control(pawn) {
|
||||
if (!pawn.inputs) {
|
||||
console.warn(`attempted to control a pawn without any input object.`);
|
||||
return;
|
||||
}
|
||||
this.pawns.push_unique(pawn);
|
||||
},
|
||||
|
||||
|
|
|
@ -53,8 +53,6 @@ physics.gravity.strength = 500;
|
|||
physics.damp = physics.make_damp();
|
||||
physics.damp.mask = ~1;
|
||||
|
||||
physics.delta = 1/240;
|
||||
|
||||
return {
|
||||
physics
|
||||
}
|
||||
|
|
|
@ -4,324 +4,6 @@ render.doc = {
|
|||
wireframe: "Show only wireframes of models."
|
||||
};
|
||||
|
||||
var shaderlang = {
|
||||
macos: "metal_macos",
|
||||
windows: "hlsl5",
|
||||
linux: "glsl430",
|
||||
web: "wgsl",
|
||||
ios: "metal_ios",
|
||||
}
|
||||
|
||||
var attr_map = {
|
||||
a_pos: 0,
|
||||
a_uv: 1,
|
||||
a_norm: 2,
|
||||
a_bone: 3,
|
||||
a_weight: 4,
|
||||
a_color: 5,
|
||||
a_tan: 6,
|
||||
a_angle: 7,
|
||||
a_wh: 8,
|
||||
a_st: 9,
|
||||
a_ppos: 10,
|
||||
a_scale: 11
|
||||
}
|
||||
|
||||
var blend_map = {
|
||||
mix: true,
|
||||
none: false
|
||||
}
|
||||
|
||||
var primitive_map = {
|
||||
point: 1,
|
||||
line: 2,
|
||||
linestrip: 3,
|
||||
triangle: 4,
|
||||
trianglestrip: 5
|
||||
}
|
||||
|
||||
var cull_map = {
|
||||
none: 1,
|
||||
front: 2,
|
||||
back: 3
|
||||
}
|
||||
|
||||
var depth_map = {
|
||||
off: false,
|
||||
on: true
|
||||
}
|
||||
|
||||
var face_map = {
|
||||
cw: 2,
|
||||
ccw: 1
|
||||
}
|
||||
|
||||
render.poly_prim = function(verts)
|
||||
{
|
||||
var index = [];
|
||||
if (verts.length < 1) return undefined;
|
||||
|
||||
for (var i = 0; i < verts.length; i++)
|
||||
verts[i][2] = 0;
|
||||
|
||||
for (var i = 2; i < verts.length; i++) {
|
||||
index.push(0);
|
||||
index.push(i-1);
|
||||
index.push(i);
|
||||
}
|
||||
|
||||
return {
|
||||
pos: os.make_buffer(verts.flat()),
|
||||
verts: verts.length,
|
||||
index: os.make_buffer(index, 1),
|
||||
count: index.length
|
||||
};
|
||||
}
|
||||
|
||||
function shader_directive(shader, name, map)
|
||||
{
|
||||
var reg = new RegExp(`#${name}.*`, 'g');
|
||||
var mat = shader.match(reg);
|
||||
if (!mat) return undefined;
|
||||
|
||||
reg = new RegExp(`#${name}\s*`, 'g');
|
||||
var ff = mat.map(d=>d.replace(reg,''))[0].trim();
|
||||
|
||||
if (map) return map[ff];
|
||||
return ff;
|
||||
}
|
||||
|
||||
function global_uni(uni, stage)
|
||||
{
|
||||
switch(uni.name) {
|
||||
case "time":
|
||||
render.setuniv(stage, uni.slot, profile.secs(profile.now()));
|
||||
return true;
|
||||
case "projection":
|
||||
render.setuniproj(stage, uni.slot);
|
||||
return true;
|
||||
case "view":
|
||||
render.setuniview(stage, uni.slot);
|
||||
return true;
|
||||
case "vp":
|
||||
render.setunivp(stage, uni.slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
render.make_shader = function(shader)
|
||||
{
|
||||
var file = shader;
|
||||
shader = io.slurp(shader);
|
||||
if (!shader) {
|
||||
console.info(`not found! slurping shaders/${file}`);
|
||||
shader = io.slurp(`shaders/${file}`);
|
||||
}
|
||||
var writejson = `.prosperon/${file.name()}.shader.json`;
|
||||
var st = profile.now();
|
||||
|
||||
breakme: if (io.exists(writejson)) {
|
||||
var data = json.decode(io.slurp(writejson));
|
||||
var filemod = io.mod(writejson);
|
||||
if (!data.files) break breakme;
|
||||
for (var i of data.files)
|
||||
if (io.mod(i) > filemod)
|
||||
break breakme;
|
||||
|
||||
profile.report(st, `CACHE make shader from ${file}`);
|
||||
var shaderobj = json.decode(io.slurp(writejson));
|
||||
var obj = shaderobj[os.sys()];
|
||||
obj.pipe = render.pipeline(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
var out = `.prosperon/${file.name()}.shader`;
|
||||
|
||||
var files = [file];
|
||||
|
||||
var incs = shader.match(/#include <.*>/g);
|
||||
if (incs)
|
||||
for (var inc of incs) {
|
||||
var filez = inc.match(/#include <(.*)>/)[1];
|
||||
var macro = io.slurp(filez);
|
||||
if (!macro) {
|
||||
filez = `shaders/${filez}`;
|
||||
macro = io.slurp(filez);
|
||||
}
|
||||
shader = shader.replace(inc, macro);
|
||||
files.push(filez);
|
||||
}
|
||||
|
||||
var blend = shader_directive(shader, 'blend', blend_map);
|
||||
var primitive = shader_directive(shader, 'primitive', primitive_map);
|
||||
var cull = shader_directive(shader, 'cull', cull_map);
|
||||
var depth = shader_directive(shader, 'depth', depth_map);
|
||||
var face = shader_directive(shader, 'face', face_map);
|
||||
var indexed = shader_directive(shader, 'indexed');
|
||||
|
||||
if (typeof indexed == 'undefined') indexed = true;
|
||||
if (indexed === 'false') indexed = false;
|
||||
|
||||
shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };");
|
||||
shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 ");
|
||||
// shader = shader.replace(/uniform texture2D ?(.*);/g, "uniform _$1_size { vec2 $1_size; };\nuniform texture2D $1;");
|
||||
|
||||
io.slurpwrite(out, shader);
|
||||
|
||||
var compiled = {};
|
||||
|
||||
// shader file is created, now cross compile to all targets
|
||||
for (var platform in shaderlang) {
|
||||
var backend = shaderlang[platform];
|
||||
var ret = os.system(`sokol-shdc -f bare_yaml --slang=${backend} -i ${out} -o ${out}`);
|
||||
if (ret) {
|
||||
console.error(`error compiling shader ${file}. No compilation found for ${platform}:${backend}, and no cross compiler available.`);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Take YAML and create the shader object */
|
||||
var yamlfile = `${out}_reflection.yaml`;
|
||||
var jjson = yaml.tojson(io.slurp(yamlfile));
|
||||
var obj = json.decode(jjson);
|
||||
io.rm(yamlfile);
|
||||
|
||||
obj = obj.shaders[0].programs[0];
|
||||
function add_code(stage) {
|
||||
stage.code = io.slurp(stage.path);
|
||||
|
||||
io.rm(stage.path);
|
||||
delete stage.path;
|
||||
}
|
||||
|
||||
add_code(obj.vs);
|
||||
if (!obj.fs)
|
||||
if (obj.vs.fs) {
|
||||
obj.fs = obj.vs.fs;
|
||||
delete obj.vs.fs;
|
||||
}
|
||||
add_code(obj.fs);
|
||||
|
||||
obj.blend = blend;
|
||||
obj.cull = cull;
|
||||
obj.primitive = primitive;
|
||||
obj.depth = depth;
|
||||
obj.face = face;
|
||||
obj.indexed = indexed;
|
||||
|
||||
if (obj.vs.inputs)
|
||||
for (var i of obj.vs.inputs) {
|
||||
if (!(i.name in attr_map))
|
||||
i.mat = -1;
|
||||
else
|
||||
i.mat = attr_map[i.name];
|
||||
}
|
||||
|
||||
function make_unimap(stage) {
|
||||
if (!stage.uniform_blocks) return {};
|
||||
var unimap = {};
|
||||
for (var uni of stage.uniform_blocks) {
|
||||
var uniname = uni.struct_name[0] == "_" ? uni.struct_name.slice(1) : uni.struct_name;
|
||||
|
||||
unimap[uniname] = {
|
||||
name: uniname,
|
||||
slot: Number(uni.slot),
|
||||
size: Number(uni.size)
|
||||
};
|
||||
}
|
||||
|
||||
return unimap;
|
||||
}
|
||||
|
||||
obj.vs.unimap = make_unimap(obj.vs);
|
||||
obj.fs.unimap = make_unimap(obj.fs);
|
||||
|
||||
obj.name = file;
|
||||
|
||||
compiled[platform] = obj;
|
||||
}
|
||||
|
||||
compiled.files = files;
|
||||
|
||||
io.slurpwrite(writejson, json.encode(compiled));
|
||||
profile.report(st, `make shader from ${file}`);
|
||||
|
||||
var obj = compiled[os.sys()];
|
||||
obj.pipe = render.pipeline(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
var shader_unisize = {
|
||||
4: render.setuniv,
|
||||
8: render.setuniv2,
|
||||
12: render.setuniv3,
|
||||
16: render.setuniv4
|
||||
};
|
||||
|
||||
render.shader_apply_material = function(shader, material = {})
|
||||
{
|
||||
for (var p in shader.vs.unimap) {
|
||||
if (global_uni(shader.vs.unimap[p], 0)) continue;
|
||||
if (!(p in material)) continue;
|
||||
var s = shader.vs.unimap[p];
|
||||
shader_unisize[s.size](0, s.slot, material[p]);
|
||||
}
|
||||
|
||||
for (var p in shader.fs.unimap) {
|
||||
if (global_uni(shader.fs.unimap[p], 1)) continue;
|
||||
if (!(p in material)) continue;
|
||||
var s = shader.fs.unimap[p];
|
||||
shader_unisize[s.size](1, s.slot, material[p]);
|
||||
}
|
||||
if (!material.diffuse) return;
|
||||
|
||||
if ("diffuse_size" in shader.fs.unimap)
|
||||
render.setuniv2(1, shader.fs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
|
||||
|
||||
if ("diffuse_size" in shader.vs.unimap)
|
||||
render.setuniv2(0, shader.vs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
|
||||
}
|
||||
|
||||
render.sg_bind = function(shader, mesh = {}, material = {}, ssbo)
|
||||
{
|
||||
var bind = {};
|
||||
bind.attrib = [];
|
||||
if (shader.vs.inputs)
|
||||
for (var a of shader.vs.inputs) {
|
||||
if (!(a.name in mesh)) {
|
||||
if (!(a.name.slice(2) in mesh)) {
|
||||
console.error(`cannot draw shader ${shader.name}; there is no attrib ${a.name} in the given mesh.`);
|
||||
return undefined;
|
||||
} else
|
||||
bind.attrib.push(mesh[a.name.slice(2)]);
|
||||
} else
|
||||
bind.attrib.push(mesh[a.name]);
|
||||
}
|
||||
bind.images = [];
|
||||
if (shader.fs.images)
|
||||
for (var img of shader.fs.images) {
|
||||
if (material[img.name])
|
||||
bind.images.push(material[img.name]);
|
||||
else
|
||||
bind.images.push(game.texture("icons/no_tex.gif"));
|
||||
}
|
||||
|
||||
if (shader.indexed) {
|
||||
bind.index = mesh.index;
|
||||
bind.count = mesh.count;
|
||||
} else
|
||||
bind.count = mesh.verts;
|
||||
|
||||
bind.ssbo = [];
|
||||
if (shader.vs.storage_buffers)
|
||||
for (var b of shader.vs.storage_buffers)
|
||||
bind.ssbo.push(ssbo);
|
||||
|
||||
return bind;
|
||||
}
|
||||
|
||||
render.device = {
|
||||
pc: [1920,1080],
|
||||
macbook_m2: [2560,1664, 13.6],
|
||||
|
@ -358,91 +40,16 @@ render.device = {
|
|||
|
||||
render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
|
||||
|
||||
var textshader;
|
||||
var circleshader;
|
||||
var polyshader;
|
||||
|
||||
render.init = function() {
|
||||
textshader = render.make_shader("shaders/text_base.cg");
|
||||
render.spriteshader = render.make_shader("shaders/sprite.cg");
|
||||
render.postshader = render.make_shader("shaders/simplepost.cg");
|
||||
circleshader = render.make_shader("shaders/circle.cg");
|
||||
polyshader = render.make_shader("shaders/poly.cg");
|
||||
|
||||
render.textshader = textshader;
|
||||
|
||||
os.make_circle2d().draw = function() {
|
||||
render.circle(this.body().transform().pos, this.radius, [1,1,0,1]);
|
||||
}
|
||||
|
||||
var disabled = [148/255,148/255, 148/255, 1];
|
||||
var sleep = [1, 140/255, 228/255, 1];
|
||||
var dynamic = [1, 70/255, 46/255, 1];
|
||||
var kinematic = [1, 194/255, 64/255, 1];
|
||||
var static_color = [73/255, 209/255, 80/255, 1];
|
||||
|
||||
os.make_poly2d().draw = function() {
|
||||
var body = this.body();
|
||||
var color = body.sleeping() ? [0,0.3,0,0.4] : [0,1,0,0.4];
|
||||
var t = body.transform();
|
||||
render.poly(this.points, color, body.transform());
|
||||
color.a = 1;
|
||||
render.line(this.points.wrapped(1), color, 1, body.transform());
|
||||
}
|
||||
|
||||
os.make_seg2d().draw = function() {
|
||||
render.line([this.a(), this.b()], [1,0,1,1], Math.max(this.radius/2, 1), this.body().transform());
|
||||
}
|
||||
|
||||
joint.pin().draw = function() {
|
||||
var a = this.bodyA();
|
||||
var b = this.bodyB();
|
||||
render.line([a.transform().pos.xy, b.transform().pos.xy], [0,1,1,1], 1);
|
||||
}
|
||||
}
|
||||
|
||||
render.circle = function(pos, radius, color) {
|
||||
var mat = {
|
||||
radius: radius,
|
||||
coord: pos,
|
||||
shade: color
|
||||
};
|
||||
render.setpipeline(circleshader.pipe);
|
||||
render.shader_apply_material(circleshader, mat);
|
||||
var bind = render.sg_bind(circleshader, shape.quad, mat);
|
||||
bind.inst = 1;
|
||||
render.spdraw(bind);
|
||||
}
|
||||
|
||||
render.poly = function(points, color, transform) {
|
||||
var buffer = render.poly_prim(points);
|
||||
var mat = { shade: color};
|
||||
render.setpipeline(polyshader.pipe);
|
||||
render.setunim4(0,polyshader.vs.unimap.model.slot, transform);
|
||||
render.shader_apply_material(polyshader, mat);
|
||||
var bind = render.sg_bind(polyshader, buffer, mat);
|
||||
bind.inst = 1;
|
||||
render.spdraw(bind);
|
||||
}
|
||||
|
||||
render.line = function(points, color = Color.white, thickness = 1, transform) {
|
||||
var buffer = os.make_line_prim(points, thickness, 0, false);
|
||||
render.setpipeline(polyshader.pipe);
|
||||
var mat = {
|
||||
shade: color
|
||||
};
|
||||
render.shader_apply_material(polyshader, mat);
|
||||
render.setunim4(0,polyshader.vs.unimap.model.slot, transform);
|
||||
var bind = render.sg_bind(polyshader, buffer, mat);
|
||||
bind.inst = 1;
|
||||
render.spdraw(bind);
|
||||
}
|
||||
|
||||
/* All draw in screen space */
|
||||
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.cross = function(pos, size, color = Color.red) {
|
||||
var a = [
|
||||
pos.add([0,size]),
|
||||
|
@ -452,9 +59,10 @@ render.cross = function(pos, size, color = Color.red) {
|
|||
pos.add([size,0]),
|
||||
pos.add([-size,0])
|
||||
];
|
||||
|
||||
render.line(a,color);
|
||||
render.line(b,color);
|
||||
};
|
||||
};
|
||||
|
||||
render.arrow = function(start, end, color = Color.red, wingspan = 4, wingangle = 10) {
|
||||
var dir = end.sub(start).normalized();
|
||||
|
@ -515,19 +123,10 @@ render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, ancho
|
|||
return bb;
|
||||
};
|
||||
|
||||
render.image = function(tex, pos, scale = 1, rotation = 0, color = Color.white, dimensions = [tex.width, tex.height]) {
|
||||
var t = os.make_transform();
|
||||
t.pos = pos;
|
||||
t.scale = [scale,scale,scale];
|
||||
render.setpipeline(render.spriteshader.pipe);
|
||||
render.setunim4(0, render.spriteshader.vs.unimap.model.slot, t);
|
||||
render.shader_apply_material(render.spriteshader, {
|
||||
shade: color,
|
||||
diffuse: tex
|
||||
});
|
||||
var bind = render.sg_bind(render.spriteshader, shape.quad, {diffuse:tex});
|
||||
bind.inst = 1;
|
||||
render.spdraw(bind);
|
||||
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 = {};
|
||||
|
@ -540,10 +139,12 @@ render.set_font = function(path, size) {
|
|||
}
|
||||
|
||||
render.doc = "Draw shapes in screen space.";
|
||||
//render.circle.doc = "Draw a circle at pos, with a given radius and color.";
|
||||
render.circle.doc = "Draw a circle at pos, with a given radius and color.";
|
||||
render.cross.doc = "Draw a cross centered at pos, with arm length size.";
|
||||
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
|
||||
render.poly.doc = "Draw a concave polygon from a set of points.";
|
||||
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
|
||||
|
||||
render.box.doc = "Draw a box centered at pos, with width and height in the tuple wh.";
|
||||
render.line.doc = "Draw a line from a set of points, and a given thickness.";
|
||||
|
||||
return {render};
|
||||
|
|
|
@ -192,7 +192,7 @@ Cmdline.register_order("edit", function() {
|
|||
}
|
||||
|
||||
window.size = [1280, 720];
|
||||
window.mode = "full";
|
||||
window.mode = window.modetypes.full;
|
||||
sim.pause();
|
||||
|
||||
game.engine_start(function() {
|
||||
|
@ -243,9 +243,13 @@ Cmdline.register_order("play", function(argv) {
|
|||
|
||||
var project = json.decode(io.slurp(projectfile));
|
||||
game.title = project.title;
|
||||
window.mode = window.modetypes.expand;
|
||||
global.mixin("config.js");
|
||||
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}.`);
|
||||
|
||||
game.engine_start(function() {
|
||||
render.set_font("fonts/c64.ttf", 8);
|
||||
global.app = actor.spawn("game.js");
|
||||
|
@ -451,8 +455,8 @@ function cmd_args(cmdargs)
|
|||
if (cmds.length === 0)
|
||||
cmds[0] = "play";
|
||||
else if (!Cmdline.orders[cmds[0]]) {
|
||||
console.warn(`Command ${cmds[0]} not found. Playing instead.`);
|
||||
cmds[0] = "play";
|
||||
console.warn(`Command ${cmds[0]} not found. Playing instead.`);
|
||||
}
|
||||
|
||||
Cmdline.orders[cmds[0]](cmds.slice(1));
|
||||
|
@ -472,54 +476,9 @@ Cmdline.register_cmd("l", function(n) {
|
|||
console.level = n;
|
||||
}, "Set log level.");
|
||||
|
||||
function convertYAMLtoJSON(yamlString) {
|
||||
const lines = yamlString.split('\n');
|
||||
const jsonObj = {};
|
||||
|
||||
let currentKey = '';
|
||||
let currentValue = '';
|
||||
let currentDepth = 0;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (!line || line.startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const depth = (line.match(/^\s+/g) || [''])[0].length;
|
||||
const keyValue = line.split(':');
|
||||
const key = keyValue[0].trim();
|
||||
const value = keyValue[1].trim();
|
||||
|
||||
if (depth > currentDepth) {
|
||||
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
|
||||
currentKey = key;
|
||||
currentValue = value;
|
||||
} else if (depth === currentDepth) {
|
||||
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
|
||||
currentKey = key;
|
||||
currentValue = value;
|
||||
} else {
|
||||
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
|
||||
currentKey = '';
|
||||
currentValue = '';
|
||||
i--; // To reprocess the current line with updated values
|
||||
}
|
||||
|
||||
currentDepth = depth;
|
||||
}
|
||||
|
||||
if (currentKey) {
|
||||
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
|
||||
}
|
||||
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
return {
|
||||
Resources,
|
||||
Cmdline,
|
||||
cmd_args,
|
||||
convertYAMLtoJSON
|
||||
cmd_args
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
var inputpanel = {
|
||||
title: "untitled",
|
||||
toString() { return this.title; },
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
#blend mix
|
||||
#primitive triangle
|
||||
#cull none
|
||||
#depth off
|
||||
|
||||
@vs vs
|
||||
in vec3 a_pos;
|
||||
in vec2 a_uv;
|
||||
|
||||
out vec2 uv;
|
||||
|
||||
#define PI 3.141592
|
||||
|
||||
vec3 pos;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
uniform mat4 vp;
|
||||
uniform mat4 model;
|
||||
|
||||
@include_block vert
|
||||
|
||||
void main()
|
||||
{
|
||||
pos = a_pos;
|
||||
uv = a_uv;
|
||||
vert();
|
||||
gl_Position = vp * model * vec4(pos, 1.0);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs fs
|
||||
|
||||
in vec2 uv;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
#define PI 3.141592
|
||||
|
||||
texture2D diffuse;
|
||||
sampler smp;
|
||||
|
||||
@include_block frag
|
||||
|
||||
void main()
|
||||
{
|
||||
frag();
|
||||
}
|
||||
@end
|
||||
|
||||
@program sprite vs fs
|
|
@ -1,61 +0,0 @@
|
|||
#depth off
|
||||
|
||||
@vs vs
|
||||
in vec2 a_pos;
|
||||
in vec2 a_uv;
|
||||
|
||||
struct letter {
|
||||
vec2 pos;
|
||||
vec2 wh;
|
||||
vec2 uv;
|
||||
vec2 st;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
readonly buffer ssbo {
|
||||
letter ls[];
|
||||
};
|
||||
|
||||
out vec2 uv;
|
||||
out vec2 fuv;
|
||||
out vec4 color0;
|
||||
|
||||
vec2 pos;
|
||||
|
||||
uniform mat4 vp;
|
||||
|
||||
@include_block vert
|
||||
|
||||
void main()
|
||||
{
|
||||
letter l = ls[gl_InstanceIndex];
|
||||
fuv = l.uv + vec2(a_pos.x*l.st.x, l.st.y - a_pos.y*l.st.y);
|
||||
uv = a_uv;
|
||||
color0 = l.color;
|
||||
pos = l.pos+(a_pos*l.wh);
|
||||
vert();
|
||||
gl_Position = vp * vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs fs
|
||||
in vec2 uv;
|
||||
in vec2 fuv;
|
||||
in vec4 color0;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
texture2D text;
|
||||
sampler smp;
|
||||
|
||||
@include_block frag
|
||||
|
||||
void main()
|
||||
{
|
||||
float lettera = texture(sampler2D(text,smp),fuv).r;
|
||||
if (lettera < 0.1f) discard;
|
||||
frag();
|
||||
}
|
||||
@end
|
||||
|
||||
@program text vs fs
|
|
@ -1,44 +0,0 @@
|
|||
#blend mix
|
||||
#primitive triangle
|
||||
#cull none
|
||||
#depth off
|
||||
|
||||
@vs vert
|
||||
in vec3 a_pos;
|
||||
|
||||
uniform float radius;
|
||||
uniform vec2 coord;
|
||||
uniform mat4 vp;
|
||||
out vec2 coords;
|
||||
out float rad;
|
||||
void main() {
|
||||
vec3 pos = a_pos;
|
||||
pos.xy -= 0.5;
|
||||
pos.xy *= 2;
|
||||
coords = pos.xy;
|
||||
pos *= radius;
|
||||
pos.xy += coord;
|
||||
rad = radius;
|
||||
gl_Position = vp * vec4(pos,1);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs frag
|
||||
in vec2 coords;
|
||||
in float rad;
|
||||
uniform vec4 shade;
|
||||
out vec4 color;
|
||||
void main() {
|
||||
float px = 1/rad;
|
||||
float R = 1;
|
||||
float R2 = 0.90;
|
||||
float dist = sqrt(dot(coords,coords));
|
||||
float sm = 1 - smoothstep(R-px,R,dist);
|
||||
float sm2 = smoothstep(R2-px,R2,dist);
|
||||
|
||||
float alpha = sm*sm2;
|
||||
color = vec4(shade.xyz, alpha*alpha);
|
||||
}
|
||||
@end
|
||||
|
||||
@program circle vert frag
|
|
@ -1,25 +0,0 @@
|
|||
#depth off
|
||||
#primitive triangle
|
||||
#cull none
|
||||
#blend mix
|
||||
|
||||
@vs vs
|
||||
in vec3 a_pos;
|
||||
uniform mat4 vp;
|
||||
uniform mat4 model;
|
||||
|
||||
void main() {
|
||||
gl_Position = vp * model * vec4(a_pos, 1);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs fs
|
||||
uniform vec4 shade;
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = shade;
|
||||
}
|
||||
@end
|
||||
|
||||
@program sprite vs fs
|
|
@ -1,42 +0,0 @@
|
|||
#cull back
|
||||
|
||||
@vs vs
|
||||
in vec3 a_pos;
|
||||
in vec2 a_uv;
|
||||
|
||||
out vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 pos = a_pos;
|
||||
pos -= 0.5;
|
||||
pos *= 2;
|
||||
uv = a_uv;
|
||||
gl_Position = vec4(pos.xy, 0, 1.0);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs fs
|
||||
in vec2 uv;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
#define PI 3.141592
|
||||
|
||||
@image_sample_type diffuse unfilterable_float
|
||||
texture2D diffuse;
|
||||
@sampler_type smp nonfiltering
|
||||
sampler smp;
|
||||
|
||||
uniform vec2 mouse;
|
||||
uniform float time;
|
||||
|
||||
@include_block frag
|
||||
|
||||
void main()
|
||||
{
|
||||
frag();
|
||||
}
|
||||
@end
|
||||
|
||||
@program p vs fs
|
|
@ -1,8 +0,0 @@
|
|||
@block frag
|
||||
void frag()
|
||||
{
|
||||
color = texture(sampler2D(diffuse,smp),uv);
|
||||
}
|
||||
@end
|
||||
|
||||
#include <postbase.cg>
|
|
@ -1,22 +0,0 @@
|
|||
@block vert
|
||||
uniform vec4 emissive;
|
||||
uniform vec4 rect;
|
||||
uniform vec2 diffuse_size;
|
||||
void vert()
|
||||
{
|
||||
pos *= vec3(diffuse_size * rect.zw,1);
|
||||
uv = (uv*rect.zw)+rect.xy;
|
||||
}
|
||||
@end
|
||||
|
||||
@block frag
|
||||
uniform vec4 shade;
|
||||
void frag()
|
||||
{
|
||||
color = texture(sampler2D(diffuse,smp), uv);
|
||||
if (color.a < 0.1) discard;
|
||||
color *= shade;
|
||||
}
|
||||
@end
|
||||
|
||||
#include <base.cg>
|
|
@ -1,3 +0,0 @@
|
|||
@block vert
|
||||
void vert(){}
|
||||
@end
|
|
@ -1,10 +0,0 @@
|
|||
#include <stdvert.cg>
|
||||
|
||||
@block frag
|
||||
void frag()
|
||||
{
|
||||
color = color0;
|
||||
}
|
||||
@end
|
||||
|
||||
#include <basetext.cg>
|
|
@ -1,25 +1,541 @@
|
|||
#include "2dphysics.h"
|
||||
|
||||
#include "gameobject.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "debugdraw.h"
|
||||
#include "stb_ds.h"
|
||||
#include <assert.h>
|
||||
#include <chipmunk/chipmunk_unsafe.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "2dphysics.h"
|
||||
|
||||
#include "jsffi.h"
|
||||
#include "script.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
cpSpace *space = NULL;
|
||||
|
||||
static JSValue *fns = NULL;
|
||||
static JSValue *hits = NULL;
|
||||
struct rgba color_white = {255,255,255,255};
|
||||
struct rgba color_black = {0,0,0,255};
|
||||
struct rgba color_clear = {0,0,0,0};
|
||||
|
||||
struct rgba disabled_color = {148,148,148,255};
|
||||
struct rgba sleep_color = {255,140,228,255};
|
||||
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;
|
||||
|
||||
cpTransform m3_to_cpt(HMM_Mat3 m)
|
||||
{
|
||||
cpTransform t;
|
||||
t.a = m.Columns[0].x;
|
||||
t.b = m.Columns[0].y;
|
||||
t.tx = m.Columns[2].x;
|
||||
t.c = m.Columns[1].x;
|
||||
t.d = m.Columns[1].y;
|
||||
t.ty = m.Columns[2].y;
|
||||
return t;
|
||||
}
|
||||
|
||||
cpShape *phys2d_query_pos(cpVect pos) {
|
||||
return cpSpacePointQueryNearest(space, pos, 0.f, CP_SHAPE_FILTER_ALL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int qhit;
|
||||
void qpoint(cpShape *shape, cpFloat dist, cpVect point, int *data)
|
||||
{
|
||||
qhit++;
|
||||
}
|
||||
|
||||
void bbhit(cpShape *shape, int *data)
|
||||
{
|
||||
qhit++;
|
||||
}
|
||||
|
||||
int query_point(HMM_Vec2 pos)
|
||||
{
|
||||
qhit = 0;
|
||||
// cpSpacePointQuery(space, pos.cp, 0, filter, qpoint, &qhit);
|
||||
cpSpaceBBQuery(space, cpBBNewForCircle(pos.cp, 2), CP_SHAPE_FILTER_ALL, bbhit, &qhit);
|
||||
return qhit;
|
||||
}
|
||||
|
||||
int p_compare(void *a, void *b)
|
||||
{
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
gameobject **clean_ids(gameobject **ids)
|
||||
{
|
||||
qsort(ids, arrlen(ids), sizeof(*ids), p_compare);
|
||||
gameobject *curid = NULL;
|
||||
for (int i = arrlen(ids)-1; i >= 0; i--)
|
||||
if (ids[i] == curid)
|
||||
arrdelswap(ids, i);
|
||||
else
|
||||
curid = ids[i];
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
int cpshape_enabled(cpShape *c) {
|
||||
cpShapeFilter filter = cpShapeGetFilter(c);
|
||||
if (filter.categories == ~CP_ALL_CATEGORIES && filter.mask == ~CP_ALL_CATEGORIES)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct rgba shape_color(cpShape *shape) {
|
||||
if (!cpshape_enabled(shape)) return disabled_color;
|
||||
switch (cpBodyGetType(cpShapeGetBody(shape))) {
|
||||
case CP_BODY_TYPE_DYNAMIC:
|
||||
// cpBodySleep(cpShapeGetBody(shape));
|
||||
if (cpBodyIsSleeping(cpShapeGetBody(shape)))
|
||||
return sleep_color;
|
||||
return dynamic_color;
|
||||
|
||||
case CP_BODY_TYPE_KINEMATIC:
|
||||
return kinematic_color;
|
||||
|
||||
case CP_BODY_TYPE_STATIC:
|
||||
return static_color;
|
||||
}
|
||||
|
||||
return static_color;
|
||||
}
|
||||
|
||||
static warp_gravity *space_gravity;
|
||||
|
||||
void phys2d_init()
|
||||
{
|
||||
space = cpSpaceNew();
|
||||
cpSpaceSetSleepTimeThreshold(space, 1);
|
||||
cpSpaceSetCollisionSlop(space, 0.01);
|
||||
cpSpaceSetCollisionBias(space, cpfpow(1.0-0.5, 165.f));
|
||||
space_gravity = warp_gravity_make();
|
||||
}
|
||||
|
||||
void phys2d_set_gravity(HMM_Vec2 v)
|
||||
{
|
||||
float str = HMM_LenV2(v);
|
||||
HMM_Vec2 dir = HMM_NormV2(v);
|
||||
space_gravity->strength = str;
|
||||
space_gravity->t.scale = (HMM_Vec3){v.x,v.y, 0};
|
||||
space_gravity->planar_force = (HMM_Vec3){v.x,v.y,0};
|
||||
}
|
||||
|
||||
constraint *constraint_make(cpConstraint *c)
|
||||
{
|
||||
constraint *cp = malloc(sizeof(*cp));
|
||||
cp->c = c;
|
||||
cp->break_cb = JS_UNDEFINED;
|
||||
cp->remove_cb = JS_UNDEFINED;
|
||||
cpSpaceAddConstraint(space,c);
|
||||
cpConstraintSetUserData(c, cp);
|
||||
return cp;
|
||||
}
|
||||
|
||||
void constraint_break(constraint *constraint)
|
||||
{
|
||||
if (!constraint->c) return;
|
||||
cpSpaceRemoveConstraint(space, constraint->c);
|
||||
cpConstraintFree(constraint->c);
|
||||
constraint->c = NULL;
|
||||
script_call_sym(constraint->break_cb,0,NULL);
|
||||
}
|
||||
|
||||
void constraint_free(constraint *constraint)
|
||||
{
|
||||
constraint_break(constraint);
|
||||
free(constraint);
|
||||
}
|
||||
|
||||
void constraint_test(cpConstraint *constraint, float *dt)
|
||||
{
|
||||
float max = cpConstraintGetMaxForce(constraint);
|
||||
if (!isfinite(max)) return;
|
||||
float force = cpConstraintGetImpulse(constraint)/ *dt;
|
||||
if (force > max)
|
||||
constraint_break(cpConstraintGetUserData(constraint));
|
||||
}
|
||||
|
||||
void phys2d_update(float deltaT) {
|
||||
cpSpaceStep(space, deltaT);
|
||||
arrsetlen(fns,0);
|
||||
arrsetlen(hits,0);
|
||||
cpSpaceEachConstraint(space, constraint_test, &deltaT);
|
||||
cb_idx = 0;
|
||||
}
|
||||
|
||||
void init_phys2dshape(struct phys2d_shape *shape, gameobject *go, void *data) {
|
||||
shape->go = go;
|
||||
shape->data = data;
|
||||
shape->t.scale = (HMM_Vec2){1.0,1.0};
|
||||
go_shape_apply(go->body, shape->shape, go);
|
||||
cpShapeSetCollisionType(shape->shape, (cpCollisionType)go);
|
||||
cpShapeSetUserData(shape->shape, shape);
|
||||
}
|
||||
|
||||
void phys2d_shape_del(struct phys2d_shape *shape) {
|
||||
if (!shape->shape) return;
|
||||
cpSpaceRemoveShape(space, shape->shape);
|
||||
cpShapeFree(shape->shape);
|
||||
}
|
||||
|
||||
/***************** CIRCLE2D *****************/
|
||||
struct phys2d_circle *Make2DCircle(gameobject *go) {
|
||||
struct phys2d_circle *new = malloc(sizeof(struct phys2d_circle));
|
||||
|
||||
new->radius = 10.f;
|
||||
new->offset = v2zero;
|
||||
|
||||
new->shape.shape = cpSpaceAddShape(space, cpCircleShapeNew(go->body, new->radius, cpvzero));
|
||||
new->shape.debugdraw = phys2d_dbgdrawcircle;
|
||||
new->shape.moi = phys2d_circle_moi;
|
||||
new->shape.apply = phys2d_applycircle;
|
||||
new->shape.free = NULL;
|
||||
init_phys2dshape(&new->shape, go, new);
|
||||
phys2d_applycircle(new);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
float phys2d_circle_moi(struct phys2d_circle *c) {
|
||||
float m = c->shape.go->mass;
|
||||
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 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));
|
||||
float radius = cpCircleShapeGetRadius(c);
|
||||
struct rgba color = shape_color(c);
|
||||
float seglen = cpShapeGetSensor(c) ? 5 : -1;
|
||||
draw_circle(pos, radius, 1, color, seglen);
|
||||
color.a = col_alpha;
|
||||
draw_circle(pos,radius,radius,color,-1);
|
||||
}
|
||||
|
||||
void phys2d_shape_apply(struct phys2d_shape *s)
|
||||
{
|
||||
float moment = cpBodyGetMoment(s->go->body);
|
||||
float moi = s->moi(s->data);
|
||||
|
||||
s->apply(s->data);
|
||||
float newmoi = s->moi(s->data);
|
||||
moment-=moi;
|
||||
moment += newmoi;
|
||||
if (moment < 0) moment = 0;
|
||||
cpBodySetMoment(s->go->body, moment);
|
||||
}
|
||||
|
||||
void phys2d_dbgdrawcircle(struct phys2d_circle *circle) {
|
||||
phys2d_dbgdrawcpcirc(circle->shape.shape);
|
||||
}
|
||||
|
||||
void phys2d_applycircle(struct phys2d_circle *circle) {
|
||||
gameobject *go = circle->shape.go;
|
||||
float radius = circle->radius * HMM_MAX(HMM_ABS(go->scale.X), HMM_ABS(go->scale.Y));
|
||||
cpCircleShapeSetRadius(circle->shape.shape, radius);
|
||||
cpCircleShapeSetOffset(circle->shape.shape, circle->offset.cp);
|
||||
}
|
||||
|
||||
/************** POLYGON ************/
|
||||
|
||||
struct phys2d_poly *Make2DPoly(gameobject *go) {
|
||||
struct phys2d_poly *new = malloc(sizeof(struct phys2d_poly));
|
||||
|
||||
new->points = NULL;
|
||||
arrsetlen(new->points, 0);
|
||||
new->radius = 0.f;
|
||||
|
||||
new->shape.shape = cpSpaceAddShape(space, cpPolyShapeNewRaw(go->body, 0, (cpVect*)new->points, new->radius));
|
||||
new->shape.debugdraw = phys2d_dbgdrawpoly;
|
||||
new->shape.moi = phys2d_poly_moi;
|
||||
new->shape.free = phys2d_poly_free;
|
||||
new->shape.apply = phys2d_applypoly;
|
||||
init_phys2dshape(&new->shape, go, new);
|
||||
return new;
|
||||
}
|
||||
|
||||
void phys2d_poly_free(struct phys2d_poly *poly)
|
||||
{
|
||||
arrfree(poly->points);
|
||||
free(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);
|
||||
|
||||
float moi = cpMomentForPoly(m, len, points, cpvzero, poly->radius);
|
||||
if (!isfinite(moi))
|
||||
return 0;
|
||||
|
||||
return moi;
|
||||
}
|
||||
|
||||
void phys2d_polydel(struct phys2d_poly *poly) {
|
||||
arrfree(poly->points);
|
||||
phys2d_shape_del(&poly->shape);
|
||||
}
|
||||
|
||||
void phys2d_polyaddvert(struct phys2d_poly *poly) {
|
||||
arrput(poly->points, v2zero);
|
||||
}
|
||||
|
||||
void phys2d_poly_setverts(struct phys2d_poly *poly, HMM_Vec2 *verts) {
|
||||
if (!verts) return;
|
||||
if (poly->points)
|
||||
arrfree(poly->points);
|
||||
|
||||
arrsetlen(poly->points, arrlen(verts));
|
||||
|
||||
for (int i = 0; i < arrlen(verts); i++)
|
||||
poly->points[i] = verts[i];
|
||||
|
||||
phys2d_shape_apply(&poly->shape);
|
||||
}
|
||||
|
||||
void phys2d_applypoly(struct phys2d_poly *poly) {
|
||||
if (arrlen(poly->points) <= 0) return;
|
||||
assert(sizeof(poly->points[0]) == sizeof(cpVect));
|
||||
struct gameobject *go = poly->shape.go;
|
||||
transform2d t = go2t(shape2go(poly->shape.shape));
|
||||
t.pos.cp = cpvzero;
|
||||
t.angle = 0;
|
||||
cpTransform T = m3_to_cpt(transform2d2mat(t));
|
||||
cpPolyShapeSetVerts(poly->shape.shape, arrlen(poly->points), (cpVect*)poly->points, T);
|
||||
cpPolyShapeSetRadius(poly->shape.shape, poly->radius);
|
||||
cpSpaceReindexShapesForBody(space, cpShapeGetBody(poly->shape.shape));
|
||||
}
|
||||
|
||||
void phys2d_dbgdrawpoly(struct phys2d_poly *poly) {
|
||||
struct rgba color = shape_color(poly->shape.shape);
|
||||
struct rgba line_color = color;
|
||||
color.a = col_alpha;
|
||||
|
||||
if (arrlen(poly->points) >= 3) {
|
||||
int n = cpPolyShapeGetCount(poly->shape.shape);
|
||||
HMM_Vec2 points[n+1];
|
||||
transform2d t = go2t(shape2go(poly->shape.shape));
|
||||
t.scale = (HMM_Vec2){1,1};
|
||||
HMM_Mat3 rt = transform2d2mat(t);
|
||||
for (int i = 0; i < n; i++)
|
||||
points[i] = mat_t_pos(rt, (HMM_Vec2)cpPolyShapeGetVert(poly->shape.shape, i));
|
||||
|
||||
points[n] = points[0];
|
||||
|
||||
draw_poly(points, n, color);
|
||||
float seglen = cpShapeGetSensor(poly->shape.shape) ? sensor_seg : 0;
|
||||
draw_line(points, n, line_color, seglen, 0);
|
||||
}
|
||||
}
|
||||
/****************** EDGE 2D**************/
|
||||
|
||||
struct phys2d_edge *Make2DEdge(gameobject *go) {
|
||||
struct phys2d_edge *new = malloc(sizeof(struct phys2d_edge));
|
||||
new->points = NULL;
|
||||
arrsetlen(new->points, 0);
|
||||
new->thickness = 0.f;
|
||||
new->shapes = NULL;
|
||||
arrsetlen(new->shapes, 0);
|
||||
new->shape.go = go;
|
||||
new->shape.data = new;
|
||||
new->shape.debugdraw = phys2d_dbgdrawedge;
|
||||
new->shape.moi = phys2d_edge_moi;
|
||||
new->shape.shape = NULL;
|
||||
new->shape.apply = NULL;
|
||||
new->shape.free = phys2d_edge_free;
|
||||
new->draws = 0;
|
||||
phys2d_applyedge(new);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
void phys2d_edge_free(struct phys2d_edge *edge)
|
||||
{
|
||||
for (int i = 0; i < arrlen(edge->shapes); i++)
|
||||
cpShapeSetUserData(edge->shapes[i], NULL);
|
||||
arrfree(edge->points);
|
||||
arrfree(edge->shapes);
|
||||
free(edge);
|
||||
}
|
||||
|
||||
float phys2d_edge_moi(struct phys2d_edge *edge) {
|
||||
float m = edge->shape.go->mass;
|
||||
float moi = 0;
|
||||
for (int i = 0; i < arrlen(edge->points) - 1; i++)
|
||||
moi += cpMomentForSegment(m, edge->points[i].cp, edge->points[i + 1].cp, edge->thickness);
|
||||
|
||||
return moi;
|
||||
}
|
||||
|
||||
void phys2d_edgedel(struct phys2d_edge *edge) { phys2d_shape_del(&edge->shape); }
|
||||
|
||||
void phys2d_edgeaddvert(struct phys2d_edge *edge, HMM_Vec2 v) {
|
||||
arrput(edge->points, v);
|
||||
if (arrlen(edge->points) > 1)
|
||||
arrput(edge->shapes, cpSpaceAddShape(space, cpSegmentShapeNew(edge->shape.go->body, cpvzero, cpvzero, edge->thickness)));
|
||||
}
|
||||
|
||||
void phys2d_edge_rmvert(struct phys2d_edge *edge, int index) {
|
||||
if (index>arrlen(edge->points) || index < 0) return;
|
||||
|
||||
arrdel(edge->points, index);
|
||||
|
||||
if (arrlen(edge->points) == 0) return;
|
||||
|
||||
if (index == 0) {
|
||||
cpSpaceRemoveShape(space, edge->shapes[index]);
|
||||
cpShapeFree(edge->shapes[index]);
|
||||
arrdel(edge->shapes, index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index != arrlen(edge->points))
|
||||
cpSegmentShapeSetEndpoints(edge->shapes[index - 1], edge->points[index - 1].cp, edge->points[index].cp);
|
||||
|
||||
cpSpaceRemoveShape(space, edge->shapes[index - 1]);
|
||||
cpShapeFree(edge->shapes[index - 1]);
|
||||
arrdel(edge->shapes, index - 1);
|
||||
}
|
||||
|
||||
void phys2d_edge_setvert(struct phys2d_edge *edge, int index, cpVect val) {
|
||||
assert(arrlen(edge->points) > index && index >= 0);
|
||||
edge->points[index].cp = val;
|
||||
}
|
||||
|
||||
void phys2d_edge_update_verts(struct phys2d_edge *edge, HMM_Vec2 *verts)
|
||||
{
|
||||
if (arrlen(edge->points) == arrlen(verts)) {
|
||||
for (int i = 0; i < arrlen(verts); i++)
|
||||
phys2d_edge_setvert(edge,i,verts[i].cp);
|
||||
} else {
|
||||
int vertchange = arrlen(verts)-arrlen(edge->points);
|
||||
phys2d_edge_clearverts(edge);
|
||||
phys2d_edge_addverts(edge,verts);
|
||||
}
|
||||
|
||||
phys2d_applyedge(edge);
|
||||
}
|
||||
|
||||
void phys2d_edge_clearverts(struct phys2d_edge *edge) {
|
||||
for (int i = arrlen(edge->points) - 1; i >= 0; i--)
|
||||
phys2d_edge_rmvert(edge, i);
|
||||
}
|
||||
|
||||
void phys2d_edge_addverts(struct phys2d_edge *edge, HMM_Vec2 *verts) {
|
||||
for (int i = 0; i < arrlen(verts); i++)
|
||||
phys2d_edgeaddvert(edge, verts[i]);
|
||||
}
|
||||
|
||||
/* Calculates all true positions of verts, links them up, and so on */
|
||||
void phys2d_applyedge(struct phys2d_edge *edge) {
|
||||
struct gameobject *go = edge->shape.go;
|
||||
|
||||
for (int i = 0; i < arrlen(edge->shapes); i++) {
|
||||
/* Points must be scaled with gameobject, */
|
||||
HMM_Vec2 a = HMM_MulV2(go->scale.xy, edge->points[i]);
|
||||
HMM_Vec2 b = HMM_MulV2(go->scale.xy, edge->points[i+1]);
|
||||
cpSegmentShapeSetEndpoints(edge->shapes[i], a.cp, b.cp);
|
||||
cpSegmentShapeSetRadius(edge->shapes[i], edge->thickness);
|
||||
if (i > 0 && i < arrlen(edge->shapes) - 1)
|
||||
cpSegmentShapeSetNeighbors(edge->shapes[i], HMM_MulV2(go->scale.xy,edge->points[i-1]).cp, HMM_MulV2(go->scale.xy,edge->points[i+2]).cp);
|
||||
go_shape_apply(NULL, edge->shapes[i], go);
|
||||
cpShapeSetUserData(edge->shapes[i], &edge->shape);
|
||||
}
|
||||
|
||||
cpSpaceReindexShapesForBody(space, edge->shape.go->body);
|
||||
}
|
||||
|
||||
void phys2d_dbgdrawedge(struct phys2d_edge *edge) {
|
||||
edge->draws++;
|
||||
if (edge->draws > 1) {
|
||||
if (edge->draws >= arrlen(edge->shapes))
|
||||
edge->draws = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (arrlen(edge->shapes) < 1) return;
|
||||
|
||||
HMM_Vec2 drawpoints[arrlen(edge->points)];
|
||||
struct gameobject *go = edge->shape.go;
|
||||
|
||||
HMM_Mat3 g2w = t_go2world(go);
|
||||
for (int i = 0; i < arrlen(edge->points); i++)
|
||||
drawpoints[i] = mat_t_pos(g2w, edge->points[i]);
|
||||
|
||||
float seglen = cpShapeGetSensor(edge->shapes[0]) ? sensor_seg : 0;
|
||||
struct rgba color = shape_color(edge->shapes[0]);
|
||||
struct rgba line_color = color;
|
||||
color.a = col_alpha;
|
||||
draw_edge(drawpoints, arrlen(edge->points), color, edge->thickness * 2, 0, line_color, seglen);
|
||||
draw_points(drawpoints, arrlen(edge->points), 2, kinematic_color);
|
||||
}
|
||||
|
||||
/************ COLLIDER ****************/
|
||||
void shape_enabled(struct phys2d_shape *shape, int enabled) {
|
||||
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) {
|
||||
if (cpshape_enabled(shape->shape))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shape_set_sensor(struct phys2d_shape *shape, int sensor) {
|
||||
if (!shape->shape) {
|
||||
struct phys2d_edge *edge = shape->data;
|
||||
|
||||
for (int i = 0; i < arrlen(edge->shapes); i++)
|
||||
cpShapeSetSensor(edge->shapes[i], sensor);
|
||||
} else
|
||||
cpShapeSetSensor(shape->shape, sensor);
|
||||
}
|
||||
|
||||
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]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cpShapeGetSensor(shape->shape);
|
||||
}
|
||||
|
||||
void phys2d_reindex_body(cpBody *body) { cpSpaceReindexShapesForBody(space, body); }
|
||||
|
||||
JSValue arb2js(cpArbiter *arb)
|
||||
{
|
||||
cpBody *body1;
|
||||
|
@ -30,21 +546,18 @@ JSValue arb2js(cpArbiter *arb)
|
|||
cpShape *shape2;
|
||||
cpArbiterGetShapes(arb, &shape1, &shape2);
|
||||
|
||||
JSValue j = *(JSValue*)cpShapeGetUserData(shape2);
|
||||
|
||||
JSValue jg = body2go(body2)->ref;
|
||||
|
||||
HMM_Vec2 srfv;
|
||||
srfv.cp = cpArbiterGetSurfaceVelocity(arb);
|
||||
struct phys2d_shape *pshape = cpShapeGetUserData(shape2);
|
||||
gameobject *go2 = cpBodyGetUserData(body2);
|
||||
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "normal", vec22js((HMM_Vec2)cpArbiterGetNormal(arb)));
|
||||
JS_SetPropertyStr(js, obj, "obj", JS_DupValue(js,jg));
|
||||
JS_SetPropertyStr(js, obj, "shape", JS_DupValue(js, j));
|
||||
JS_SetPropertyStr(js, obj, "point", vec22js((HMM_Vec2)cpArbiterGetPointA(arb, 0)));
|
||||
JS_SetPropertyStr(js, obj, "obj", JS_DupValue(js,go2->ref));
|
||||
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);
|
||||
JS_SetPropertyStr(js, obj, "velocity", vec22js(srfv));
|
||||
JS_SetPropertyStr(js, obj, "impulse", vec22js((HMM_Vec2)cpArbiterTotalImpulse(arb)));
|
||||
JS_SetPropertyStr(js, obj, "ke", number2js(cpArbiterTotalKE(arb)));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
@ -62,21 +575,31 @@ void register_hit(cpArbiter *arb, gameobject *go, const char *name)
|
|||
JSValue cb = JS_GetPropertyStr(js, go->ref, name);
|
||||
if (!JS_IsUndefined(cb)) {
|
||||
JSValue jarb = arb2js(arb);
|
||||
arrput(fns, JS_DupValue(js,cb));
|
||||
arrput(hits, jarb);
|
||||
cpSpaceAddPostStepCallback(space, phys_run_post, fns+arrlen(fns)-1, hits+arrlen(hits)-1);
|
||||
fns[cb_idx] = JS_DupValue(js, cb);
|
||||
hits[cb_idx] = jarb;
|
||||
cpSpaceAddPostStepCallback(space, phys_run_post, &fns[cb_idx], &hits[cb_idx]);
|
||||
cb_idx++;
|
||||
}
|
||||
|
||||
cpShape *s1, *s2;
|
||||
cpArbiterGetShapes(arb, &s1, &s2);
|
||||
JSValue j1 = *(JSValue*)cpShapeGetUserData(s1);
|
||||
JSValue j2 = *(JSValue*)cpShapeGetUserData(s2);
|
||||
cb = JS_GetPropertyStr(js, j1, name);
|
||||
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);
|
||||
arrput(fns, JS_DupValue(js,cb));
|
||||
arrput(hits, jarb);
|
||||
cpSpaceAddPostStepCallback(space, phys_run_post, fns+arrlen(fns)-1, hits+arrlen(hits)-1);
|
||||
fns[cb_idx] = JS_DupValue(js,cb);
|
||||
hits[cb_idx] = jarb;
|
||||
cpSpaceAddPostStepCallback(space, phys_run_post, &fns[cb_idx], &hits[cb_idx]);
|
||||
cb_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,130 @@
|
|||
#ifndef TWODPHYSICS_H
|
||||
#define TWODPHYSICS_H
|
||||
|
||||
#include "script.h"
|
||||
#include <chipmunk/chipmunk.h>
|
||||
#include "gameobject.h"
|
||||
#include "script.h"
|
||||
#include "render.h"
|
||||
#include "transform.h"
|
||||
|
||||
extern float phys2d_gravity;
|
||||
extern int physOn;
|
||||
extern cpSpace *space;
|
||||
|
||||
extern struct rgba disabled_color;
|
||||
extern struct rgba dynamic_color;
|
||||
extern struct rgba kinematic_color;
|
||||
extern struct rgba static_color;
|
||||
extern struct rgba sleep_color;
|
||||
|
||||
typedef struct constraint {
|
||||
cpConstraint *c;
|
||||
JSValue break_cb; /* function called when it is forcibly broken */
|
||||
JSValue remove_cb; /* called when it is removed at all */
|
||||
} constraint;
|
||||
|
||||
constraint *constraint_make(cpConstraint *c);
|
||||
void constraint_break(constraint *constraint);
|
||||
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 */
|
||||
void (*debugdraw)(void *data);
|
||||
float (*moi)(void *data);
|
||||
void (*apply)(void *data);
|
||||
void (*free)(void *data);
|
||||
};
|
||||
|
||||
void phys2d_shape_apply(struct phys2d_shape *s);
|
||||
|
||||
/* Circles are the fastest colldier type */
|
||||
struct phys2d_circle {
|
||||
float radius;
|
||||
HMM_Vec2 offset;
|
||||
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;
|
||||
transform2d t;
|
||||
float radius;
|
||||
struct phys2d_shape shape;
|
||||
};
|
||||
|
||||
/* An edge with no volume. Cannot collide with each other. Join to make levels. Static only. */
|
||||
struct phys2d_edge {
|
||||
HMM_Vec2 *points; /* Points defined relative to the gameobject */
|
||||
float thickness;
|
||||
cpShape **shapes;
|
||||
struct phys2d_shape shape;
|
||||
int draws;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
struct phys2d_poly *Make2DPoly(gameobject *go);
|
||||
void phys2d_poly_free(struct phys2d_poly *poly);
|
||||
void phys2d_polydel(struct phys2d_poly *poly);
|
||||
void phys2d_applypoly(struct phys2d_poly *poly);
|
||||
void phys2d_dbgdrawpoly(struct phys2d_poly *poly);
|
||||
void phys2d_polyaddvert(struct phys2d_poly *poly);
|
||||
void phys2d_poly_setverts(struct phys2d_poly *poly, HMM_Vec2 *verts);
|
||||
float phys2d_poly_moi(struct phys2d_poly *poly);
|
||||
|
||||
struct phys2d_edge *Make2DEdge(gameobject *go);
|
||||
void phys2d_edge_free(struct phys2d_edge *edge);
|
||||
void phys2d_edgedel(struct phys2d_edge *edge);
|
||||
void phys2d_applyedge(struct phys2d_edge *edge);
|
||||
void phys2d_dbgdrawedge(struct phys2d_edge *edge);
|
||||
void phys2d_edgeaddvert(struct phys2d_edge *edge, HMM_Vec2 v);
|
||||
void phys2d_edge_rmvert(struct phys2d_edge *edge, int index);
|
||||
float phys2d_edge_moi(struct phys2d_edge *edge);
|
||||
|
||||
void phys2d_edge_setvert(struct phys2d_edge *edge, int index, cpVect val);
|
||||
void phys2d_edge_clearverts(struct phys2d_edge *edge);
|
||||
void phys2d_edge_rmvert(struct phys2d_edge *edge, int index);
|
||||
void phys2d_edge_update_verts(struct phys2d_edge *edge, HMM_Vec2 *verts);
|
||||
void phys2d_edge_addverts(struct phys2d_edge *edge, HMM_Vec2 *verts);
|
||||
void phys2d_edge_set_sensor(struct phys2d_edge *edge, int sensor);
|
||||
void phys2d_edge_set_enabled(struct phys2d_edge *edge, int enabled);
|
||||
|
||||
void phys2d_init();
|
||||
void phys2d_update(float deltaT);
|
||||
void phys2d_setup_handlers(gameobject *go);
|
||||
cpShape *phys2d_query_pos(cpVect pos);
|
||||
void phys2d_query_ray(HMM_Vec2 start, HMM_Vec2 end, float radius, cpShapeFilter filter, JSValue cb);
|
||||
|
||||
JSValue arb2js(cpArbiter *arb);
|
||||
struct shape_cb {
|
||||
struct phys2d_shape *shape;
|
||||
struct phys_cbs cbs;
|
||||
};
|
||||
|
||||
void fire_hits();
|
||||
|
||||
void phys2d_set_gravity(HMM_Vec2 v);
|
||||
|
||||
void shape_enabled(struct phys2d_shape *shape, int enabled);
|
||||
int shape_is_enabled(struct phys2d_shape *shape);
|
||||
void shape_set_sensor(struct phys2d_shape *shape, int sensor);
|
||||
int shape_get_sensor(struct phys2d_shape *shape);
|
||||
|
||||
struct rgba shape_color_s(cpShape *shape);
|
||||
|
||||
void shape_gui(struct phys2d_shape *shape);
|
||||
void phys2d_setup_handlers(gameobject *go);
|
||||
int query_point(HMM_Vec2 pos);
|
||||
|
||||
void phys2d_reindex_body(cpBody *body);
|
||||
|
||||
#endif
|
||||
|
|
201
source/engine/3d/light.c
Normal file
201
source/engine/3d/light.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
#include "light.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
void Light::serialize(FILE * file)
|
||||
{
|
||||
GameObject::serialize(file);
|
||||
|
||||
SerializeFloat(file, &strength);
|
||||
SerializeVec3(file, (float *) &color);
|
||||
SerializeBool(file, &dynamic);
|
||||
}
|
||||
|
||||
void Light::deserialize(FILE * file)
|
||||
{
|
||||
GameObject::deserialize(file);
|
||||
|
||||
DeserializeFloat(file, &strength);
|
||||
DeserializeVec3(file, (float *) &color);
|
||||
DeserializeBool(file, &dynamic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const mfloat_t dlight_init_rot[3] = { 80.f, 120.f, 165.f };
|
||||
|
||||
struct mDirectionalLight *dLight = NULL;
|
||||
|
||||
struct mDirectionalLight *MakeDLight()
|
||||
{
|
||||
if (dLight != NULL) {
|
||||
dLight =
|
||||
(struct mDirectionalLight *)
|
||||
malloc(sizeof(struct mDirectionalLight));
|
||||
quat_from_euler(dLight->light.obj.transform.rotation,
|
||||
dlight_init_rot);
|
||||
|
||||
return dLight;
|
||||
}
|
||||
|
||||
return dLight;
|
||||
}
|
||||
|
||||
void dlight_prepshader(struct mDirectionalLight *light,
|
||||
struct shader *shader)
|
||||
{
|
||||
mfloat_t fwd[3] = { 0.f };
|
||||
trans_forward(fwd, &light->light.obj.transform);
|
||||
shader_setvec3(shader, "dirLight.direction", fwd);
|
||||
shader_setvec3(shader, "dirLight.color", light->light.color);
|
||||
shader_setfloat(shader, "dirLight.strength", light->light.strength);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct mPointLight *pointLights[4];
|
||||
static int numLights = 0;
|
||||
|
||||
|
||||
struct mPointLight *MakePointlight()
|
||||
{
|
||||
if (numLights < 4) {
|
||||
struct mPointLight *light =
|
||||
(struct mPointLight *) malloc(sizeof(struct mPointLight));
|
||||
pointLights[numLights++] = light;
|
||||
light->light.strength = 0.2f;
|
||||
light->constant = 1.f;
|
||||
light->linear = 0.9f;
|
||||
light->quadratic = 0.032f;
|
||||
return light;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void prepstring(char *buffer, char *prepend, const char *append)
|
||||
{
|
||||
snprintf(buffer, 100, "%s%s", prepend, append);
|
||||
}
|
||||
|
||||
void pointlights_prepshader(struct shader *shader)
|
||||
{
|
||||
for (int i = 0; i < numLights; i++)
|
||||
pointlight_prepshader(pointLights[i], shader, i);
|
||||
}
|
||||
|
||||
void pointlight_prepshader(struct mPointLight *light,
|
||||
struct shader *shader, int num)
|
||||
{
|
||||
shader_use(shader);
|
||||
char prepend[100] = { '\0' };
|
||||
snprintf(prepend, 100, "%s%d%s", "pointLights[", num, "].");
|
||||
char str[100] = { '\0' };
|
||||
|
||||
prepstring(str, prepend, "position");
|
||||
shader_setvec3(shader, str, light->light.obj.transform.position);
|
||||
|
||||
prepstring(str, prepend, "constant");
|
||||
shader_setfloat(shader, str, light->constant);
|
||||
|
||||
prepstring(str, prepend, "linear");
|
||||
shader_setfloat(shader, str, light->linear);
|
||||
|
||||
prepstring(str, prepend, "quadratic");
|
||||
shader_setfloat(shader, str, light->quadratic);
|
||||
|
||||
prepstring(str, prepend, "strength");
|
||||
shader_setfloat(shader, str, light->light.strength);
|
||||
|
||||
prepstring(str, prepend, "color");
|
||||
shader_setvec3(shader, str, light->light.color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static struct mSpotLight *spotLights[4];
|
||||
static int numSpots = 0;
|
||||
|
||||
struct mSpotLight *MakeSpotlight()
|
||||
{
|
||||
if (numSpots < 4) {
|
||||
struct mSpotLight *light =
|
||||
(struct mSpotLight *) malloc(sizeof(struct mSpotLight));
|
||||
spotLights[numSpots++] = light;
|
||||
return light;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void spotlights_prepshader(struct shader *shader)
|
||||
{
|
||||
for (int i = 0; i < numSpots; i++)
|
||||
spotlight_prepshader(spotLights[i], shader, i);
|
||||
}
|
||||
|
||||
void spotlight_prepshader(struct mSpotLight *light, struct shader *shader,
|
||||
int num)
|
||||
{
|
||||
mfloat_t fwd[3] = { 0.f };
|
||||
trans_forward(fwd, &light->light.obj.transform);
|
||||
shader_use(shader);
|
||||
shader_setvec3(shader, "spotLight.position",
|
||||
light->light.obj.transform.position);
|
||||
shader_setvec3(shader, "spotLight.direction", fwd);
|
||||
shader_setvec3(shader, "spotLight.color", light->light.color);
|
||||
shader_setfloat(shader, "spotLight.strength", light->light.strength);
|
||||
shader_setfloat(shader, "spotLight.cutoff", light->cutoff);
|
||||
shader_setfloat(shader, "spotLight.distance", light->distance);
|
||||
shader_setfloat(shader, "spotLight.outerCutoff", light->outerCutoff);
|
||||
shader_setfloat(shader, "spotLight.linear", light->linear);
|
||||
shader_setfloat(shader, "spotLight.quadratic", light->quadratic);
|
||||
shader_setfloat(shader, "spotLight.constant", light->constant);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
void light_gui(struct mLight *light)
|
||||
{
|
||||
object_gui(&light->obj);
|
||||
|
||||
if (nk_tree_push(ctx, NK_TREE_NODE, "Light", NK_MINIMIZED)) {
|
||||
nk_property_float(ctx, "Strength", 0.f, &light->strength, 1.f, 0.01f, 0.001f);
|
||||
// ImGui::ColorEdit3("Color", &light->color[0]);
|
||||
nk_checkbox_label(ctx, "Dynamic", (bool *) &light->dynamic);
|
||||
nk_tree_pop(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pointlight_gui(struct mPointLight *light)
|
||||
{
|
||||
light_gui(&light->light);
|
||||
|
||||
if (nk_tree_push(ctx, NK_TREE_NODE, "Point Light", NK_MINIMIZED)) {
|
||||
nk_property_float(ctx, "Constant", 0.f, &light->constant, 1.f, 0.01f, 0.001f);
|
||||
nk_property_float(ctx, "Linear", 0.f, &light->linear, 0.3f, 0.01f, 0.001f);
|
||||
nk_property_float(ctx, "Quadratic", 0.f, &light->quadratic, 0.3f, 0.01f, 0.001f);
|
||||
nk_tree_pop(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void spotlight_gui(struct mSpotLight *spot)
|
||||
{
|
||||
light_gui(&spot->light);
|
||||
|
||||
if (nk_tree_push(ctx, NK_TREE_NODE, "Spotlight", NK_MINIMIZED)) {
|
||||
nk_property_float(ctx, "Linear", 0.f, &spot->linear, 1.f, 0.01f, 0.001f);
|
||||
nk_property_float(ctx, "Quadratic", 0.f, &spot->quadratic, 1.f, 0.01f, 0.001f);
|
||||
nk_property_float(ctx, "Distance", 0.f, &spot->distance, 200.f, 1.f, 0.1f, 200.f);
|
||||
nk_property_float(ctx, "Cutoff Degrees", 0.f, &spot->cutoff, 0.7f, 0.01f, 0.001f);
|
||||
nk_property_float(ctx, "Outer Cutoff Degrees", 0.f, &spot->outerCutoff, 0.7f, 0.01f, 0.001f);
|
||||
nk_tree_pop(ctx);
|
||||
}
|
||||
}
|
||||
*/
|
63
source/engine/3d/light.h
Normal file
63
source/engine/3d/light.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef LIGHT_H
|
||||
#define LIGHT_H
|
||||
|
||||
#include <stdint.h>
|
||||
/*
|
||||
struct mLight {
|
||||
struct gameobject *go;
|
||||
uint8_t color[3];
|
||||
float strength;
|
||||
int dynamic;
|
||||
int on;
|
||||
};
|
||||
|
||||
|
||||
struct mPointLight {
|
||||
struct mLight light;
|
||||
float constant;
|
||||
float linear;
|
||||
float quadratic;
|
||||
};
|
||||
|
||||
struct mPointLight *MakePointlight();
|
||||
void pointlight_prepshader(struct mPointLight *light,
|
||||
struct shader *shader, int num);
|
||||
void pointlights_prepshader(struct shader *shader);
|
||||
|
||||
|
||||
struct mSpotLight {
|
||||
struct mLight light;
|
||||
float constant;
|
||||
float linear;
|
||||
float quadratic;
|
||||
float distance;
|
||||
|
||||
float cutoff;
|
||||
float outerCutoff;
|
||||
};
|
||||
|
||||
struct mSpotLight *MakeSpotlight();
|
||||
void spotlight_gui(struct mSpotLight *light);
|
||||
void spotlight_prepshader(struct mSpotLight *light, struct shader *shader,
|
||||
int num);
|
||||
void spotlights_prepshader(struct shader *shader);
|
||||
|
||||
|
||||
|
||||
struct mDirectionalLight {
|
||||
struct mLight light;
|
||||
};
|
||||
|
||||
void dlight_prepshader(struct mDirectionalLight *light,
|
||||
struct shader *shader);
|
||||
struct mDirectionalLight *MakeDLight();
|
||||
|
||||
extern struct mDirectionalLight *dLight;
|
||||
|
||||
|
||||
|
||||
void light_gui(struct mLight *light);
|
||||
void pointlight_gui(struct mPointLight *light);
|
||||
void spotlight_gui(struct mSpotLight *spot);
|
||||
*/
|
||||
#endif
|
364
source/engine/3d/model.c
Normal file
364
source/engine/3d/model.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
#include "model.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "resources.h"
|
||||
#include "stb_ds.h"
|
||||
#include "font.h"
|
||||
#include "gameobject.h"
|
||||
|
||||
//#include "diffuse.sglsl.h"
|
||||
#include "unlit.sglsl.h"
|
||||
|
||||
#include "render.h"
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
|
||||
#include "math.h"
|
||||
#include "time.h"
|
||||
|
||||
#define CGLTF_IMPLEMENTATION
|
||||
#include <cgltf.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
#include "sokol/sokol_gfx.h"
|
||||
|
||||
static void processnode();
|
||||
static void processmesh();
|
||||
static void processtexture();
|
||||
|
||||
static sg_shader model_shader;
|
||||
static sg_pipeline model_pipe;
|
||||
|
||||
struct bone_weights {
|
||||
char b1;
|
||||
char b2;
|
||||
char b3;
|
||||
char b4;
|
||||
};
|
||||
|
||||
struct mesh_v {
|
||||
HMM_Vec3 pos;
|
||||
struct uv_n uv;
|
||||
uint32_t norm;
|
||||
struct bone_weights bones;
|
||||
};
|
||||
|
||||
void model_init() {
|
||||
model_shader = sg_make_shader(unlit_shader_desc(sg_query_backend()));
|
||||
|
||||
model_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = model_shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT3,
|
||||
[1].format = SG_VERTEXFORMAT_USHORT2N,
|
||||
[1].buffer_index = 1,
|
||||
},
|
||||
},
|
||||
.index_type = SG_INDEXTYPE_UINT16,
|
||||
.cull_mode = SG_CULLMODE_FRONT,
|
||||
.depth.write_enabled = true,
|
||||
.depth.compare = SG_COMPAREFUNC_LESS_EQUAL
|
||||
});
|
||||
}
|
||||
|
||||
cgltf_attribute *get_attr_type(cgltf_primitive *p, cgltf_attribute_type t)
|
||||
{
|
||||
for (int i = 0; i < p->attributes_count; i++) {
|
||||
if (p->attributes[i].type == t)
|
||||
return &p->attributes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned short pack_short_texcoord(float x, float y)
|
||||
{
|
||||
unsigned short s;
|
||||
char xc = x*255;
|
||||
char yc = y*255;
|
||||
return (((unsigned short)yc) << 8) | xc;
|
||||
}
|
||||
|
||||
unsigned short pack_short_tex(float c) { return c * USHRT_MAX; }
|
||||
|
||||
uint32_t pack_int10_n2(float *norm)
|
||||
{
|
||||
uint32_t ni[3];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ni[i] = fabs(norm[i]) * 511.0 + 0.5;
|
||||
ni[i] = (ni[i] > 511) ? 511 : ni[i];
|
||||
ni[i] = ( norm[i] < 0.0 ) ? -ni[i] : ni[i];
|
||||
}
|
||||
|
||||
return (ni[0] & 0x3FF) | ( (ni[1] & 0x3FF) << 10) | ( (ni[2] & 0x3FF) << 20) | ( (0 & 0x3) << 30);
|
||||
}
|
||||
|
||||
void mesh_add_material(mesh *mesh, cgltf_material *mat)
|
||||
{
|
||||
if (!mat) return;
|
||||
|
||||
if (mat && mat->has_pbr_metallic_roughness) {
|
||||
cgltf_image *img = mat->pbr_metallic_roughness.base_color_texture.texture->image;
|
||||
if (img->buffer_view) {
|
||||
cgltf_buffer_view *buf = img->buffer_view;
|
||||
mesh->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id;
|
||||
} else
|
||||
mesh->bind.fs.images[0] = texture_from_file(img->uri)->id;
|
||||
} else
|
||||
mesh->bind.fs.images[0] = texture_from_file("icons/moon.gif")->id;
|
||||
|
||||
mesh->bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
|
||||
/*
|
||||
cgltf_texture *tex;
|
||||
if (tex = mat->normal_texture.texture)
|
||||
mesh->bind.fs.images[1] = texture_from_file(tex->image->uri)->id;
|
||||
else
|
||||
mesh->bind.fs.images[1] = texture_from_file("k")->id;*/
|
||||
}
|
||||
|
||||
sg_buffer texcoord_floats(float *f, int verts, int comp)
|
||||
{
|
||||
int n = verts*comp;
|
||||
unsigned short packed[n];
|
||||
for (int i = 0, v = 0; i < n; i++)
|
||||
packed[i] = pack_short_tex(f[i]);
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = packed,
|
||||
.data.size = sizeof(unsigned short) * verts,
|
||||
.label = "tex coord vert buffer",
|
||||
});
|
||||
}
|
||||
|
||||
sg_buffer normal_floats(float *f, int verts, int comp)
|
||||
{
|
||||
uint32_t packed_norms[verts];
|
||||
for (int v = 0, i = 0; v < verts; v++, i+= comp)
|
||||
packed_norms[v] = pack_int10_n2(f+i);
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = packed_norms,
|
||||
.data.size = sizeof(uint32_t) * verts,
|
||||
.label = "normal vert buffer",
|
||||
});
|
||||
}
|
||||
|
||||
HMM_Vec3 index_to_vert(uint32_t idx, float *f)
|
||||
{
|
||||
return (HMM_Vec3){f[idx*3], f[idx*3+1], f[idx*3+2]};
|
||||
}
|
||||
|
||||
void mesh_add_primitive(mesh *mesh, cgltf_primitive *prim)
|
||||
{
|
||||
uint16_t *idxs;
|
||||
if (prim->indices) {
|
||||
int c = prim->indices->count;
|
||||
idxs = malloc(sizeof(*idxs)*c);
|
||||
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,
|
||||
.label = "mesh index buffer",
|
||||
});
|
||||
|
||||
mesh->idx_count = c;
|
||||
} else {
|
||||
YughWarn("Model does not have indices. Generating them.");
|
||||
int c = prim->attributes[0].data->count;
|
||||
|
||||
mesh->idx_count = c;
|
||||
idxs = malloc(sizeof(*idxs)*c);
|
||||
|
||||
for (int z = 0; z < c; z++)
|
||||
idxs[z] = z;
|
||||
|
||||
mesh->bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = idxs,
|
||||
.data.size = sizeof(uint16_t) * c,
|
||||
.type = SG_BUFFERTYPE_INDEXBUFFER});
|
||||
}
|
||||
free(idxs);
|
||||
|
||||
mesh_add_material(mesh, prim->material);
|
||||
int has_norm = 0;
|
||||
|
||||
for (int k = 0; k < prim->attributes_count; k++) {
|
||||
cgltf_attribute attribute = prim->attributes[k];
|
||||
|
||||
int n = cgltf_accessor_unpack_floats(attribute.data, NULL, 0); /* floats per vertex x num elements. In other words, total floats pulled */
|
||||
int comp = cgltf_num_components(attribute.data->type);
|
||||
int verts = n/comp;
|
||||
float vs[n];
|
||||
cgltf_accessor_unpack_floats(attribute.data, vs, n);
|
||||
|
||||
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,
|
||||
.label = "mesh vert buffer"
|
||||
});
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_normal:
|
||||
// has_norm = 1;
|
||||
// mesh->bind.vertex_buffers[2] = normal_floats(vs, verts, comp);
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_tangent:
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_color:
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_weights:
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_joints:
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_texcoord:
|
||||
mesh->bind.vertex_buffers[1] = texcoord_floats(vs, verts, comp);
|
||||
break;
|
||||
case cgltf_attribute_type_invalid:
|
||||
YughWarn("Invalid type.");
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_custom:
|
||||
break;
|
||||
case cgltf_attribute_type_max_enum:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (!has_norm) {
|
||||
cgltf_attribute *pa = get_attr_type(prim, cgltf_attribute_type_position);
|
||||
int n = cgltf_accessor_unpack_floats(pa->data, NULL,0);
|
||||
int comp = 3;
|
||||
int verts = n/comp;
|
||||
uint32_t face_norms[verts];
|
||||
float ps[n];
|
||||
cgltf_accessor_unpack_floats(pa->data,ps,n);
|
||||
|
||||
for (int i = 0; i < verts; i+=3) {
|
||||
HMM_Vec3 a = index_to_vert(i,ps);
|
||||
HMM_Vec3 b = index_to_vert(i+1,ps);
|
||||
HMM_Vec3 c = index_to_vert(i+2,ps);
|
||||
HMM_Vec3 norm = HMM_NormV3(HMM_Cross(HMM_SubV3(b,a), HMM_SubV3(c,a)));
|
||||
uint32_t packed_norm = pack_int10_n2(norm.Elements);
|
||||
face_norms[i] = face_norms[i+1] = face_norms[i+2] = packed_norm;
|
||||
}
|
||||
|
||||
mesh->bind.vertex_buffers[2] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = face_norms,
|
||||
.data.size = sizeof(uint32_t) * verts});
|
||||
}*/
|
||||
}
|
||||
|
||||
void model_add_cgltf_mesh(model *model, cgltf_mesh *gltf_mesh)
|
||||
{
|
||||
mesh mesh = {0};
|
||||
|
||||
for (int i = 0; i < gltf_mesh->primitives_count; i++)
|
||||
mesh_add_primitive(&mesh, &gltf_mesh->primitives[i]);
|
||||
|
||||
arrput(model->meshes,mesh);
|
||||
}
|
||||
|
||||
void model_add_cgltf_anim(model *model, cgltf_animation *anim)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void model_add_cgltf_skin(model *model, cgltf_skin *skin)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void model_process_node(model *model, cgltf_node *node)
|
||||
{
|
||||
if (node->has_matrix)
|
||||
memcpy(model->matrix.Elements, node->matrix, sizeof(float)*16);
|
||||
|
||||
if (node->mesh)
|
||||
model_add_cgltf_mesh(model, node->mesh);
|
||||
|
||||
if (node->skin)
|
||||
model_add_cgltf_skin(model, node->skin);
|
||||
}
|
||||
|
||||
void model_process_scene(model *model, cgltf_scene *scene)
|
||||
{
|
||||
for (int i = 0; i < scene->nodes_count; i++)
|
||||
model_process_node(model, scene->nodes[i]);
|
||||
}
|
||||
|
||||
struct model *model_make(const char *path)
|
||||
{
|
||||
YughInfo("Making the model from %s.", path);
|
||||
cgltf_options options = {0};
|
||||
cgltf_data *data = NULL;
|
||||
cgltf_result result = cgltf_parse_file(&options, path, &data);
|
||||
|
||||
if (result) {
|
||||
YughError("CGLTF could not parse file %s, err %d.", path, result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = cgltf_load_buffers(&options, data, path);
|
||||
|
||||
if (result) {
|
||||
YughError("CGLTF could not load buffers for file %s, err %d.", path, result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct model *model = calloc(1, sizeof(*model));
|
||||
|
||||
if (data->scenes_count == 0 || data->scenes_count > 1) return NULL;
|
||||
model_process_scene(model, data->scene);
|
||||
|
||||
for (int i = 0; i < data->meshes_count; i++)
|
||||
model_add_cgltf_mesh(model, &data->meshes[i]);
|
||||
|
||||
for (int i = 0; i < data->animations_count; i++)
|
||||
model_add_cgltf_anim(model, &data->animations[i]);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
void model_free(model *m)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void model_draw_go(model *model, gameobject *go, gameobject *cam)
|
||||
{
|
||||
HMM_Mat4 view = t3d_go2world(cam);
|
||||
HMM_Mat4 proj = HMM_Perspective_RH_NO(20, 1, 0.01, 10000);
|
||||
HMM_Mat4 vp = HMM_MulM4(proj, view);
|
||||
|
||||
vs_p_t vs_p;
|
||||
memcpy(vs_p.vp, vp.Elements, sizeof(float)*16);
|
||||
memcpy(vs_p.model, t3d_go2world(go).Elements, sizeof(float)*16);
|
||||
|
||||
sg_apply_pipeline(model_pipe);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vs_p, SG_RANGE_REF(vs_p));
|
||||
|
||||
for (int i = 0; i < arrlen(model->meshes); i++) {
|
||||
sg_apply_bindings(&model->meshes[i].bind);
|
||||
sg_draw(0, model->meshes[i].idx_count, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void material_free(material *mat)
|
||||
{
|
||||
|
||||
}
|
45
source/engine/3d/model.h
Normal file
45
source/engine/3d/model.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef MODEL_H
|
||||
#define MODEL_H
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
#include "transform.h"
|
||||
#include "sokol/sokol_gfx.h"
|
||||
#include "gameobject.h"
|
||||
|
||||
extern HMM_Vec3 eye;
|
||||
|
||||
typedef struct material {
|
||||
|
||||
} material;
|
||||
|
||||
struct model;
|
||||
|
||||
/* A single mesh */
|
||||
typedef struct mesh {
|
||||
sg_bindings bind; /* Encapsulates material, norms, etc */
|
||||
uint32_t idx_count;
|
||||
} mesh;
|
||||
|
||||
/* A collection of meshes which create a full figure */
|
||||
typedef struct model {
|
||||
struct mesh *meshes;
|
||||
HMM_Mat4 matrix;
|
||||
} model;
|
||||
|
||||
typedef struct bone {
|
||||
transform3d t;
|
||||
struct bone *children;
|
||||
} bone;
|
||||
|
||||
/* Make a Model struct */
|
||||
struct model *model_make(const char *path);
|
||||
void model_free(model *m);
|
||||
|
||||
void model_draw_go(model *m, gameobject *go, gameobject *cam);
|
||||
|
||||
void model_init();
|
||||
|
||||
material *material_make();
|
||||
void material_free(material *mat);
|
||||
|
||||
#endif
|
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
|
@ -2,35 +2,36 @@
|
|||
#include "log.h"
|
||||
#include "stb_ds.h"
|
||||
|
||||
void animation_run(struct animation *anim, float now)
|
||||
void sampler_add(sampler *s, float time, HMM_Vec4 val)
|
||||
{
|
||||
float elapsed = now - anim->time;
|
||||
elapsed = fmod(elapsed,2);
|
||||
if (!anim->channels) return;
|
||||
|
||||
for (int i = 0; i < arrlen(anim->channels); i++) {
|
||||
struct anim_channel *ch = anim->channels+i;
|
||||
HMM_Vec4 s = sample_sampler(ch->sampler, elapsed);
|
||||
*(ch->target) = s;
|
||||
}
|
||||
arrput(s->times,time);
|
||||
arrput(s->data,val);
|
||||
}
|
||||
|
||||
HMM_Vec4 sample_cubicspline(sampler *sampler, float t, int prev, int next)
|
||||
{
|
||||
return (HMM_Vec4)HMM_SLerp(HMM_QV4(sampler->data[prev]), t, HMM_QV4(sampler->data[next]));
|
||||
float t2 = t*t;
|
||||
float t3 = t2*t;
|
||||
float td = sampler->times[next]-sampler->times[prev];
|
||||
|
||||
HMM_Vec4 v = HMM_MulV4F(sampler->data[prev*3+1], (2*t3-3*t2+1));
|
||||
v = HMM_AddV4(v, HMM_MulV4F(sampler->data[prev*3+2], td*(t3-2*t2+t)));
|
||||
v = HMM_AddV4(v, HMM_MulV4F(sampler->data[next*3+1], 3*t2-2*t3));
|
||||
v = HMM_AddV4(v, HMM_MulV4F(sampler->data[next*3], td*(t3-t2)));
|
||||
return v;
|
||||
}
|
||||
|
||||
HMM_Vec4 sample_sampler(sampler *sampler, float time)
|
||||
{
|
||||
if (arrlen(sampler->data) == 0) return v4zero;
|
||||
if (arrlen(sampler->data) == 0) return (HMM_Vec4){0,0,0,0};
|
||||
if (arrlen(sampler->data) == 1) return sampler->data[0];
|
||||
int previous_time=0;
|
||||
int next_time=0;
|
||||
|
||||
for (int i = 1; i < arrlen(sampler->times); i++) {
|
||||
if (time < sampler->times[i]) {
|
||||
previous_time = i-1;
|
||||
next_time = i;
|
||||
previous_time = sampler->times[i-1];
|
||||
next_time = sampler->times[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,12 @@ struct keyframe {
|
|||
#define CUBICSPLINE 2
|
||||
#define SLERP 3
|
||||
|
||||
typedef struct samplerf {
|
||||
float *times;
|
||||
float *data;
|
||||
int type;
|
||||
} samplerf;
|
||||
|
||||
typedef struct sampler {
|
||||
float *times;
|
||||
HMM_Vec4 *data;
|
||||
|
@ -20,8 +26,6 @@ typedef struct sampler {
|
|||
} sampler;
|
||||
|
||||
struct anim_channel {
|
||||
HMM_Vec4 *target;
|
||||
int comps;
|
||||
sampler *sampler;
|
||||
};
|
||||
|
||||
|
@ -29,10 +33,10 @@ struct animation {
|
|||
char *name;
|
||||
double time;
|
||||
struct anim_channel *channels;
|
||||
sampler *samplers;
|
||||
};
|
||||
|
||||
void animation_run(struct animation *anim, float now);
|
||||
void sampler_add(sampler *s, float time, HMM_Vec4 val);
|
||||
HMM_Vec4 sample_sampler(sampler *sampler, float time);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "config.h"
|
||||
#include "render.h"
|
||||
|
||||
#define SOKOL_TRACE_HOOKS
|
||||
#define SOKOL_IMPL
|
||||
|
@ -7,6 +7,7 @@
|
|||
#include "sokol/sokol_args.h"
|
||||
#include "sokol/sokol_gfx.h"
|
||||
#include "sokol/sokol_app.h"
|
||||
#include "sokol_gfx_ext.h"
|
||||
|
||||
#define MSF_GIF_IMPL
|
||||
#include "msf_gif.h"
|
||||
|
@ -24,11 +25,11 @@
|
|||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_FAILURE_USERMSG
|
||||
#define STBI_NO_STDIO
|
||||
#ifdef __TINYC__
|
||||
#define STBI_NO_SIMD
|
||||
#endif
|
||||
#include "stb_image.h"
|
||||
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include "stb_image_resize2.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_BOX
|
||||
#include "stb_image_write.h"
|
||||
|
@ -40,15 +41,3 @@
|
|||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvg.h"
|
||||
#include "nanosvgrast.h"
|
||||
|
||||
#define CGLTF_IMPLEMENTATION
|
||||
#include "cgltf.h"
|
||||
|
||||
#define PAR_SHAPES_IMPLEMENTATION
|
||||
#include "par/par_shapes.h"
|
||||
|
||||
#define PAR_STREAMLINES_IMPLEMENTATION
|
||||
#include "par/par_streamlines.h"
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
|
|
@ -4,18 +4,12 @@
|
|||
#define MAXPATH 256 /* 255 chars + null */
|
||||
#define MAXNAME 50
|
||||
|
||||
#define SCREEN_WIDTH 1280
|
||||
#define SCREEN_HEIGHT 720
|
||||
#define PI 3.14159265358979323846264338327950288f
|
||||
#define DEG2RADS 0.0174532925199432957692369076848861271344287188854172545609719144f
|
||||
#define RAD2DEGS 57.2958f
|
||||
|
||||
#if defined __linux__
|
||||
#define SOKOL_GLCORE
|
||||
#elif __EMSCRIPTEN__
|
||||
#define SOKOL_WGPU
|
||||
#elif __WIN32
|
||||
#define SOKOL_D3D11
|
||||
#elif __APPLE__
|
||||
#define SOKOL_METAL
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,16 +13,15 @@
|
|||
#include "font.h"
|
||||
#include "render.h"
|
||||
|
||||
#include "mpeg2.sglsl.h"
|
||||
|
||||
#include "cbuf.h"
|
||||
|
||||
#include "sokol/sokol_gfx.h"
|
||||
|
||||
void datastream_free(datastream *ds)
|
||||
{
|
||||
sg_destroy_image(ds->img);
|
||||
plm_destroy(ds->plm);
|
||||
free(ds);
|
||||
}
|
||||
sg_shader vid_shader;
|
||||
sg_pipeline vid_pipeline;
|
||||
sg_bindings vid_bind;
|
||||
|
||||
void soundstream_fillbuf(struct datastream *ds, soundbyte *buf, int frames) {
|
||||
for (int i = 0; i < frames*CHANNELS; i++)
|
||||
|
@ -30,14 +29,17 @@ void soundstream_fillbuf(struct datastream *ds, soundbyte *buf, int frames) {
|
|||
}
|
||||
|
||||
static void render_frame(plm_t *mpeg, plm_frame_t *frame, struct datastream *ds) {
|
||||
if (ds->dirty) return;
|
||||
return;
|
||||
uint8_t rgb[frame->height*frame->width*4];
|
||||
memset(rgb,255,frame->height*frame->width*4);
|
||||
plm_frame_to_rgba(frame, rgb, frame->width*4);
|
||||
sg_image_data imgd = {0};
|
||||
imgd.subimage[0][0] = SG_RANGE(rgb);
|
||||
sg_image_data imgd;
|
||||
sg_range ir = {
|
||||
.ptr = rgb,
|
||||
.size = frame->height*frame->width*4*sizeof(uint8_t)
|
||||
};
|
||||
|
||||
imgd.subimage[0][0] = ir;
|
||||
sg_update_image(ds->img, &imgd);
|
||||
ds->dirty = true;
|
||||
}
|
||||
|
||||
static void render_audio(plm_t *mpeg, plm_samples_t *samples, struct datastream *ds) {
|
||||
|
@ -65,21 +67,16 @@ struct datastream *ds_openvideo(const char *path)
|
|||
plm_get_num_audio_streams(ds->plm),
|
||||
plm_get_duration(ds->plm));
|
||||
|
||||
|
||||
ds->img = sg_make_image(&(sg_image_desc){
|
||||
.width = plm_get_width(ds->plm),
|
||||
.height = plm_get_height(ds->plm),
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.type = SG_IMAGETYPE_2D,
|
||||
.pixel_format = SG_PIXELFORMAT_RGBA8,
|
||||
.height = plm_get_height(ds->plm)
|
||||
});
|
||||
|
||||
plm_set_video_decode_callback(ds->plm, render_frame, ds);
|
||||
|
||||
return ds;
|
||||
|
||||
ds->ring = ringnew(ds->ring, 8192);
|
||||
plugin_node(make_node(ds, soundstream_fillbuf, NULL), masterbus);
|
||||
|
||||
plm_set_video_decode_callback(ds->plm, render_frame, ds);
|
||||
plm_set_audio_decode_callback(ds->plm, render_audio, ds);
|
||||
plm_set_loop(ds->plm, false);
|
||||
|
||||
|
@ -89,12 +86,17 @@ struct datastream *ds_openvideo(const char *path)
|
|||
// Adjust the audio lead time according to the audio_spec buffer size
|
||||
plm_set_audio_lead_time(ds->plm, BUF_FRAMES / SAMPLERATE);
|
||||
|
||||
ds->playing = true;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
void MakeDatastream() {
|
||||
vid_shader = sg_make_shader(mpeg2_shader_desc(sg_query_backend()));
|
||||
}
|
||||
|
||||
void ds_advance(struct datastream *ds, double s) {
|
||||
ds->dirty = false;
|
||||
plm_decode(ds->plm, s);
|
||||
if (ds->playing) plm_decode(ds->plm, s);
|
||||
}
|
||||
|
||||
void ds_seek(struct datastream *ds, double time) {
|
||||
|
@ -108,6 +110,31 @@ void ds_advanceframes(struct datastream *ds, int frames) {
|
|||
}
|
||||
}
|
||||
|
||||
void ds_pause(struct datastream *ds) {
|
||||
ds->playing = false;
|
||||
}
|
||||
|
||||
void ds_stop(struct datastream *ds) {
|
||||
if (ds->plm != NULL) {
|
||||
plm_destroy(ds->plm);
|
||||
ds->plm = NULL;
|
||||
}
|
||||
|
||||
ds->playing = false;
|
||||
}
|
||||
|
||||
// TODO: Must be a better way
|
||||
int ds_videodone(struct datastream *ds) {
|
||||
return (ds->plm == NULL) || plm_get_time(ds->plm) >= plm_get_duration(ds->plm);
|
||||
}
|
||||
|
||||
double ds_remainingtime(struct datastream *ds) {
|
||||
if (ds->plm != NULL)
|
||||
return plm_get_duration(ds->plm) - plm_get_time(ds->plm);
|
||||
else
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
double ds_length(struct datastream *ds) {
|
||||
return plm_get_duration(ds->plm);
|
||||
}
|
||||
|
|
|
@ -11,22 +11,17 @@ struct soundstream;
|
|||
|
||||
struct datastream {
|
||||
plm_t *plm;
|
||||
double last_time;
|
||||
int playing;
|
||||
sg_image img;
|
||||
sg_image y;
|
||||
sg_image cr;
|
||||
sg_image cb;
|
||||
int width;
|
||||
int height;
|
||||
int dirty;
|
||||
soundbyte *ring;
|
||||
};
|
||||
|
||||
typedef struct datastream datastream;
|
||||
|
||||
struct texture;
|
||||
|
||||
void datastream_free(datastream *ds);
|
||||
|
||||
void MakeDatastream();
|
||||
struct datastream *ds_openvideo(const char *path);
|
||||
struct texture *ds_maketexture(struct datastream *);
|
||||
void ds_advance(struct datastream *ds, double);
|
||||
|
|
656
source/engine/debug/debugdraw.c
Normal file
656
source/engine/debug/debugdraw.c
Normal file
|
@ -0,0 +1,656 @@
|
|||
#include "debugdraw.h"
|
||||
|
||||
#include "render.h"
|
||||
#include "yugine.h"
|
||||
#include "log.h"
|
||||
#include <assert.h>
|
||||
#include "window.h"
|
||||
#include "2dphysics.h"
|
||||
#include "stb_ds.h"
|
||||
#include "sokol/sokol_gfx.h"
|
||||
|
||||
#include "point.sglsl.h"
|
||||
#include "poly.sglsl.h"
|
||||
#include "circle.sglsl.h"
|
||||
#include "line.sglsl.h"
|
||||
#include "grid.sglsl.h"
|
||||
|
||||
#include "grid3d.sglsl.h"
|
||||
|
||||
#define PAR_STREAMLINES_IMPLEMENTATION
|
||||
#include "par/par_streamlines.h"
|
||||
|
||||
#include "font.h"
|
||||
|
||||
#define v_amt 500000
|
||||
|
||||
struct flush {
|
||||
sg_shader shader;
|
||||
sg_pipeline pipe;
|
||||
sg_bindings bind;
|
||||
void *verts;
|
||||
int c;
|
||||
int v;
|
||||
int sc;
|
||||
int sv;
|
||||
};
|
||||
typedef struct flush flush;
|
||||
|
||||
static flush fpoint;
|
||||
static flush circle;
|
||||
|
||||
void flushview(flush *f, HMM_Mat4 *view)
|
||||
{
|
||||
sg_apply_pipeline(f->pipe);
|
||||
sg_apply_bindings(&f->bind);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*view));
|
||||
sg_draw(f->sc, f->c, 1);
|
||||
}
|
||||
|
||||
void flushpass(flush *f)
|
||||
{
|
||||
f->sc = f->c;
|
||||
f->c = 0;
|
||||
f->sv = f->v;
|
||||
f->v = 0;
|
||||
}
|
||||
|
||||
static sg_shader point_shader;
|
||||
static sg_pipeline point_pipe;
|
||||
static sg_bindings point_bind;
|
||||
struct point_vertex {
|
||||
struct draw_p3 pos;
|
||||
struct rgba color;
|
||||
float radius;
|
||||
};
|
||||
static int point_c = 0;
|
||||
static int point_sc = 0;
|
||||
|
||||
static sg_shader line_shader;
|
||||
static sg_pipeline line_pipe;
|
||||
static sg_bindings line_bind;
|
||||
struct line_vert {
|
||||
struct draw_p pos;
|
||||
float dist;
|
||||
struct rgba color;
|
||||
float seg_len;
|
||||
float seg_speed;
|
||||
};
|
||||
static int line_c = 0;
|
||||
static int line_v = 0;
|
||||
static int line_sc = 0;
|
||||
static int line_sv = 0;
|
||||
static sg_pipeline grid_pipe;
|
||||
static sg_bindings grid_bind;
|
||||
static sg_shader grid_shader;
|
||||
static int grid_c = 0;
|
||||
|
||||
static sg_pipeline poly_pipe;
|
||||
static sg_bindings poly_bind;
|
||||
static sg_shader poly_shader;
|
||||
static int poly_c = 0;
|
||||
static int poly_v = 0;
|
||||
static int poly_sc = 0;
|
||||
static int poly_sv = 0;
|
||||
struct poly_vertex {
|
||||
struct draw_p pos;
|
||||
float uv[2];
|
||||
struct rgba color;
|
||||
};
|
||||
|
||||
static sg_pipeline circle_pipe;
|
||||
static sg_bindings circle_bind;
|
||||
static sg_shader csg;
|
||||
static int circle_count = 0;
|
||||
static int circle_sc = 0;
|
||||
struct circle_vertex {
|
||||
struct draw_p pos;
|
||||
float radius;
|
||||
struct rgba color;
|
||||
float segsize;
|
||||
float fill;
|
||||
};
|
||||
|
||||
static sg_pipeline g3_pipe;
|
||||
static sg_shader g3_shader;
|
||||
|
||||
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)
|
||||
{
|
||||
if (poly_c != 0) {
|
||||
sg_apply_pipeline(poly_pipe);
|
||||
sg_apply_bindings(&poly_bind);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS,0,SG_RANGE_REF(*view));
|
||||
sg_draw(poly_sc,poly_c,1);
|
||||
}
|
||||
|
||||
if (point_c != 0) {
|
||||
sg_apply_pipeline(point_pipe);
|
||||
sg_apply_bindings(&point_bind);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS,0,SG_RANGE_REF(*view));
|
||||
sg_draw(point_sc,point_c,1);
|
||||
}
|
||||
|
||||
if (line_c != 0) {
|
||||
sg_apply_pipeline(line_pipe);
|
||||
sg_apply_bindings(&line_bind);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS,0,SG_RANGE_REF(*view));
|
||||
lfs_params_t lt;
|
||||
lt.time = apptime();
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_FS,0,SG_RANGE_REF(lt));
|
||||
sg_draw(line_sc,line_c,1);
|
||||
}
|
||||
|
||||
if (circle_count != 0) {
|
||||
sg_apply_pipeline(circle_pipe);
|
||||
sg_apply_bindings(&circle_bind);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*view));
|
||||
sg_draw(circle_sc,4,circle_count);
|
||||
}
|
||||
|
||||
debug_nextpass();
|
||||
}
|
||||
|
||||
void debug_newframe()
|
||||
{
|
||||
point_sc = 0;
|
||||
point_c = 0;
|
||||
circle_sc = circle_count = line_sv = line_v = line_sc = line_c = poly_sc = poly_c = 0;
|
||||
poly_sv = poly_v = 0;
|
||||
|
||||
}
|
||||
|
||||
static sg_shader_uniform_block_desc projection_ubo = {
|
||||
.size = sizeof(useproj),
|
||||
.uniforms = {
|
||||
[0] = { .name = "proj", .type = SG_UNIFORMTYPE_MAT4 },
|
||||
}
|
||||
};
|
||||
|
||||
static sg_shader_uniform_block_desc time_ubo = {
|
||||
.size = sizeof(float),
|
||||
.uniforms = {
|
||||
[0] = { .name = "time", .type = SG_UNIFORMTYPE_FLOAT },
|
||||
}
|
||||
};
|
||||
|
||||
void debugdraw_init()
|
||||
{
|
||||
/*
|
||||
g3_shader = sg_make_shader(grid3d_shader_desc(sg_query_backend()));
|
||||
g3_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = g3_shader,
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
.index_type = SG_INDEXTYPE_UINT32
|
||||
});
|
||||
*/
|
||||
point_shader = sg_make_shader(point_shader_desc(sg_query_backend()));
|
||||
|
||||
point_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = point_shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
|
||||
[1].format = SG_VERTEXFORMAT_UBYTE4N, /* color */
|
||||
[2].format = SG_VERTEXFORMAT_FLOAT /* radius */
|
||||
}
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_POINTS,
|
||||
.colors[0].blend = blend_trans,
|
||||
.label = "dbg point",
|
||||
});
|
||||
|
||||
point_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct point_vertex)*v_amt,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "point vertex buffer"
|
||||
});
|
||||
|
||||
line_shader = sg_make_shader(line_shader_desc(sg_query_backend()));
|
||||
|
||||
line_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = line_shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT3, /* 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 */
|
||||
}
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_LINES,
|
||||
.index_type = SG_INDEXTYPE_UINT16,
|
||||
.colors[0].blend = blend_trans,
|
||||
.label = "dbg line",
|
||||
});
|
||||
|
||||
line_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct line_vert)*v_amt,
|
||||
.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,
|
||||
.label = "line index buffer",
|
||||
});
|
||||
|
||||
csg = sg_make_shader(circle_shader_desc(sg_query_backend()));
|
||||
circle_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.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
|
||||
},
|
||||
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE,
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
.cull_mode = SG_CULLMODE_BACK,
|
||||
.colors[0].blend = blend_trans,
|
||||
.label = "circle pipeline"
|
||||
});
|
||||
|
||||
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] = {
|
||||
-1,-1,
|
||||
-1,1,
|
||||
1,-1,
|
||||
1,1
|
||||
};
|
||||
|
||||
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()));
|
||||
|
||||
grid_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = grid_shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
|
||||
}
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
.cull_mode = SG_CULLMODE_BACK,
|
||||
.label = "grid pipeline",
|
||||
.colors[0].blend = blend_trans,
|
||||
});
|
||||
|
||||
grid_bind.vertex_buffers[0] = circle_bind.vertex_buffers[1];
|
||||
|
||||
poly_shader = sg_make_shader(poly_shader_desc(sg_query_backend()));
|
||||
poly_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = poly_shader,
|
||||
.layout = {
|
||||
.attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, /* pos */
|
||||
[1].format = SG_VERTEXFORMAT_FLOAT2, /* uv */
|
||||
[2].format = SG_VERTEXFORMAT_UBYTE4N /* color rgba */
|
||||
}
|
||||
},
|
||||
.index_type = SG_INDEXTYPE_UINT32,
|
||||
.colors[0].blend = blend_trans,
|
||||
// .cull_mode = SG_CULLMODE_FRONT,
|
||||
.label = "dbg poly",
|
||||
});
|
||||
poly_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.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,
|
||||
.label = "poly index buffer"
|
||||
});
|
||||
}
|
||||
|
||||
void draw_line3d(HMM_Vec3 *points, int n, struct rgba color, float seg_len, float seg_speed)
|
||||
{
|
||||
if (n < 2) return;
|
||||
|
||||
seg_speed = 1;
|
||||
|
||||
struct line_vert v[n];
|
||||
float dist = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
v[i].pos.x = points[i].x;
|
||||
v[i].pos.y = points[i].y;
|
||||
v[i].color = color;
|
||||
v[i].seg_len = seg_len;
|
||||
v[i].seg_speed = seg_speed;
|
||||
}
|
||||
|
||||
v[0].dist = 0;
|
||||
for (int i = 1; i < n; i++) {
|
||||
dist += HMM_DistV3(points[i-1], points[i]);
|
||||
v[i].dist = dist;
|
||||
}
|
||||
|
||||
int i_c = (n-1)*2;
|
||||
|
||||
uint16_t idxs[i_c];
|
||||
for (int i = 0, d = 0; i < n-1; i++, d+=2) {
|
||||
idxs[d] = i + line_v + line_sv;
|
||||
idxs[d+1] = idxs[d]+1;
|
||||
}
|
||||
|
||||
sg_range vr = {
|
||||
.ptr = v,
|
||||
.size = sizeof(struct line_vert)*n
|
||||
};
|
||||
|
||||
sg_range ir = {
|
||||
.ptr = idxs,
|
||||
.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);
|
||||
|
||||
line_c += i_c;
|
||||
line_v += n;
|
||||
}
|
||||
|
||||
void draw_line(HMM_Vec2 *points, int n, struct rgba color, float seg_len, float seg_speed)
|
||||
{
|
||||
if (n < 2) return;
|
||||
|
||||
HMM_Vec3 points3[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
points3[i].xy = points[i];
|
||||
|
||||
draw_line3d(points3, n, color, seg_len, seg_speed);
|
||||
}
|
||||
|
||||
HMM_Vec2 center_of_vects(HMM_Vec2 *v, int n)
|
||||
{
|
||||
HMM_Vec2 c;
|
||||
for (int i = 0; i < n; i++) {
|
||||
c.x += v[i].x;
|
||||
c.y += v[i].y;
|
||||
}
|
||||
|
||||
c.x /= n;
|
||||
c.y /= n;
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Given a series of points p, computes a new series with them expanded on either side by d */
|
||||
HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n)
|
||||
{
|
||||
if (d == 0) {
|
||||
HMM_Vec2 *ret = NULL;
|
||||
arraddn(ret,n);
|
||||
for (int i = 0; i < n; i++)
|
||||
ret[i] = p[i];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
parsl_position par_v[n];
|
||||
uint16_t spine_lens[] = {n};
|
||||
for (int i = 0; i < n; i++) {
|
||||
par_v[i].x = p[i].x;
|
||||
par_v[i].y = p[i].y;
|
||||
};
|
||||
|
||||
parsl_context *par_ctx = parsl_create_context((parsl_config){
|
||||
.thickness = d,
|
||||
.flags= PARSL_FLAG_ANNOTATIONS,
|
||||
.u_mode = PAR_U_MODE_DISTANCE
|
||||
});
|
||||
|
||||
parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){
|
||||
.num_vertices = n,
|
||||
.num_spines = 1,
|
||||
.vertices = par_v,
|
||||
.spine_lengths = spine_lens,
|
||||
.closed = 0,
|
||||
});
|
||||
|
||||
HMM_Vec2 *ret = NULL;
|
||||
arraddn(ret,mesh->num_vertices);
|
||||
for (int i = 0; i < mesh->num_vertices; i++) {
|
||||
ret[i].x = mesh->positions[i].x;
|
||||
ret[i].y = mesh->positions[i].y;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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, float thickness, int flags, struct rgba line_color, float line_seg)
|
||||
{
|
||||
int closed = 0;
|
||||
if (thickness <= 0) {
|
||||
draw_line(points,n,line_color,0,0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* todo: should be dashed, and filled. use a texture. */
|
||||
/* draw polygon outline */
|
||||
if (HMM_EqV2(points[0], points[n-1])) {
|
||||
closed = true;
|
||||
n--;
|
||||
}
|
||||
|
||||
parsl_position par_v[n];
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
par_v[i].x = points[i].x;
|
||||
par_v[i].y = points[i].y;
|
||||
}
|
||||
|
||||
uint16_t spine_lens[] = {n};
|
||||
|
||||
parsl_context *par_ctx = parsl_create_context((parsl_config){
|
||||
.thickness = thickness,
|
||||
.flags = PARSL_FLAG_ANNOTATIONS,
|
||||
});
|
||||
|
||||
parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){
|
||||
.num_vertices = n,
|
||||
.num_spines = 1,
|
||||
.vertices = par_v,
|
||||
.spine_lengths = spine_lens,
|
||||
.closed = closed
|
||||
});
|
||||
|
||||
for (int i = 0; i < mesh->num_triangles*3; i++)
|
||||
mesh->triangle_indices[i] += (poly_v+poly_sv);
|
||||
|
||||
struct poly_vertex vertices[mesh->num_vertices];
|
||||
|
||||
for (int i = 0; i < mesh->num_vertices; i++) {
|
||||
vertices[i].pos = (struct draw_p){ .x = mesh->positions[i].x, .y = mesh->positions[i].y };
|
||||
vertices[i].uv[0] = mesh->annotations[i].u_along_curve;
|
||||
vertices[i].uv[1] = mesh->annotations[i].v_across_curve;
|
||||
vertices[i].color = color;
|
||||
}
|
||||
|
||||
sg_append_buffer(poly_bind.vertex_buffers[0], &(sg_range){.ptr = vertices, .size = sizeof(struct poly_vertex)*mesh->num_vertices});
|
||||
sg_append_buffer(poly_bind.index_buffer, &(sg_range){.ptr = mesh->triangle_indices, sizeof(uint32_t)*mesh->num_triangles*3});
|
||||
|
||||
poly_c += mesh->num_triangles*3;
|
||||
poly_v += mesh->num_vertices;
|
||||
|
||||
parsl_destroy_context(par_ctx);
|
||||
|
||||
/* Now drawing the line outlines */
|
||||
if (thickness == 1) {
|
||||
draw_line(points,n,line_color,line_seg, 0);
|
||||
} else {
|
||||
HMM_Vec2 in_p[n];
|
||||
HMM_Vec2 out_p[n];
|
||||
|
||||
for (int i = 1, v = 0; i < n*2+1; i+=2, v++) {
|
||||
in_p[v].x = vertices[i].pos.x;
|
||||
in_p[v].y = vertices[i].pos.y;
|
||||
}
|
||||
|
||||
for (int i = 0, v = 0; i < n*2; i+=2,v++) {
|
||||
out_p[v].x = vertices[i].pos.x;
|
||||
out_p[v].y = vertices[i].pos.y;
|
||||
}
|
||||
|
||||
if (!closed) {
|
||||
HMM_Vec2 p[n*2];
|
||||
for (int i = 0; i < n; i++)
|
||||
p[i] = in_p[i];
|
||||
for (int i = n-1, v = n; i >= 0; i--,v++)
|
||||
p[v] = out_p[i];
|
||||
|
||||
draw_line(p,n*2,line_color,line_seg,0);
|
||||
return;
|
||||
}
|
||||
|
||||
draw_line(in_p,n,line_color,line_seg,0);
|
||||
draw_line(out_p,n,line_color,line_seg,0);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_circle(HMM_Vec2 pos, float radius, float pixels, struct rgba color, float seg)
|
||||
{
|
||||
struct circle_vertex cv;
|
||||
cv.pos.x = pos.x;
|
||||
cv.pos.y = pos.y;
|
||||
cv.radius = radius;
|
||||
cv.color = color;
|
||||
cv.segsize = seg/radius;
|
||||
cv.fill = pixels/radius;
|
||||
sg_append_buffer(circle_bind.vertex_buffers[0], &(sg_range){.ptr = &cv, .size = sizeof(struct circle_vertex)});
|
||||
circle_count++;
|
||||
}
|
||||
|
||||
void draw_box(HMM_Vec2 c, HMM_Vec2 wh, struct rgba color)
|
||||
{
|
||||
float hw = wh.x / 2.f;
|
||||
float hh = wh.y / 2.f;
|
||||
|
||||
HMM_Vec2 verts[4] = {
|
||||
{ .x = c.x-hw, .y = c.y-hh },
|
||||
{ .x = c.x+hw, .y = c.y-hh },
|
||||
{ .x = c.x+hw, .y = c.y+hh },
|
||||
{ .x = c.x-hw, .y = c.y+hh }
|
||||
};
|
||||
|
||||
draw_poly(verts, 4, color);
|
||||
}
|
||||
|
||||
void draw_grid(float width, float span, struct rgba color)
|
||||
{
|
||||
HMM_Vec2 offset = campos;
|
||||
offset = HMM_MulV2F(offset, 1/camzoom);
|
||||
|
||||
float ubo[4];
|
||||
ubo[0] = offset.x;
|
||||
ubo[1] = offset.y;
|
||||
ubo[2] = mainwin.size.x;
|
||||
ubo[3] = mainwin.size.y;
|
||||
|
||||
sg_apply_pipeline(grid_pipe);
|
||||
sg_apply_bindings(&grid_bind);
|
||||
float col[4];
|
||||
rgba2floats(col,color);
|
||||
|
||||
fs_params_t pt;
|
||||
pt.thickness = (float)width;
|
||||
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));
|
||||
sg_draw(0,4,1);
|
||||
}
|
||||
|
||||
void draw_cppoint(HMM_Vec2 point, float r, struct rgba color)
|
||||
{
|
||||
struct point_vertex p = {
|
||||
.color = color,
|
||||
.radius = r
|
||||
};
|
||||
p.pos.x = point.X;
|
||||
p.pos.y = point.Y;
|
||||
sg_append_buffer(point_bind.vertex_buffers[0], &(sg_range){.ptr = &p, .size = sizeof(struct point_vertex)});
|
||||
point_c++;
|
||||
}
|
||||
|
||||
void draw_points(HMM_Vec2 *points, int n, float size, struct rgba color)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
draw_cppoint(points[i], size, color);
|
||||
}
|
||||
|
||||
void draw_poly(HMM_Vec2 *points, int n, struct rgba color)
|
||||
{
|
||||
/* Find polygon mesh */
|
||||
int tric = n - 2;
|
||||
|
||||
if (tric < 1) return;
|
||||
|
||||
uint32_t tridxs[tric*3];
|
||||
|
||||
for (int i = 2, ti = 0; i < n; i++, ti+=3) {
|
||||
tridxs[ti] = 0;
|
||||
tridxs[ti+1] = i-1;
|
||||
tridxs[ti+2] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tric*3; i++)
|
||||
tridxs[i] += poly_v+poly_sv;
|
||||
|
||||
struct poly_vertex polyverts[n];
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
polyverts[i].pos = (struct draw_p) { .x = points[i].x, .y = points[i].y};
|
||||
polyverts[i].uv[0] = 0.0;
|
||||
polyverts[i].uv[1] = 0.0;
|
||||
polyverts[i].color = color;
|
||||
}
|
||||
|
||||
sg_append_buffer(poly_bind.vertex_buffers[0], &(sg_range){.ptr = polyverts, .size = sizeof(struct poly_vertex)*n});
|
||||
sg_append_buffer(poly_bind.index_buffer, &(sg_range){.ptr = tridxs, sizeof(uint32_t)*3*tric});
|
||||
|
||||
poly_c += tric*3;
|
||||
poly_v += n;
|
||||
}
|
27
source/engine/debug/debugdraw.h
Normal file
27
source/engine/debug/debugdraw.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef DEBUGDRAW_H
|
||||
#define DEBUGDRAW_H
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
struct rgba;
|
||||
|
||||
void debugdraw_init();
|
||||
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_line3d(HMM_Vec3 *points, int n, struct rgba color, float seg_len, float seg_speed);
|
||||
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);
|
||||
void draw_box(HMM_Vec2 c, HMM_Vec2 wh, struct rgba color);
|
||||
void draw_poly(HMM_Vec2 *points, int n, struct rgba color);
|
||||
|
||||
void draw_grid(float width, float span, struct rgba color);
|
||||
|
||||
void debug_flush(HMM_Mat4 *view);
|
||||
void debug_newframe();
|
||||
|
||||
HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n);
|
||||
|
||||
#endif
|
|
@ -15,10 +15,6 @@
|
|||
|
||||
#include "script.h"
|
||||
|
||||
#ifdef __WIN32
|
||||
#include "debugapi.h"
|
||||
#endif
|
||||
|
||||
#define ESC "\033["
|
||||
#define BLACK 30
|
||||
#define RED 31
|
||||
|
@ -29,7 +25,7 @@
|
|||
#define CYAN 36
|
||||
#define WHITE 37
|
||||
|
||||
#define COLOR(TXT, _C) ESC "22;" #_C "m" #TXT ESC "0m"
|
||||
#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) };
|
||||
|
@ -47,6 +43,7 @@ void log_init()
|
|||
if (!fexists(".prosperon")) {
|
||||
logout = tmpfile();
|
||||
dump = tmpfile();
|
||||
writeout = stdout;
|
||||
}
|
||||
else {
|
||||
logout = fopen(".prosperon/log.txt", "w");
|
||||
|
@ -107,8 +104,8 @@ void mYughLog(int category, int priority, int line, const char *file, const char
|
|||
void log_print(const char *str)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout, "%s", str);
|
||||
fprintf(writeout, "%s", str);
|
||||
fprintf(stdout, str);
|
||||
fprintf(writeout, str);
|
||||
fflush(stdout);
|
||||
#else
|
||||
printf(str);
|
|
@ -10,6 +10,8 @@
|
|||
#include <string.h>
|
||||
#include <window.h>
|
||||
#include "resources.h"
|
||||
#include "debugdraw.h"
|
||||
#include "text.sglsl.h"
|
||||
#include "render.h"
|
||||
|
||||
#include "stb_image_write.h"
|
||||
|
@ -20,25 +22,64 @@
|
|||
|
||||
struct sFont *use_font;
|
||||
|
||||
sg_buffer text_ssbo;
|
||||
#define max_chars 100000
|
||||
|
||||
static sg_shader fontshader;
|
||||
static sg_bindings bind_text;
|
||||
static sg_pipeline pipe_text;
|
||||
struct text_vert {
|
||||
HMM_Vec2 pos;
|
||||
HMM_Vec2 wh;
|
||||
HMM_Vec2 uv;
|
||||
HMM_Vec2 st;
|
||||
HMM_Vec4 color;
|
||||
struct draw_p pos;
|
||||
struct draw_p wh;
|
||||
struct uv_n uv;
|
||||
struct uv_n st;
|
||||
struct rgba color;
|
||||
};
|
||||
|
||||
static struct text_vert *text_buffer;
|
||||
|
||||
void font_init() {
|
||||
text_ssbo = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct text_vert),
|
||||
.type = SG_BUFFERTYPE_STORAGEBUFFER,
|
||||
text_buffer = malloc(sizeof(*text_buffer)*max_chars);
|
||||
fontshader = sg_make_shader(text_shader_desc(sg_query_backend()));
|
||||
pipe_text = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.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 */
|
||||
},
|
||||
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
.colors[0].blend = blend_trans,
|
||||
.label = "text",
|
||||
});
|
||||
|
||||
float text_verts[8] = {
|
||||
0,0,
|
||||
0,1,
|
||||
1,0,
|
||||
1,1
|
||||
};
|
||||
|
||||
bind_text.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = SG_RANGE(text_verts),
|
||||
.usage = SG_USAGE_IMMUTABLE,
|
||||
.label = "text rectangle buffer",
|
||||
});
|
||||
|
||||
bind_text.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct text_vert)*max_chars,
|
||||
.type = SG_BUFFERTYPE_VERTEXBUFFER,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "text buffer"
|
||||
});
|
||||
|
||||
bind_text.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
|
||||
}
|
||||
|
||||
void font_free(font *f)
|
||||
|
@ -50,6 +91,7 @@ void font_free(font *f)
|
|||
void font_set(font *f)
|
||||
{
|
||||
use_font = f;
|
||||
bind_text.fs.images[0] = f->texID;
|
||||
}
|
||||
|
||||
struct sFont *MakeSDFFont(const char *fontfile, int height)
|
||||
|
@ -112,8 +154,7 @@ struct sFont *MakeFont(const char *fontfile, int height) {
|
|||
newfont->emscale = stbtt_ScaleForPixelHeight(&fontinfo, height);
|
||||
newfont->linegap = (newfont->ascent - newfont->descent) * newfont->emscale*1.5;
|
||||
|
||||
newfont->texture = malloc(sizeof(texture));
|
||||
newfont->texture->id = sg_make_image(&(sg_image_desc){
|
||||
newfont->texID = sg_make_image(&(sg_image_desc){
|
||||
.type = SG_IMAGETYPE_2D,
|
||||
.width = packsize,
|
||||
.height = packsize,
|
||||
|
@ -125,9 +166,6 @@ struct sFont *MakeFont(const char *fontfile, int height) {
|
|||
}
|
||||
});
|
||||
|
||||
newfont->texture->width = packsize;
|
||||
newfont->texture->height = packsize;
|
||||
|
||||
for (unsigned char c = 32; c < 127; c++) {
|
||||
stbtt_packedchar glyph = glyphs[c - 32];
|
||||
|
||||
|
@ -154,6 +192,8 @@ struct sFont *MakeFont(const char *fontfile, int height) {
|
|||
return newfont;
|
||||
}
|
||||
|
||||
static int curchar = 0;
|
||||
|
||||
void draw_underline_cursor(HMM_Vec2 pos, float scale, struct rgba color)
|
||||
{
|
||||
pos.Y -= 2;
|
||||
|
@ -173,31 +213,29 @@ void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba
|
|||
HMM_Vec2 b;
|
||||
b.x = cursor.X + wh.x/2;
|
||||
b.y = cursor.Y + wh.y/2;
|
||||
|
||||
draw_box(b, wh, color);
|
||||
}
|
||||
|
||||
int text_flush() {
|
||||
if (arrlen(text_buffer) == 0) return 0;
|
||||
|
||||
void text_flush(HMM_Mat4 *proj) {
|
||||
if (curchar == 0) return;
|
||||
sg_range verts;
|
||||
verts.ptr = text_buffer;
|
||||
verts.size = sizeof(struct text_vert) * arrlen(text_buffer);
|
||||
if (sg_query_buffer_will_overflow(text_ssbo, verts.size)) {
|
||||
sg_destroy_buffer(text_ssbo);
|
||||
text_ssbo = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = verts.size,
|
||||
.type = SG_BUFFERTYPE_STORAGEBUFFER,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "text buffer"
|
||||
});
|
||||
}
|
||||
verts.size = sizeof(struct text_vert) * curchar;
|
||||
int offset = sg_append_buffer(bind_text.vertex_buffers[0], &verts);
|
||||
|
||||
sg_append_buffer(text_ssbo, &verts);
|
||||
int n = arrlen(text_buffer);
|
||||
arrsetlen(text_buffer, 0);
|
||||
return n;
|
||||
bind_text.vertex_buffer_offsets[0] = offset;
|
||||
sg_apply_pipeline(pipe_text);
|
||||
sg_apply_bindings(&bind_text);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*proj));
|
||||
sg_draw(0, 4, curchar);
|
||||
curchar = 0;
|
||||
}
|
||||
|
||||
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color) {
|
||||
if (curchar-10 >= max_chars)
|
||||
return;
|
||||
|
||||
struct rgba colorbox = {0,0,0,255};
|
||||
|
||||
struct text_vert vert;
|
||||
|
@ -211,13 +249,14 @@ void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgb
|
|||
|
||||
// if (vert.pos.x > frame.l || vert.pos.y > frame.t || (vert.pos.y + vert.wh.y) < frame.b || (vert.pos.x + vert.wh.x) < frame.l) return;
|
||||
|
||||
vert.uv.x = c.rect.x;
|
||||
vert.uv.y = c.rect.y;
|
||||
vert.st.x = c.rect.w;
|
||||
vert.st.y = c.rect.h;
|
||||
rgba2floats(vert.color.e, color);
|
||||
vert.uv.u = c.rect.x*USHRT_MAX;
|
||||
vert.uv.v = c.rect.y*USHRT_MAX;
|
||||
vert.st.u = c.rect.w*USHRT_MAX;
|
||||
vert.st.v = c.rect.h*USHRT_MAX;
|
||||
vert.color = color;
|
||||
|
||||
arrput(text_buffer, vert);
|
||||
memcpy(text_buffer + curchar, &vert, sizeof(struct text_vert));
|
||||
curchar++;
|
||||
}
|
||||
|
||||
const char *esc_color(const char *c, struct rgba *color, struct rgba defc)
|
||||
|
@ -350,7 +389,7 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f
|
|||
if (*wordstart == '\e')
|
||||
wordstart = esc_color(wordstart, &usecolor, color);
|
||||
|
||||
//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], 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 += use_font->Characters[*wordstart].Advance * tracking * scale;
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
struct shader;
|
||||
struct window;
|
||||
|
||||
extern sg_buffer text_ssbo;
|
||||
|
||||
/// Holds all state information relevant to a character as loaded using FreeType
|
||||
struct Character {
|
||||
float Size[2]; // Size of glyph
|
||||
|
@ -28,7 +26,6 @@ struct sFont {
|
|||
float emscale;
|
||||
struct Character Characters[256];
|
||||
sg_image texID;
|
||||
texture *texture;
|
||||
};
|
||||
|
||||
typedef struct sFont font;
|
||||
|
@ -43,6 +40,7 @@ void text_settype(struct sFont *font);
|
|||
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking);
|
||||
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking);
|
||||
|
||||
int text_flush();
|
||||
// void text_frame();
|
||||
void text_flush(HMM_Mat4 *proj);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,37 +2,105 @@
|
|||
|
||||
#include "2dphysics.h"
|
||||
#include <string.h>
|
||||
#include "debugdraw.h"
|
||||
#include "log.h"
|
||||
#include "math.h"
|
||||
|
||||
#include "stb_ds.h"
|
||||
|
||||
transform go2t(gameobject *go)
|
||||
gameobject *body2go(cpBody *body) { return cpBodyGetUserData(body); }
|
||||
gameobject *shape2go(cpShape *shape) {
|
||||
struct phys2d_shape *pshape = cpShapeGetUserData(shape);
|
||||
if (!pshape) return NULL;
|
||||
return pshape->go;
|
||||
}
|
||||
|
||||
HMM_Vec2 go_pos(gameobject *go)
|
||||
{
|
||||
transform t = {0};
|
||||
t.pos.cp = cpBodyGetPosition(go->body);
|
||||
t.rotation = angle2rotation(cpBodyGetAngle(go->body));
|
||||
t.scale = go->t->scale;
|
||||
cpVect p = cpBodyGetPosition(go->body);
|
||||
return (HMM_Vec2){p.x, p.y};
|
||||
}
|
||||
float go_angle(gameobject *go) { return cpBodyGetAngle(go->body); }
|
||||
|
||||
transform3d go2t3(gameobject *go)
|
||||
{
|
||||
transform3d t;
|
||||
HMM_Vec2 p = go_pos(go);
|
||||
t.pos.X = p.X;
|
||||
t.pos.Y = p.Y;
|
||||
t.pos.Z = go->drawlayer;
|
||||
t.scale = go->scale;
|
||||
t.scale.Z = go->scale.X;
|
||||
t.rotation = go->quat;
|
||||
return t;
|
||||
}
|
||||
|
||||
gameobject *body2go(cpBody *b)
|
||||
HMM_Vec2 go2world(gameobject *go, HMM_Vec2 pos) { return mat_t_pos(t_go2world(go), pos); }
|
||||
HMM_Vec2 world2go(gameobject *go, HMM_Vec2 pos) { return mat_t_pos(t_world2go(go), pos); }
|
||||
HMM_Mat3 t_go2world(gameobject *go) { return transform2d2mat(go2t(go)); }
|
||||
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)); }
|
||||
|
||||
transform2d go2t(gameobject *go)
|
||||
{
|
||||
return cpBodyGetUserData(b);
|
||||
transform2d t;
|
||||
t.pos.cp = cpBodyGetPosition(go->body);
|
||||
t.angle = cpBodyGetAngle(go->body);
|
||||
t.scale = go->scale.XY;
|
||||
if (!isfinite(t.scale.X)) t.scale.X = 1;
|
||||
if (!isfinite(t.scale.Y)) t.scale.Y = 1;
|
||||
return t;
|
||||
}
|
||||
|
||||
gameobject *shape2go(cpShape *s)
|
||||
{
|
||||
cpBody *b = cpShapeGetBody(s);
|
||||
return cpBodyGetUserData(b);
|
||||
void go_shape_apply(cpBody *body, cpShape *shape, gameobject *go) {
|
||||
cpShapeSetFriction(shape, go->friction);
|
||||
cpShapeSetElasticity(shape, go->elasticity);
|
||||
cpShapeSetCollisionType(shape, (cpCollisionType)go);
|
||||
|
||||
cpShapeFilter filter;
|
||||
filter.group = (cpCollisionType)go;
|
||||
filter.categories = go->categories;
|
||||
filter.mask = go->mask;
|
||||
// filter.mask = CP_ALL_CATEGORIES;
|
||||
cpShapeSetFilter(shape, filter);
|
||||
|
||||
struct phys2d_shape *ape = cpShapeGetUserData(shape);
|
||||
if (ape && ape->apply)
|
||||
ape->apply(ape->data);
|
||||
}
|
||||
|
||||
void gameobject_apply(gameobject *go) { *go->t = go2t(go); }
|
||||
void go_shape_moi(cpBody *body, cpShape *shape, gameobject *go) {
|
||||
float moment = cpBodyGetMoment(body);
|
||||
struct phys2d_shape *s = cpShapeGetUserData(shape);
|
||||
if (!s) {
|
||||
cpBodySetMoment(body, moment + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
moment += s->moi(s->data);
|
||||
if (moment < 0) moment = 0;
|
||||
cpBodySetMoment(body, moment);
|
||||
}
|
||||
|
||||
void gameobject_apply(gameobject *go) {
|
||||
YughSpam("Applying gameobject %p", go);
|
||||
cpBodySetType(go->body, go->phys);
|
||||
cpBodyEachShape(go->body, go_shape_apply, go);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
|
||||
{
|
||||
gameobject *go = body2go(body);
|
||||
gameobject_apply(go);
|
||||
cpVect pos = cpBodyGetPosition(body);
|
||||
HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, go->warp_mask).xy;
|
||||
if (!go) {
|
||||
|
@ -58,15 +126,23 @@ static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt
|
|||
gameobject *MakeGameobject() {
|
||||
gameobject *ngo = malloc(sizeof(*ngo));
|
||||
gameobject go = {
|
||||
.scale = (HMM_Vec3){1.f,1.f,1.f},
|
||||
.phys = CP_BODY_TYPE_STATIC,
|
||||
.maxvelocity = INFINITY,
|
||||
.maxangularvelocity = INFINITY,
|
||||
.mass = 1.f,
|
||||
.next = -1,
|
||||
.drawlayer = 0,
|
||||
.damping = INFINITY,
|
||||
.timescale = 1.0,
|
||||
.ref = JS_UNDEFINED,
|
||||
.mask = ~0,
|
||||
.categories = 1,
|
||||
.warp_mask = ~0,
|
||||
.quat = HMM_QFromAxisAngle_RH((HMM_Vec3){0,1,0}, 0)
|
||||
};
|
||||
|
||||
go.body = cpSpaceAddBody(space, cpBodyNew(1, 1));
|
||||
go.body = cpSpaceAddBody(space, cpBodyNew(go.mass, 1.f));
|
||||
cpBodySetVelocityUpdateFunc(go.body, velocityFn);
|
||||
|
||||
*ngo = go;
|
||||
|
@ -76,12 +152,26 @@ gameobject *MakeGameobject() {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void rm_body_constraints(cpBody *body, cpConstraint *c, void *data)
|
||||
void rm_body_constraints(cpBody *body, cpConstraint *constraint, void *data)
|
||||
{
|
||||
cpSpaceRemoveConstraint(space, c);
|
||||
constraint_break(cpConstraintGetUserData(constraint));
|
||||
}
|
||||
|
||||
void gameobject_free(gameobject *go) {
|
||||
|
@ -92,3 +182,31 @@ void gameobject_free(gameobject *go) {
|
|||
cpBodyFree(go->body);
|
||||
free(go);
|
||||
}
|
||||
|
||||
void gameobject_setangle(gameobject *go, float angle) {
|
||||
cpBodySetAngle(go->body, angle);
|
||||
phys2d_reindex_body(go->body);
|
||||
}
|
||||
|
||||
void gameobject_setpos(gameobject *go, cpVect vec) {
|
||||
if (!go || !go->body) return;
|
||||
cpBodySetPosition(go->body, vec);
|
||||
phys2d_reindex_body(go->body);
|
||||
}
|
||||
|
||||
void body_draw_shapes_dbg(cpBody *body, cpShape *shape, void *data) {
|
||||
struct phys2d_shape *s = cpShapeGetUserData(shape);
|
||||
s->debugdraw(s->data);
|
||||
}
|
||||
|
||||
HMM_Vec3 go_pos3d(gameobject *go)
|
||||
{
|
||||
HMM_Vec2 pos2d = go_pos(go);
|
||||
return (HMM_Vec3){pos2d.x, pos2d.y, go->drawlayer};
|
||||
}
|
||||
|
||||
void gameobject_draw_debug(gameobject *go) {
|
||||
if (!go || !go->body) return;
|
||||
|
||||
cpBodyEachShape(go->body, body_draw_shapes_dbg, NULL);
|
||||
}
|
||||
|
|
|
@ -27,15 +27,25 @@
|
|||
}while(0)
|
||||
|
||||
struct gameobject {
|
||||
cpBodyType phys;
|
||||
cpBody *body; /* NULL if this object is dead; has 2d position and rotation, relative to global 0 */
|
||||
HMM_Vec3 scale; /* local */
|
||||
HMM_Quat quat;
|
||||
int next;
|
||||
float mass;
|
||||
float friction;
|
||||
float elasticity;
|
||||
float damping;
|
||||
float timescale;
|
||||
float maxvelocity;
|
||||
float maxangularvelocity;
|
||||
unsigned int layer;
|
||||
cpBitmask categories;
|
||||
cpBitmask mask;
|
||||
unsigned int warp_mask;
|
||||
JSValue ref;
|
||||
transform *t; // the transform this body controls
|
||||
HMM_Mat4 world;
|
||||
float drawlayer;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -58,13 +68,31 @@ typedef struct gameobject gameobject;
|
|||
gameobject *MakeGameobject();
|
||||
void gameobject_apply(gameobject *go);
|
||||
void gameobject_free(gameobject *go);
|
||||
transform go2t(gameobject *go);
|
||||
|
||||
HMM_Vec3 go_pos(gameobject *go);
|
||||
transform2d go2t(gameobject *go);
|
||||
transform3d go2t3(gameobject *go);
|
||||
|
||||
gameobject *shape2go(cpShape *s);
|
||||
gameobject *body2go(cpBody *b);
|
||||
HMM_Vec2 go2world(gameobject *go, HMM_Vec2 pos);
|
||||
HMM_Vec2 world2go(gameobject *go, HMM_Vec2 pos);
|
||||
|
||||
HMM_Mat3 t_go2world(gameobject *go);
|
||||
HMM_Mat3 t_world2go(gameobject *go);
|
||||
HMM_Mat4 t3d_go2world(gameobject *go);
|
||||
HMM_Mat4 t3d_world2go(gameobject *go);
|
||||
|
||||
HMM_Vec3 go_pos3d(gameobject *go);
|
||||
|
||||
HMM_Vec2 go_pos(gameobject *go);
|
||||
void gameobject_setpos(gameobject *go, cpVect vec);
|
||||
float go_angle(gameobject *go);
|
||||
void gameobject_setangle(gameobject *go, float angle);
|
||||
|
||||
gameobject *body2go(cpBody *body);
|
||||
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 */
|
||||
|
||||
void gameobject_draw_debug(gameobject *go);
|
||||
#endif
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
#include "gui.h"
|
||||
|
||||
#include "render.h"
|
||||
#include "sokol/sokol_app.h"
|
||||
#include "imgui.h"
|
||||
#define SOKOL_IMPL
|
||||
#include "sokol/util/sokol_imgui.h"
|
||||
#include "sokol/util/sokol_gfx_imgui.h"
|
||||
|
||||
static sgimgui_t sgimgui;
|
||||
|
||||
#include "jsffi.h"
|
||||
|
||||
JSC_CCALL(imgui_begin,
|
||||
char *title = js2strdup(argv[0]);
|
||||
bool active = true;
|
||||
ImGui::Begin(title, &active, ImGuiWindowFlags_MenuBar);
|
||||
free(title);
|
||||
return boolean2js(active);
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_end, ImGui::End())
|
||||
|
||||
JSC_CCALL(imgui_beginmenu,
|
||||
char *title = js2strdup(argv[0]);
|
||||
bool active = ImGui::BeginMenu(title);
|
||||
free(title);
|
||||
return boolean2js(active);
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_menuitem,
|
||||
char *name = js2strdup(argv[0]);
|
||||
char *hotkey = js2strdup(argv[1]);
|
||||
if (ImGui::MenuItem(name,hotkey))
|
||||
script_call_sym(argv[2], 0, NULL);
|
||||
free(name);
|
||||
free(hotkey);
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_beginmenubar, ImGui::BeginMenuBar())
|
||||
JSC_CCALL(imgui_endmenubar, ImGui::EndMenuBar())
|
||||
|
||||
JSC_SSCALL(imgui_textinput,
|
||||
char buffer[512];
|
||||
strncpy(buffer, str2, 512);
|
||||
ImGui::InputText(str, buffer, sizeof(buffer));
|
||||
ret = str2js(buffer);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_imgui_funcs[] = {
|
||||
MIST_FUNC_DEF(imgui, begin, 1),
|
||||
MIST_FUNC_DEF(imgui, end,0),
|
||||
MIST_FUNC_DEF(imgui, beginmenu, 1),
|
||||
MIST_FUNC_DEF(imgui, menuitem, 3),
|
||||
MIST_FUNC_DEF(imgui, beginmenubar, 0),
|
||||
MIST_FUNC_DEF(imgui, endmenubar, 0),
|
||||
MIST_FUNC_DEF(imgui, textinput, 2),
|
||||
};
|
||||
|
||||
static int started = 0;
|
||||
|
||||
JSValue gui_init(JSContext *js)
|
||||
{
|
||||
simgui_desc_t sdesc = {0};
|
||||
simgui_setup(&sdesc);
|
||||
|
||||
sgimgui_desc_t desc = {0};
|
||||
sgimgui_init(&sgimgui, &desc);
|
||||
|
||||
JSValue imgui = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, imgui, js_imgui_funcs, countof(js_imgui_funcs));
|
||||
started = 1;
|
||||
return imgui;
|
||||
}
|
||||
|
||||
void gui_input(sapp_event *e)
|
||||
{
|
||||
if (started)
|
||||
simgui_handle_event(e);
|
||||
}
|
||||
|
||||
void gui_newframe(int x, int y, float dt)
|
||||
{
|
||||
simgui_frame_desc_t frame = {
|
||||
.width = x,
|
||||
.height = y,
|
||||
.delta_time = dt
|
||||
};
|
||||
simgui_new_frame(&frame);
|
||||
}
|
||||
|
||||
void gfx_gui()
|
||||
{
|
||||
sgimgui_draw(&sgimgui);
|
||||
sgimgui_draw_menu(&sgimgui, "sokol-gfx");
|
||||
}
|
||||
|
||||
void gui_endframe()
|
||||
{
|
||||
simgui_render();
|
||||
}
|
||||
|
||||
void gui_exit()
|
||||
{
|
||||
sgimgui_discard(&sgimgui);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include "jsffi.h"
|
||||
#include "sokol/sokol_app.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JSValue gui_init(JSContext *js);
|
||||
void gui_newframe(int x, int y, float dt);
|
||||
void gfx_gui();
|
||||
void gui_input(sapp_event *e);
|
||||
void gui_endframe();
|
||||
void gui_exit();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -44,7 +44,7 @@ static char *touch_jstrn(char *dest, int len, sapp_touchpoint *touch, int n)
|
|||
char touchdest[512] = {0};
|
||||
strncat(dest,"[", 512);
|
||||
for (int i = 0; i < n; i++) {
|
||||
snprintf(touchdest, 512, "{id:%zd, x:%g, y:%g},", touch[i].identifier, touch[i].pos_x, touch[i].pos_y);
|
||||
snprintf(touchdest, 512, "{id:%p, x:%g, y:%g},", touch[i].identifier, touch[i].pos_x, touch[i].pos_y);
|
||||
strncat(dest,touchdest,512);
|
||||
}
|
||||
strncat(dest,"]", 512);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,22 +1,15 @@
|
|||
#ifndef FFI_H
|
||||
#define FFI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "quickjs/quickjs.h"
|
||||
#include "HandmadeMath.h"
|
||||
#include <stdarg.h>
|
||||
#include <chipmunk/chipmunk.h>
|
||||
|
||||
extern JSValue cpShape2js(cpShape *s);
|
||||
|
||||
#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 JS_SETSIG JSContext *js, JSValue self, JSValue val
|
||||
#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]); \
|
||||
|
@ -41,52 +34,52 @@ extern JSValue cpShape2js(cpShape *s);
|
|||
#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 self, int argc, JSValue *argv) { \
|
||||
#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 (JS_SETSIG) { \
|
||||
js2##ID (self)->ENTRY = js2##TYPE (val); \
|
||||
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 self) { \
|
||||
return TYPE##2js(js2##ID (self)->ENTRY); \
|
||||
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 (self));)
|
||||
#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 (JS_SETSIG) { \
|
||||
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 self) { \
|
||||
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 (JS_SETSIG) { \
|
||||
cpBody *b = js2gameobject(self)->body; \
|
||||
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 self) { \
|
||||
cpBody *b = js2gameobject(self)->body; \
|
||||
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 self) { \
|
||||
return TYPE##2js(js2##ID (self)->ENTRY); } \
|
||||
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue this) { \
|
||||
return TYPE##2js(js2##ID (this)->ENTRY); } \
|
||||
|
||||
#define QJSCLASS(TYPE)\
|
||||
static JSClassID js_##TYPE##_id;\
|
||||
|
@ -98,17 +91,14 @@ static JSClassDef js_##TYPE##_class = {\
|
|||
#TYPE,\
|
||||
.finalizer = js_##TYPE##_finalizer,\
|
||||
};\
|
||||
TYPE *js2##TYPE (JSValue val) { \
|
||||
assert(JS_GetClassID(val) == js_##TYPE##_id); \
|
||||
return JS_GetOpaque(val,js_##TYPE##_id); \
|
||||
}\
|
||||
JSValue TYPE##2js(TYPE *n) { \
|
||||
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 self) { return str2js("%p", js2##TYPE(self)); } \
|
||||
static JSValue js_##TYPE##_memid (JSContext *js, JSValue this) { return str2js("%p", js2##TYPE(this)); } \
|
||||
|
||||
#define QJSGLOBALCLASS(NAME) \
|
||||
JSValue NAME = JS_NewObject(js); \
|
||||
|
@ -134,9 +124,6 @@ void ffi_load();
|
|||
JSValue vec22js(HMM_Vec2 v);
|
||||
HMM_Vec2 js2vec2(JSValue v);
|
||||
|
||||
const char *js2str(JSValue v);
|
||||
char *js2strdup(JSValue v);
|
||||
|
||||
JSValue bitmask2js(cpBitmask mask);
|
||||
cpBitmask js2bitmask(JSValue v);
|
||||
int js_print_exception(JSValue v);
|
||||
|
@ -144,29 +131,8 @@ int js_print_exception(JSValue v);
|
|||
struct rgba js2color(JSValue v);
|
||||
double js2number(JSValue v);
|
||||
JSValue number2js(double g);
|
||||
|
||||
uint64_t js2uint64(JSValue v);
|
||||
|
||||
JSValue str2js(const char *c, ...);
|
||||
|
||||
void nota_int(char *blob);
|
||||
|
||||
JSValue js_getpropidx(JSValue v, uint32_t i);
|
||||
JSValue js_getpropstr(JSValue v, const char *str);
|
||||
void jsfreestr(const char *str);
|
||||
int js_arrlen(JSValue v);
|
||||
void js_setpropstr(JSValue v, const char *str, JSValue p);
|
||||
|
||||
int js2boolean(JSValue v);
|
||||
JSValue boolean2js(int b);
|
||||
|
||||
#define PREP_PARENT(TYPE, PARENT) \
|
||||
TYPE##_proto = JS_NewObject(js); \
|
||||
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \
|
||||
JS_SetPrototype(js, TYPE##_proto, PARENT##_proto); \
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,624 +0,0 @@
|
|||
#include "model.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "resources.h"
|
||||
#include "stb_ds.h"
|
||||
#include "gameobject.h"
|
||||
|
||||
#include "render.h"
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
|
||||
#include "math.h"
|
||||
#include "time.h"
|
||||
|
||||
#include <cgltf.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "yugine.h"
|
||||
|
||||
#include "jsffi.h"
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
#include "sokol/sokol_gfx.h"
|
||||
|
||||
static void processnode();
|
||||
static void processmesh();
|
||||
static void processtexture();
|
||||
|
||||
static cgltf_data *cdata;
|
||||
static char *cpath;
|
||||
|
||||
cgltf_attribute *get_attr_type(cgltf_primitive *p, cgltf_attribute_type t)
|
||||
{
|
||||
for (int i = 0; i < p->attributes_count; i++) {
|
||||
if (p->attributes[i].type == t)
|
||||
return &p->attributes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned short pack_short_tex(float c) { return c * USHRT_MAX; }
|
||||
|
||||
|
||||
void mesh_add_material(primitive *prim, cgltf_material *mat)
|
||||
{
|
||||
if (!mat) return;
|
||||
|
||||
prim->mat = calloc(sizeof(*prim->mat), 1);
|
||||
material *pmat = prim->mat;
|
||||
|
||||
if (mat->has_pbr_metallic_roughness && mat->pbr_metallic_roughness.base_color_texture.texture) {
|
||||
cgltf_image *img = mat->pbr_metallic_roughness.base_color_texture.texture->image;
|
||||
if (img->buffer_view) {
|
||||
cgltf_buffer_view *buf = img->buffer_view;
|
||||
pmat->diffuse = texture_fromdata(buf->buffer->data, buf->size);
|
||||
} else {
|
||||
char *path = makepath(dirname(cpath), img->uri);
|
||||
pmat->diffuse = texture_from_file(path);
|
||||
free(path);
|
||||
}
|
||||
} else
|
||||
pmat->diffuse = texture_from_file("icons/moon.gif");
|
||||
}
|
||||
|
||||
sg_buffer texcoord_floats(float *f, int n)
|
||||
{
|
||||
unsigned short packed[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
float v = f[i];
|
||||
if (v < 0) v = 0;
|
||||
if (v > 1) v = 1;
|
||||
packed[i] = pack_short_tex(v);
|
||||
}
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = SG_RANGE(packed),
|
||||
.label = "tex coord vert buffer",
|
||||
});
|
||||
}
|
||||
|
||||
sg_buffer par_idx_buffer(uint32_t *p, int v)
|
||||
{
|
||||
uint16_t idx[v];
|
||||
for (int i = 0; i < v; i++) idx[i] = p[i];
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = SG_RANGE(idx),
|
||||
.type = SG_BUFFERTYPE_INDEXBUFFER
|
||||
});
|
||||
}
|
||||
|
||||
sg_buffer float_buffer(float *f, int v)
|
||||
{
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = (sg_range){
|
||||
.ptr = f,
|
||||
.size = sizeof(*f)*v
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sg_buffer index_buffer(float *f, int verts)
|
||||
{
|
||||
uint16_t idxs[verts];
|
||||
for (int i = 0; i < verts; i++)
|
||||
idxs[i] = f[i];
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = SG_RANGE(idxs),
|
||||
.type = SG_BUFFERTYPE_INDEXBUFFER,
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t pack_int10_n2(float *norm)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int n = (norm[i]+1.0)*511;
|
||||
ret |= (n & 0x3ff) << (10*i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
sg_buffer normal_floats(float *f, int n)
|
||||
{
|
||||
return float_buffer(f, n);
|
||||
uint32_t packed_norms[n/3];
|
||||
for (int v = 0, i = 0; v < n/3; v++, i+= 3)
|
||||
packed_norms[v] = pack_int10_n2(f+i);
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = SG_RANGE(packed_norms),
|
||||
.label = "normal vert buffer",
|
||||
});
|
||||
}
|
||||
|
||||
sg_buffer ubyten_buffer(float *f, int v)
|
||||
{
|
||||
unsigned char b[v];
|
||||
for (int i = 0; i < (v); i++)
|
||||
b[i] = f[i]*255;
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
|
||||
}
|
||||
|
||||
sg_buffer ubyte_buffer(float *f, int v)
|
||||
{
|
||||
unsigned char b[v];
|
||||
for (int i = 0; i < (v); i++)
|
||||
b[i] = f[i];
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
|
||||
}
|
||||
|
||||
sg_buffer joint_buf(float *f, int v)
|
||||
{
|
||||
char joints[v];
|
||||
for (int i = 0; i < (v); i++)
|
||||
joints[i] = f[i];
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(joints)});
|
||||
}
|
||||
|
||||
sg_buffer weight_buf(float *f, int v)
|
||||
{
|
||||
unsigned char weights[v];
|
||||
for (int i = 0; i < (v); i++)
|
||||
weights[i] = f[i]*255;
|
||||
|
||||
return sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(weights)});
|
||||
}
|
||||
|
||||
HMM_Vec3 index_to_vert(uint32_t idx, float *f)
|
||||
{
|
||||
return (HMM_Vec3){f[idx*3], f[idx*3+1], f[idx*3+2]};
|
||||
}
|
||||
|
||||
void primitive_gen_indices(primitive *prim)
|
||||
{
|
||||
if (prim->idx_count != 0) return;
|
||||
|
||||
uint16_t *idxs = malloc(sizeof(*idxs)*prim->idx_count);
|
||||
|
||||
for (int z = 0; z < prim->idx_count; z++)
|
||||
idxs[z] = z;
|
||||
|
||||
prim->index = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = idxs,
|
||||
.data.size = sizeof(uint16_t) * prim->idx_count,
|
||||
.type = SG_BUFFERTYPE_INDEXBUFFER});
|
||||
|
||||
free(idxs);
|
||||
}
|
||||
|
||||
struct primitive mesh_add_primitive(cgltf_primitive *prim)
|
||||
{
|
||||
primitive retp = (primitive){0};
|
||||
|
||||
uint16_t *idxs;
|
||||
if (prim->indices) {
|
||||
int n = cgltf_accessor_unpack_floats(prim->indices, NULL, 0);
|
||||
float fidx[n];
|
||||
cgltf_accessor_unpack_floats(prim->indices, fidx, n);
|
||||
idxs = malloc(sizeof(*idxs)*n);
|
||||
for (int i = 0; i < n; i++)
|
||||
idxs[i] = fidx[i];
|
||||
|
||||
retp.index = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = idxs,
|
||||
.data.size = sizeof(*idxs) * n,
|
||||
.type = SG_BUFFERTYPE_INDEXBUFFER,
|
||||
.label = "mesh index buffer",
|
||||
});
|
||||
|
||||
retp.idx_count = n;
|
||||
free(idxs);
|
||||
} else {
|
||||
retp.idx_count = cgltf_accessor_unpack_floats(prim->attributes[0].data, NULL, 0);
|
||||
primitive_gen_indices(&retp);
|
||||
}
|
||||
|
||||
printf("adding material\n");
|
||||
mesh_add_material(&retp, prim->material);
|
||||
|
||||
for (int k = 0; k < prim->attributes_count; k++) {
|
||||
cgltf_attribute attribute = prim->attributes[k];
|
||||
|
||||
int n = cgltf_accessor_unpack_floats(attribute.data, NULL, 0); /* floats per vertex x num elements. In other words, total floats pulled */
|
||||
int comp = cgltf_num_components(attribute.data->type);
|
||||
int verts = n/comp;
|
||||
float vs[n];
|
||||
cgltf_accessor_unpack_floats(attribute.data, vs, n);
|
||||
|
||||
switch (attribute.type) {
|
||||
case cgltf_attribute_type_position:
|
||||
retp.pos = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = vs,
|
||||
.data.size = sizeof(float) * n,
|
||||
.label = "mesh vert buffer"
|
||||
});
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_normal:
|
||||
retp.norm = normal_floats(vs, n);
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_tangent:
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_color:
|
||||
retp.color = ubyten_buffer(vs,n);
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_weights:
|
||||
retp.weight = ubyten_buffer(vs, n);
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_joints:
|
||||
retp.bone = ubyte_buffer(vs, n);
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_texcoord:
|
||||
retp.uv = texcoord_floats(vs, n);
|
||||
break;
|
||||
case cgltf_attribute_type_invalid:
|
||||
YughWarn("Invalid type.");
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_custom:
|
||||
break;
|
||||
|
||||
case cgltf_attribute_type_max_enum:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!retp.bone.id) {
|
||||
char joints[retp.idx_count*4];
|
||||
memset(joints, 0, retp.idx_count*4);
|
||||
retp.bone = sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(joints)});
|
||||
}
|
||||
|
||||
if (!retp.weight.id) {
|
||||
char weights[retp.idx_count*4];
|
||||
memset(weights,0,retp.idx_count*4);
|
||||
retp.weight = sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(weights)});
|
||||
}
|
||||
|
||||
if (!retp.color.id) {
|
||||
char colors[retp.idx_count*4];
|
||||
memset(colors,0,retp.idx_count*4);
|
||||
retp.color = sg_make_buffer(&(sg_buffer_desc) { .data = SG_RANGE(colors) });
|
||||
}
|
||||
|
||||
if (!retp.norm.id) {
|
||||
YughInfo("Making normals.");
|
||||
cgltf_attribute *pa = get_attr_type(prim, cgltf_attribute_type_position);
|
||||
int n = cgltf_accessor_unpack_floats(pa->data, NULL,0);
|
||||
int comp = 3;
|
||||
int verts = n/comp;
|
||||
uint32_t face_norms[verts];
|
||||
float ps[n];
|
||||
cgltf_accessor_unpack_floats(pa->data,ps,n);
|
||||
|
||||
for (int i = 0; i < verts; i+=3) {
|
||||
HMM_Vec3 a = index_to_vert(i,ps);
|
||||
HMM_Vec3 b = index_to_vert(i+1,ps);
|
||||
HMM_Vec3 c = index_to_vert(i+2,ps);
|
||||
HMM_Vec3 norm = HMM_NormV3(HMM_Cross(HMM_SubV3(b,a), HMM_SubV3(c,a)));
|
||||
uint32_t packed_norm = pack_int10_n2(norm.Elements);
|
||||
face_norms[i] = face_norms[i+1] = face_norms[i+2] = packed_norm;
|
||||
}
|
||||
|
||||
retp.norm = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data.ptr = face_norms,
|
||||
.data.size = sizeof(uint32_t) * verts});
|
||||
}
|
||||
|
||||
return retp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void model_add_cgltf_mesh(mesh *m, cgltf_mesh *gltf_mesh)
|
||||
{
|
||||
for (int i = 0; i < gltf_mesh->primitives_count; i++)
|
||||
arrput(m->primitives, mesh_add_primitive(gltf_mesh->primitives+i));
|
||||
}
|
||||
|
||||
void packFloats(float *src, float *dest, int srcLength) {
|
||||
int i, j;
|
||||
for (i = 0, j = 0; i < srcLength; i += 3, j += 4) {
|
||||
dest[j] = src[i];
|
||||
dest[j + 1] = src[i + 1];
|
||||
dest[j + 2] = src[i + 2];
|
||||
dest[j + 3] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void model_add_cgltf_anim(model *model, cgltf_animation *anim)
|
||||
{
|
||||
YughInfo("FOUND ANIM, using %d channels and %d samplers", anim->channels_count, anim->samplers_count);
|
||||
|
||||
struct animation an = (struct animation){0};
|
||||
arrsetlen(an.samplers, anim->samplers_count);
|
||||
|
||||
for (int i = 0; i < anim->samplers_count; i++) {
|
||||
cgltf_animation_sampler s = anim->samplers[i];
|
||||
sampler samp = (sampler){0};
|
||||
int n = cgltf_accessor_unpack_floats(s.input, NULL, 0);
|
||||
arrsetlen(samp.times, n);
|
||||
cgltf_accessor_unpack_floats(s.input, samp.times, n);
|
||||
|
||||
n = cgltf_accessor_unpack_floats(s.output, NULL, 0);
|
||||
int comp = cgltf_num_components(s.output->type);
|
||||
arrsetlen(samp.data, n/comp);
|
||||
if (comp == 4)
|
||||
cgltf_accessor_unpack_floats(s.output, samp.data, n);
|
||||
else {
|
||||
float *out = malloc(sizeof(*out)*n);
|
||||
cgltf_accessor_unpack_floats(s.output, out, n);
|
||||
packFloats(out, samp.data, n);
|
||||
free(out);
|
||||
}
|
||||
|
||||
samp.type = s.interpolation;
|
||||
|
||||
if (samp.type == LINEAR && comp == 4)
|
||||
samp.type = SLERP;
|
||||
|
||||
an.samplers[i] = samp;
|
||||
}
|
||||
|
||||
for (int i = 0; i < anim->channels_count; i++) {
|
||||
cgltf_animation_channel ch = anim->channels[i];
|
||||
struct anim_channel ach = (struct anim_channel){0};
|
||||
md5joint *md = model->nodes+(ch.target_node-cdata->nodes);
|
||||
switch(ch.target_path) {
|
||||
case cgltf_animation_path_type_translation:
|
||||
ach.target = &md->pos;
|
||||
break;
|
||||
case cgltf_animation_path_type_rotation:
|
||||
ach.target = &md->rot;
|
||||
break;
|
||||
case cgltf_animation_path_type_scale:
|
||||
ach.target = &md->scale;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ach.sampler = an.samplers+(ch.sampler-anim->samplers);
|
||||
|
||||
arrput(an.channels, ach);
|
||||
}
|
||||
|
||||
model->anim = an;
|
||||
model->anim.time = apptime();
|
||||
}
|
||||
|
||||
void model_add_cgltf_skin(model *model, cgltf_skin *skin)
|
||||
{
|
||||
int n = cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, NULL, 0);
|
||||
struct skin sk = (struct skin){0};
|
||||
arrsetlen(sk.invbind, n/16);
|
||||
cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, sk.invbind, n);
|
||||
YughInfo("FOUND SKIN, of %d bones, and %d vert comps", skin->joints_count, n);
|
||||
|
||||
cgltf_node *root = skin->skeleton;
|
||||
|
||||
arrsetlen(sk.joints, skin->joints_count);
|
||||
sk.root = model->nodes+(skin->skeleton-cdata->nodes);
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
sk.binds[i] = MAT1;
|
||||
|
||||
for (int i = 0; i < skin->joints_count; i++) {
|
||||
int offset = skin->joints[i]-cdata->nodes;
|
||||
sk.joints[i] = model->nodes+offset;
|
||||
md5joint *j = sk.joints[i];
|
||||
cgltf_node *n = skin->joints[i];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
j->pos.e[i] = n->translation[i];
|
||||
j->scale.e[i] = n->scale[i];
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
j->rot.e[i] = n->rotation[i];
|
||||
}
|
||||
|
||||
model->skin = sk;
|
||||
}
|
||||
|
||||
void model_process_node(model *model, cgltf_node *node)
|
||||
{
|
||||
int n = node-cdata->nodes;
|
||||
cgltf_node_transform_world(node, model->nodes[n].t.e);
|
||||
model->nodes[n].parent = model->nodes+(node->parent-cdata->nodes);
|
||||
|
||||
if (node->mesh) {
|
||||
int meshn = node->mesh-cdata->meshes;
|
||||
arrsetlen(model->meshes, meshn+1);
|
||||
model->meshes[meshn].m = &model->nodes[n].t;
|
||||
model_add_cgltf_mesh(model->meshes+meshn, node->mesh);
|
||||
}
|
||||
}
|
||||
|
||||
struct model *model_make(const char *path)
|
||||
{
|
||||
YughInfo("Making the model from %s.", path);
|
||||
cpath = path;
|
||||
cgltf_options options = {0};
|
||||
cgltf_data *data = NULL;
|
||||
cgltf_result result = cgltf_parse_file(&options, path, &data);
|
||||
struct model *model = NULL;
|
||||
|
||||
if (result) {
|
||||
YughError("CGLTF could not parse file %s, err %d.", path, result);
|
||||
goto CLEAN;
|
||||
}
|
||||
|
||||
result = cgltf_load_buffers(&options, data, path);
|
||||
|
||||
if (result) {
|
||||
YughError("CGLTF could not load buffers for file %s, err %d.", path, result);
|
||||
goto CLEAN;
|
||||
}
|
||||
|
||||
cdata = data;
|
||||
|
||||
model = calloc(1, sizeof(*model));
|
||||
|
||||
arrsetlen(model->nodes, data->nodes_count);
|
||||
for (int i = 0; i < data->nodes_count; i++)
|
||||
model_process_node(model, data->nodes+i);
|
||||
|
||||
for (int i = 0; i < data->animations_count; i++)
|
||||
model_add_cgltf_anim(model, data->animations+i);
|
||||
|
||||
for (int i = 0; i < data->skins_count; i++)
|
||||
model_add_cgltf_skin(model, data->skins+i);
|
||||
|
||||
CLEAN:
|
||||
cgltf_free(data);
|
||||
return model;
|
||||
}
|
||||
|
||||
void model_free(model *m)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
sg_bindings primitive_bind(primitive *p)
|
||||
{
|
||||
sg_bindings b = {0};
|
||||
b.vertex_buffers[MAT_POS] = p->pos;
|
||||
b.vertex_buffers[MAT_UV] = p->uv;
|
||||
b.vertex_buffers[MAT_NORM] = p->norm;
|
||||
b.vertex_buffers[MAT_BONE] = p->bone;
|
||||
b.vertex_buffers[MAT_WEIGHT] = p->weight;
|
||||
b.vertex_buffers[MAT_COLOR] = p->color;
|
||||
b.index_buffer = p->index;
|
||||
b.fs.images[0] = p->mat->diffuse->id;
|
||||
b.fs.samplers[0] = tex_sampler;
|
||||
return b;
|
||||
}
|
||||
|
||||
void model_draw_go(model *model, transform *go)
|
||||
{
|
||||
HMM_Mat4 gom = transform2mat(*go);
|
||||
|
||||
animation_run(&model->anim, apptime());
|
||||
|
||||
skin *sk = &model->skin;
|
||||
for (int i = 0; i < arrlen(sk->joints); i++) {
|
||||
md5joint *md = sk->joints[i];
|
||||
HMM_Mat4 local = HMM_M4TRS(md->pos.xyz, md->rot, md->scale.xyz);
|
||||
if (md->parent)
|
||||
local = HMM_MulM4(md->parent->t, local);
|
||||
md->t = local;
|
||||
sk->binds[i] = HMM_MulM4(md->t, sk->invbind[i]);
|
||||
}
|
||||
/*sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_skinv, &(sg_range){
|
||||
.ptr = sk->binds,
|
||||
.size = sizeof(*sk->binds)*50
|
||||
});
|
||||
*/
|
||||
|
||||
for (int i = 0; i < arrlen(model->meshes); i++) {
|
||||
HMM_Mat4 mod = *model->meshes[i].m;
|
||||
mod = HMM_MulM4(mod, gom);
|
||||
mesh msh = model->meshes[i];
|
||||
for (int j = 0; j < arrlen(msh.primitives); j++) {
|
||||
sg_bindings b = primitive_bind(msh.primitives+j);
|
||||
sg_apply_bindings(&b);
|
||||
sg_draw(0, msh.primitives[j].idx_count, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mat2type(int mat)
|
||||
{
|
||||
switch(mat) {
|
||||
case MAT_POS:
|
||||
case MAT_NORM:
|
||||
return SG_VERTEXFORMAT_FLOAT3;
|
||||
case MAT_PPOS:
|
||||
case MAT_WH:
|
||||
case MAT_ST:
|
||||
return SG_VERTEXFORMAT_FLOAT2;
|
||||
case MAT_UV:
|
||||
case MAT_TAN:
|
||||
return SG_VERTEXFORMAT_USHORT2N;
|
||||
return SG_VERTEXFORMAT_UINT10_N2;
|
||||
case MAT_BONE:
|
||||
return SG_VERTEXFORMAT_UBYTE4;
|
||||
case MAT_WEIGHT:
|
||||
case MAT_COLOR:
|
||||
return SG_VERTEXFORMAT_UBYTE4N;
|
||||
case MAT_ANGLE:
|
||||
case MAT_SCALE:
|
||||
return SG_VERTEXFORMAT_FLOAT;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mat2step(int mat)
|
||||
{
|
||||
switch(mat) {
|
||||
case MAT_POS:
|
||||
case MAT_UV:
|
||||
case MAT_TAN:
|
||||
case MAT_NORM:
|
||||
case MAT_BONE:
|
||||
case MAT_WEIGHT:
|
||||
return SG_VERTEXSTEP_PER_VERTEX;
|
||||
};
|
||||
return SG_VERTEXSTEP_PER_INSTANCE;
|
||||
}
|
||||
|
||||
sg_buffer mat2buffer(int mat, primitive *p)
|
||||
{
|
||||
switch(mat) {
|
||||
case MAT_POS: return p->pos;
|
||||
case MAT_NORM: return p->norm;
|
||||
case MAT_UV: return p->uv;
|
||||
case MAT_BONE: return p->bone;
|
||||
case MAT_WEIGHT: return p->weight;
|
||||
case MAT_COLOR: return p->color;
|
||||
};
|
||||
|
||||
return p->pos;
|
||||
}
|
||||
|
||||
sg_bindings primitive_bindings(primitive *p, JSValue v)
|
||||
{
|
||||
sg_bindings b = {0};
|
||||
JSValue inputs = js_getpropstr(js_getpropstr(v, "vs"), "inputs");
|
||||
for (int i = 0; i < js_arrlen(inputs); i++) {
|
||||
JSValue attr = js_getpropidx(inputs, i);
|
||||
int mat = js2number(js_getpropstr(attr, "mat"));
|
||||
int slot = js2number(js_getpropstr(attr, "slot"));
|
||||
sg_buffer buf = mat2buffer(mat,p);
|
||||
if (!buf.id) {
|
||||
// ERROR
|
||||
}
|
||||
b.vertex_buffers[slot] = buf;
|
||||
}
|
||||
|
||||
b.index_buffer = p->index;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void primitive_free(primitive *prim)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void material_free(material *mat)
|
||||
{
|
||||
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
#ifndef MODEL_H
|
||||
#define MODEL_H
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
#include "transform.h"
|
||||
#include "sokol/sokol_gfx.h"
|
||||
#include "gameobject.h"
|
||||
#include "anim.h"
|
||||
#include "texture.h"
|
||||
|
||||
#define MAT_POS 0
|
||||
#define MAT_UV 1
|
||||
#define MAT_NORM 2
|
||||
#define MAT_BONE 3
|
||||
#define MAT_WEIGHT 4
|
||||
#define MAT_COLOR 5
|
||||
#define MAT_TAN 6
|
||||
#define MAT_ANGLE 7
|
||||
#define MAT_WH 8
|
||||
#define MAT_ST 9
|
||||
#define MAT_PPOS 10
|
||||
#define MAT_SCALE 11
|
||||
|
||||
typedef struct material {
|
||||
struct texture *diffuse;
|
||||
struct texture *metalrough;
|
||||
float metal;
|
||||
float rough;
|
||||
struct texture *normal;
|
||||
float nrm;
|
||||
struct texture *occlusion;
|
||||
float occl;
|
||||
struct texture *emissive;
|
||||
HMM_Vec3 emis;
|
||||
} material;
|
||||
|
||||
struct model;
|
||||
|
||||
typedef struct primitive {
|
||||
sg_buffer pos;
|
||||
sg_buffer norm;
|
||||
sg_buffer uv;
|
||||
sg_buffer bone;
|
||||
sg_buffer weight;
|
||||
sg_buffer color;
|
||||
sg_buffer index;
|
||||
material *mat;
|
||||
uint32_t idx_count;
|
||||
} primitive;
|
||||
|
||||
/* A single mesh */
|
||||
typedef struct mesh {
|
||||
primitive *primitives;
|
||||
HMM_Mat4 *m;
|
||||
} mesh;
|
||||
|
||||
typedef struct joint {
|
||||
int me;
|
||||
struct joint *children;
|
||||
} joint_t;
|
||||
|
||||
typedef struct md5joint {
|
||||
struct md5joint *parent;
|
||||
HMM_Vec4 pos;
|
||||
HMM_Quat rot;
|
||||
HMM_Vec4 scale;
|
||||
HMM_Mat4 t;
|
||||
} md5joint;
|
||||
|
||||
typedef struct skin {
|
||||
md5joint **joints;
|
||||
HMM_Mat4 *invbind;
|
||||
HMM_Mat4 binds[50]; /* binds = joint * invbind */
|
||||
md5joint *root;
|
||||
} skin;
|
||||
|
||||
/* A collection of meshes which create a full figure */
|
||||
typedef struct model {
|
||||
struct mesh *meshes;
|
||||
md5joint *nodes;
|
||||
material *mats;
|
||||
skin skin;
|
||||
struct animation anim;
|
||||
} model;
|
||||
|
||||
/* Make a Model struct */
|
||||
struct model *model_make(const char *path);
|
||||
void model_free(model *m);
|
||||
|
||||
void model_draw_go(model *m, transform *go);
|
||||
sg_bindings primitive_bindings(primitive *p, JSValue pipe);
|
||||
void primitive_gen_indices(primitive *prim);
|
||||
int mat2type(int mat);
|
||||
|
||||
sg_buffer float_buffer(float *f, int v);
|
||||
sg_buffer index_buffer(float *f, int verts);
|
||||
sg_buffer texcoord_floats(float *f, int n);
|
||||
sg_buffer par_idx_buffer(uint32_t *i, int v);
|
||||
sg_buffer normal_floats(float *f, int n);
|
||||
sg_buffer ubyten_buffer(float *f, int v);
|
||||
sg_buffer ubyte_buffer(float *f, int v);
|
||||
sg_buffer joint_buf(float *f, int v);
|
||||
sg_buffer weight_buf(float *f, int v);
|
||||
void primitive_free(primitive *prim);
|
||||
|
||||
material *material_make();
|
||||
void material_free(material *mat);
|
||||
|
||||
#endif
|
|
@ -1,37 +1,124 @@
|
|||
#include "particle.h"
|
||||
#include "stb_ds.h"
|
||||
#include "render.h"
|
||||
#include "particle.sglsl.h"
|
||||
#include "2dphysics.h"
|
||||
#include "math.h"
|
||||
#include "log.h"
|
||||
#include "simplex.h"
|
||||
#include "pthread.h"
|
||||
#include "math.h"
|
||||
|
||||
#define SCHED_IMPLEMENTATION
|
||||
#include "sched.h"
|
||||
|
||||
static emitter **emitters;
|
||||
|
||||
static sg_shader par_shader;
|
||||
static sg_pipeline par_pipe;
|
||||
static sg_bindings par_bind;
|
||||
static int draw_count;
|
||||
|
||||
#define MAX_PARTICLES 1000000
|
||||
|
||||
struct scheduler sched;
|
||||
void *mem;
|
||||
|
||||
struct par_vert {
|
||||
HMM_Vec2 pos;
|
||||
float angle;
|
||||
HMM_Vec2 scale;
|
||||
struct rgba color;
|
||||
};
|
||||
|
||||
typedef struct par_vert par_vert;
|
||||
|
||||
void particle_init()
|
||||
{
|
||||
sched_size needed;
|
||||
scheduler_init(&sched, &needed, 1, NULL);
|
||||
mem = calloc(needed, 1);
|
||||
scheduler_start(&sched,mem);
|
||||
|
||||
par_shader = sg_make_shader(particle_shader_desc(sg_query_backend()));
|
||||
|
||||
par_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = par_shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[1].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[2].format = SG_VERTEXFORMAT_FLOAT,
|
||||
[3].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[4].format = SG_VERTEXFORMAT_UBYTE4N,
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[0].buffer_index = 1
|
||||
},
|
||||
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE,
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
.label = "particle pipeline",
|
||||
.cull_mode = SG_CULLMODE_BACK,
|
||||
.colors[0].blend = blend_trans,
|
||||
.depth = {
|
||||
.write_enabled = true,
|
||||
.compare = SG_COMPAREFUNC_LESS_EQUAL
|
||||
}
|
||||
});
|
||||
|
||||
par_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(par_vert)*MAX_PARTICLES,
|
||||
.type = SG_BUFFERTYPE_VERTEXBUFFER,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "particle buffer"
|
||||
});
|
||||
|
||||
float circleverts[8] = {
|
||||
0,0,
|
||||
0,1,
|
||||
1,0,
|
||||
1,1,
|
||||
};
|
||||
|
||||
par_bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = (sg_range){.ptr = circleverts, .size = sizeof(float)*8},
|
||||
.usage = SG_USAGE_IMMUTABLE,
|
||||
.label = "particle quater buffer"
|
||||
});
|
||||
|
||||
par_bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
|
||||
}
|
||||
|
||||
emitter *make_emitter() {
|
||||
emitter *e = calloc(sizeof(*e),1);
|
||||
|
||||
e->max = 20;
|
||||
arrsetcap(e->particles, 10);
|
||||
arrsetcap(e->particles, e->max);
|
||||
for (int i = 0; i < arrlen(e->particles); i++)
|
||||
e->particles[i].life = 0;
|
||||
|
||||
e->life = 10;
|
||||
e->tte = lerp(e->explosiveness, e->life/e->max, 0);
|
||||
sampler_add(&e->color, 0, (HMM_Vec4){1,1,1,1});
|
||||
e->scale = 1;
|
||||
e->speed = 20;
|
||||
e->buffer = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct par_vert),
|
||||
.type = SG_BUFFERTYPE_STORAGEBUFFER,
|
||||
.usage = SG_USAGE_STREAM
|
||||
});
|
||||
e->texture = NULL;
|
||||
arrpush(emitters,e);
|
||||
return e;
|
||||
}
|
||||
|
||||
void emitter_free(emitter *e)
|
||||
{
|
||||
YughWarn("kill emitter");
|
||||
arrfree(e->particles);
|
||||
arrfree(e->verts);
|
||||
free(e);
|
||||
for (int i = arrlen(emitters)-1; i >= 0; i--)
|
||||
if (emitters[i] == e) {
|
||||
arrdelswap(emitters,i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void start_emitter(emitter *e) { e->on = 1; }
|
||||
void stop_emitter(emitter *e) { e->on = 0; }
|
||||
|
||||
/* Variate a value around variance. Variance between 0 and 1. */
|
||||
|
||||
float variate(float val, float variance)
|
||||
|
@ -39,69 +126,84 @@ float variate(float val, float variance)
|
|||
return val + val*(frand(variance)-(variance/2));
|
||||
}
|
||||
|
||||
int emitter_spawn(emitter *e, transform *t)
|
||||
int emitter_spawn(emitter *e)
|
||||
{
|
||||
if (arrlen(e->particles) == e->max) return 0;
|
||||
particle p = {0};
|
||||
particle p;
|
||||
p.life = e->life;
|
||||
p.pos = (HMM_Vec4){t->pos.x,t->pos.y,t->pos.z,0};
|
||||
HMM_Vec3 up = transform_direction(t, vFWD);
|
||||
float newan = (frand(e->divergence)-(e->divergence/2))*HMM_TurnToRad;
|
||||
HMM_Vec2 v2n = HMM_V2Rotate((HMM_Vec2){0,1}, newan);
|
||||
HMM_Vec3 norm = (HMM_Vec3){v2n.x, v2n.y,0};
|
||||
p.v = HMM_MulV4F((HMM_Vec4){norm.x,norm.y,norm.z,0}, variate(e->speed, e->variation));
|
||||
p.angle = 0.25;
|
||||
p.scale = variate(e->scale*t->scale.x, e->scale_var);
|
||||
p.pos = (HMM_Vec4){e->t.pos.x,e->t.pos.y,0,0};
|
||||
float newan = e->t.rotation.Elements[0]+(2*HMM_PI*(frand(e->divergence)-(e->divergence/2)));
|
||||
HMM_Vec2 norm = HMM_V2Rotate((HMM_Vec2){0,1}, newan);
|
||||
p.v = HMM_MulV4F((HMM_Vec4){norm.x,norm.y,0,0}, variate(e->speed, e->variation));
|
||||
p.angle = 0;
|
||||
p.scale = variate(e->scale, e->scale_var);
|
||||
// p.av = 1;
|
||||
arrput(e->particles,p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void emitter_emit(emitter *e, int count, transform *t)
|
||||
void emitter_emit(emitter *e, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
emitter_spawn(e, t);
|
||||
emitter_spawn(e);
|
||||
}
|
||||
|
||||
void emitter_draw(emitter *e)
|
||||
void emitters_step(double dt)
|
||||
{
|
||||
if (arrlen(e->particles) == 0) return;
|
||||
arrsetlen(e->verts, arrlen(e->particles));
|
||||
for (int i = 0; i < arrlen(e->particles); i++) {
|
||||
if (e->particles[i].time >= e->particles[i].life) continue;
|
||||
particle *p = e->particles+i;
|
||||
e->verts[i].pos = p->pos.xy;
|
||||
e->verts[i].angle = p->angle;
|
||||
e->verts[i].scale = p->scale;
|
||||
/* if (p->time < e->grow_for)
|
||||
e->verts[i].scale = lerp(p->time/e->grow_for, 0, p->scale);
|
||||
else if (p->time > (p->life - e->shrink_for))
|
||||
e->verts[i].scale = lerp((p->time-(p->life-e->shrink_for))/e->shrink_for, p->scale, 0);*/
|
||||
e->verts[i].color = p->color;
|
||||
}
|
||||
|
||||
sg_range verts;
|
||||
verts.ptr = e->verts;
|
||||
verts.size = sizeof(*e->verts)*arrlen(e->verts);
|
||||
if (sg_query_buffer_will_overflow(e->buffer, verts.size)) {
|
||||
sg_destroy_buffer(e->buffer);
|
||||
e->buffer = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = verts.size,
|
||||
.type = SG_BUFFERTYPE_STORAGEBUFFER,
|
||||
.usage = SG_USAGE_STREAM
|
||||
});
|
||||
}
|
||||
|
||||
sg_append_buffer(e->buffer, &verts);
|
||||
for (int i = 0; i < arrlen(emitters); i++)
|
||||
emitter_step(emitters[i], dt);
|
||||
}
|
||||
|
||||
void emitter_step(emitter *e, double dt, transform *t) {
|
||||
HMM_Vec4 g_accel = HMM_MulV4F((HMM_Vec4){cpSpaceGetGravity(space).x, cpSpaceGetGravity(space).y, 0, 0}, dt);
|
||||
static struct par_vert pv[MAX_PARTICLES];
|
||||
|
||||
for (int i = 0; i < arrlen(e->particles); i++) {
|
||||
void parallel_pv(emitter *e, struct scheduler *sched, struct sched_task_partition t, sched_uint thread_num)
|
||||
{
|
||||
for (int i=t.start; i < t.end; i++) {
|
||||
if (e->particles[i].time >= e->particles[i].life) continue;
|
||||
particle *p = &e->particles[i];
|
||||
pv[i].pos = p->pos.xy;
|
||||
pv[i].angle = p->angle;
|
||||
float s = p->scale;
|
||||
if (p->time < e->grow_for)
|
||||
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((HMM_Vec2){e->texture->width,e->texture->height}, s);
|
||||
pv[i].color = vec2rgba(p->color);
|
||||
}
|
||||
}
|
||||
|
||||
void emitters_draw(HMM_Mat4 *proj)
|
||||
{
|
||||
if (arrlen(emitters) == 0) return;
|
||||
int draw_count = 0;
|
||||
for (int i = 0; i < arrlen(emitters); i++) {
|
||||
emitter *e = emitters[i];
|
||||
par_bind.fs.images[0] = e->texture->id;
|
||||
|
||||
struct sched_task task;
|
||||
scheduler_add(&sched, &task, parallel_pv, e, arrlen(e->particles), arrlen(e->particles)/sched.threads_num);
|
||||
scheduler_join(&sched, &task);
|
||||
|
||||
sg_append_buffer(par_bind.vertex_buffers[0], &(sg_range){.ptr=&pv, .size=sizeof(struct par_vert)*arrlen(e->particles)});
|
||||
draw_count += arrlen(e->particles);
|
||||
}
|
||||
|
||||
sg_apply_pipeline(par_pipe);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*proj));
|
||||
sg_apply_bindings(&par_bind);
|
||||
sg_draw(0, 4, draw_count);
|
||||
}
|
||||
|
||||
static double dt;
|
||||
static HMM_Vec4 g_accel;
|
||||
|
||||
void parallel_step(emitter *e, struct scheduler *shed, struct sched_task_partition t, sched_uint thread_num)
|
||||
{
|
||||
for (int i = t.end-1; i >=0; i--) {
|
||||
if (e->particles[i].time >= e->particles[i].life) continue;
|
||||
|
||||
//if (e->warp_mask & gravmask)
|
||||
// e->particles[i].v = HMM_AddV4(e->particles[i].v, g_accel);
|
||||
if (e->warp_mask & gravmask)
|
||||
e->particles[i].v = HMM_AddV4(e->particles[i].v, g_accel);
|
||||
|
||||
e->particles[i].pos = HMM_AddV4(e->particles[i].pos, HMM_MulV4F(e->particles[i].v, dt));
|
||||
e->particles[i].angle += e->particles[i].av*dt;
|
||||
|
@ -111,14 +213,23 @@ void emitter_step(emitter *e, double dt, transform *t) {
|
|||
|
||||
if (e->particles[i].time >= e->particles[i].life)
|
||||
arrdelswap(e->particles, i);
|
||||
// else if (query_point(e->particles[i].pos.xy))
|
||||
// arrdelswap(e->particles,i);
|
||||
}
|
||||
|
||||
e->tte-=dt;
|
||||
float step = lerp(e->explosiveness, e->life/e->max,0);
|
||||
while (e->tte <= 0) {
|
||||
e->tte += step;
|
||||
if (!emitter_spawn(e, t)) break;
|
||||
else if (query_point(e->particles[i].pos.xy))
|
||||
arrdelswap(e->particles,i);
|
||||
}
|
||||
}
|
||||
|
||||
void emitter_step(emitter *e, double mdt) {
|
||||
dt = mdt;
|
||||
g_accel = HMM_MulV4F((HMM_Vec4){cpSpaceGetGravity(space).x, cpSpaceGetGravity(space).y, 0, 0}, dt);
|
||||
if (arrlen(e->particles) == 0) return;
|
||||
struct sched_task task;
|
||||
scheduler_add(&sched, &task, parallel_step, e, arrlen(e->particles), arrlen(e->particles)/sched.threads_num);
|
||||
scheduler_join(&sched, &task);
|
||||
|
||||
if (!e->on) return;
|
||||
e->tte-=dt;
|
||||
if (e->tte <= 0) {
|
||||
emitter_spawn(e);
|
||||
e->tte = lerp(e->explosiveness, e->life/e->max,0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "texture.h"
|
||||
#include "anim.h"
|
||||
#include "gameobject.h"
|
||||
#include "render.h"
|
||||
|
||||
typedef struct particle {
|
||||
HMM_Vec4 pos;
|
||||
|
@ -24,16 +23,10 @@ typedef struct particle {
|
|||
#define CLOUD 1
|
||||
#define MESH 2
|
||||
|
||||
typedef struct par_vert {
|
||||
HMM_Vec2 pos;
|
||||
float angle;
|
||||
float scale;
|
||||
HMM_Vec4 color;
|
||||
} par_vert;
|
||||
|
||||
typedef struct emitter {
|
||||
struct particle *particles;
|
||||
par_vert *verts;
|
||||
transform3d t;
|
||||
gameobject *go;
|
||||
HMM_Vec3 *mesh; /* list of points to optionally spawn from */
|
||||
HMM_Vec3 *norm; /* norm at each point */
|
||||
int type; /* spray, cloud, or mesh */
|
||||
|
@ -52,6 +45,8 @@ typedef struct emitter {
|
|||
float scale_var;
|
||||
float grow_for; /* seconds to grow from small until scale */
|
||||
float shrink_for; /* seconds to shrink to small prior to its death */
|
||||
/* PARTICLE TYPE */
|
||||
texture *texture;
|
||||
/* ROTATION AND COLLISION */
|
||||
int collision_mask; /* mask for collision */
|
||||
float bounce; /* bounce back after collision */
|
||||
|
@ -61,15 +56,21 @@ typedef struct emitter {
|
|||
float persist_var;
|
||||
/* TRAILS */
|
||||
warpmask warp_mask;
|
||||
int on;
|
||||
double tte; /* time to emit */
|
||||
sg_buffer buffer;
|
||||
} emitter;
|
||||
|
||||
void particle_init();
|
||||
|
||||
emitter *make_emitter();
|
||||
void emitter_free(emitter *e);
|
||||
|
||||
void emitter_emit(emitter *e, int count, transform *t);
|
||||
void emitter_step(emitter *e, double dt, transform *t);
|
||||
void emitter_draw(emitter *e);
|
||||
void start_emitter(emitter *e);
|
||||
void stop_emitter(emitter *e);
|
||||
|
||||
void emitter_emit(emitter *e, int count);
|
||||
void emitters_step(double dt);
|
||||
void emitters_draw(HMM_Mat4 *proj);
|
||||
void emitter_step(emitter *e, double dt);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "datastream.h"
|
||||
#include "debugdraw.h"
|
||||
#include "font.h"
|
||||
#include "gameobject.h"
|
||||
#include "log.h"
|
||||
#include "sprite.h"
|
||||
#include "particle.h"
|
||||
#include "window.h"
|
||||
#include "model.h"
|
||||
|
@ -16,33 +18,118 @@
|
|||
#include "sokol/sokol_glue.h"
|
||||
#include "stb_image_write.h"
|
||||
|
||||
#include "box.sglsl.h"
|
||||
#include "shadow.sglsl.h"
|
||||
|
||||
#include "sokol/sokol_gfx.h"
|
||||
#include "sokol_gfx_ext.h"
|
||||
|
||||
#include "gui.h"
|
||||
#include "crt.sglsl.h"
|
||||
|
||||
static HMM_Vec2 lastuse = {0};
|
||||
#include "msf_gif.h"
|
||||
|
||||
HMM_Vec2 campos = {0,0};
|
||||
float camzoom = 1;
|
||||
|
||||
viewstate globalview = {0};
|
||||
static struct {
|
||||
sg_swapchain swap;
|
||||
sg_pipeline pipe;
|
||||
sg_bindings bind;
|
||||
sg_shader shader;
|
||||
sg_image img;
|
||||
sg_image depth;
|
||||
} sg_gif;
|
||||
|
||||
sg_sampler std_sampler;
|
||||
sg_sampler nofilter_sampler;
|
||||
sg_sampler tex_sampler;
|
||||
static struct {
|
||||
sg_pipeline pipe;
|
||||
sg_bindings bind;
|
||||
sg_shader shader;
|
||||
} sg_crt;
|
||||
|
||||
int TOPLEFT = 0;
|
||||
static struct {
|
||||
int w;
|
||||
int h;
|
||||
int cpf;
|
||||
int depth;
|
||||
double timer;
|
||||
double spf;
|
||||
int rec;
|
||||
uint8_t *buffer;
|
||||
} gif;
|
||||
|
||||
sg_pass offscreen;
|
||||
MsfGifState gif_state = {};
|
||||
void gif_rec_start(int w, int h, int cpf, int bitdepth)
|
||||
{
|
||||
gif.w = w;
|
||||
gif.h = h;
|
||||
gif.depth = bitdepth;
|
||||
msf_gif_begin(&gif_state, gif.w, gif.h);
|
||||
gif.cpf = cpf;
|
||||
gif.spf = cpf/100.0;
|
||||
gif.rec = 1;
|
||||
gif.timer = apptime();
|
||||
if (gif.buffer) free(gif.buffer);
|
||||
gif.buffer = malloc(gif.w*gif.h*4);
|
||||
|
||||
sg_destroy_image(sg_gif.img);
|
||||
sg_destroy_image(sg_gif.depth);
|
||||
|
||||
sg_gif.img = sg_make_image(&(sg_image_desc){
|
||||
.render_target = true,
|
||||
.width = gif.w,
|
||||
.height = gif.h,
|
||||
.pixel_format = SG_PIXELFORMAT_RGBA8,
|
||||
.label = "gif rt",
|
||||
});
|
||||
|
||||
sg_gif.depth = sg_make_image(&(sg_image_desc){
|
||||
.render_target = true,
|
||||
.width = gif.w,
|
||||
.height = gif.h,
|
||||
.label = "gif depth",
|
||||
});
|
||||
|
||||
sg_gif.swap = sglue_swapchain();
|
||||
}
|
||||
|
||||
void gif_rec_end(const char *path)
|
||||
{
|
||||
if (!gif.rec) return;
|
||||
|
||||
MsfGifResult gif_res = msf_gif_end(&gif_state);
|
||||
if (gif_res.data) {
|
||||
FILE *f = fopen(path, "wb");
|
||||
fwrite(gif_res.data, gif_res.dataSize, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
msf_gif_free(gif_res);
|
||||
gif.rec = 0;
|
||||
}
|
||||
|
||||
void capture_screen(int x, int y, int w, int h, const char *path)
|
||||
{
|
||||
int n = 4;
|
||||
void *data = malloc(w*h*n);
|
||||
sg_query_pixels(0,0,w,h,1,data,w*h*sizeof(char)*n);
|
||||
// sg_query_image_pixels(crt_post.img, crt_post.bind.fs.samplers[0], data, w*h*4);
|
||||
stbi_write_png("cap.png", w, h, n, data, n*w);
|
||||
// stbi_write_bmp("cap.bmp", w, h, n, data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
#include "sokol/sokol_app.h"
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
|
||||
sg_pass_action pass_action = {0};
|
||||
sg_pass_action off_action = {0};
|
||||
sg_image screencolor = {0};
|
||||
sg_image screendepth = {0};
|
||||
|
||||
static struct {
|
||||
sg_pass_action pass_action;
|
||||
|
||||
sg_pass pass;
|
||||
sg_pipeline pipe;
|
||||
sg_shader shader;
|
||||
} sg_shadow;
|
||||
|
||||
void trace_apply_pipeline(sg_pipeline pip, void *data)
|
||||
{
|
||||
|
@ -130,103 +217,159 @@ static sg_trace_hooks hooks = {
|
|||
};
|
||||
|
||||
void render_init() {
|
||||
mainwin.size = (HMM_Vec2){sapp_width(), sapp_height()};
|
||||
sg_setup(&(sg_desc){
|
||||
.environment = sglue_environment(),
|
||||
.logger = { .func = sg_logging },
|
||||
.buffer_pool_size = 1024
|
||||
});
|
||||
|
||||
std_sampler = sg_make_sampler(&(sg_sampler_desc){});
|
||||
tex_sampler = sg_make_sampler(&(sg_sampler_desc){
|
||||
.min_filter = SG_FILTER_LINEAR,
|
||||
.mag_filter = SG_FILTER_LINEAR,
|
||||
.mipmap_filter = SG_FILTER_LINEAR,
|
||||
.wrap_u = SG_WRAP_REPEAT,
|
||||
.wrap_v = SG_WRAP_REPEAT
|
||||
});
|
||||
|
||||
#ifndef NDEBUG
|
||||
sg_trace_hooks hh = sg_install_trace_hooks(&hooks);
|
||||
#endif
|
||||
|
||||
font_init();
|
||||
debugdraw_init();
|
||||
sprite_initialize();
|
||||
|
||||
sg_features feat = sg_query_features();
|
||||
TOPLEFT = feat.origin_top_left;
|
||||
model_init();
|
||||
|
||||
sg_color c = (sg_color){0,0,0,1};
|
||||
pass_action = (sg_pass_action){
|
||||
.colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = c},
|
||||
};
|
||||
|
||||
sg_color oc = (sg_color){35.0/255,60.0/255,92.0/255,1};
|
||||
off_action = (sg_pass_action){
|
||||
.colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = oc},
|
||||
.depth = {
|
||||
.load_action = SG_LOADACTION_CLEAR,
|
||||
.clear_value = 1
|
||||
//.store_action = SG_STOREACTION_STORE
|
||||
sg_gif.shader = sg_make_shader(box_shader_desc(sg_query_backend()));
|
||||
|
||||
sg_gif.pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = sg_gif.shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[1].format = SG_VERTEXFORMAT_FLOAT2
|
||||
}
|
||||
};
|
||||
|
||||
screencolor = sg_make_image(&(sg_image_desc){
|
||||
.render_target = true,
|
||||
.width = 500,
|
||||
.height = 500,
|
||||
.pixel_format = sapp_color_format(),
|
||||
.sample_count = 1,
|
||||
});
|
||||
screendepth = sg_make_image(&(sg_image_desc){
|
||||
.render_target = true,
|
||||
.width =500,
|
||||
.height=500,
|
||||
.pixel_format = sapp_depth_format(),
|
||||
.sample_count = 1
|
||||
},
|
||||
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
|
||||
.label = "gif pipe",
|
||||
});
|
||||
|
||||
offscreen = (sg_pass){
|
||||
.attachments = sg_make_attachments(&(sg_attachments_desc){
|
||||
.colors[0].image = screencolor,
|
||||
.depth_stencil.image = screendepth,
|
||||
}),
|
||||
.action = off_action,
|
||||
float crt_quad[] = {
|
||||
-1, 1, 0, 1,
|
||||
-1, -1, 0, 0,
|
||||
1, -1, 1, 0,
|
||||
-1, 1, 0, 1,
|
||||
1, -1, 1, 0,
|
||||
1, 1, 1, 1
|
||||
};
|
||||
|
||||
float gif_quad[] = {
|
||||
-1, 1, 0, 1,
|
||||
-1, -1, 0, 0,
|
||||
1, -1, 1, 0,
|
||||
-1, 1, 0, 1,
|
||||
1, -1, 1, 0,
|
||||
1, 1, 1, 1
|
||||
};
|
||||
|
||||
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_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_FLOAT2,
|
||||
[1].format = SG_VERTEXFORMAT_FLOAT2
|
||||
}
|
||||
},
|
||||
.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),
|
||||
|
||||
.label = "crt vert buffer",
|
||||
});
|
||||
}
|
||||
|
||||
HMM_Mat4 projection = {0.f};
|
||||
HMM_Mat4 hudproj = {0.f};
|
||||
HMM_Mat4 useproj = {0};
|
||||
|
||||
void openglRender(HMM_Vec2 usesize) {
|
||||
if (usesize.x != lastuse.x || usesize.y != lastuse.y) {
|
||||
sg_destroy_image(screencolor);
|
||||
sg_destroy_image(screendepth);
|
||||
sg_destroy_attachments(offscreen.attachments);
|
||||
screencolor = sg_make_image(&(sg_image_desc){
|
||||
.render_target = true,
|
||||
.width = usesize.x,
|
||||
.height = usesize.y,
|
||||
.pixel_format = sapp_color_format(),
|
||||
.sample_count = 1,
|
||||
});
|
||||
screendepth = sg_make_image(&(sg_image_desc){
|
||||
.render_target = true,
|
||||
.width =usesize.x,
|
||||
.height=usesize.y,
|
||||
.pixel_format = sapp_depth_format(),
|
||||
.sample_count = 1
|
||||
});
|
||||
offscreen = (sg_pass){
|
||||
.attachments = sg_make_attachments(&(sg_attachments_desc){
|
||||
.colors[0].image = screencolor,
|
||||
.depth_stencil.image = screendepth
|
||||
}),
|
||||
.action = off_action,
|
||||
};
|
||||
}
|
||||
lastuse = usesize;
|
||||
#define MODE_STRETCH 0
|
||||
#define MODE_KEEP 1
|
||||
#define MODE_WIDTH 2
|
||||
#define MODE_HEIGHT 3
|
||||
#define MODE_EXPAND 4
|
||||
#define MODE_FULL 5
|
||||
|
||||
sg_begin_pass(&offscreen);
|
||||
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) {
|
||||
case MODE_STRETCH:
|
||||
sg_apply_viewportf(0,0,window->size.x,window->size.y,1);
|
||||
break;
|
||||
case MODE_WIDTH:
|
||||
sg_apply_viewportf(0, window->top, window->size.x, window->psize.y,1); // keep width
|
||||
break;
|
||||
case MODE_HEIGHT:
|
||||
sg_apply_viewportf(window->left,0,window->psize.x, window->size.y,1); // keep height
|
||||
break;
|
||||
case MODE_KEEP:
|
||||
sg_apply_viewportf(0,0,window->rendersize.x, window->rendersize.y, 1); // no scaling
|
||||
break;
|
||||
case MODE_EXPAND:
|
||||
if (window->aspect < window->raspect)
|
||||
sg_apply_viewportf(0, window->top, window->size.x, window->psize.y,1); // keep width
|
||||
else
|
||||
sg_apply_viewportf(window->left,0,window->psize.x, window->size.y,1); // keep height
|
||||
break;
|
||||
case MODE_FULL:
|
||||
usesize = window->size;
|
||||
break;
|
||||
}
|
||||
|
||||
// 2D projection
|
||||
campos = go_pos(cam);
|
||||
camzoom = zoom;
|
||||
|
||||
projection = HMM_Orthographic_RH_NO(
|
||||
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);
|
||||
|
||||
/* if (gif.rec && (apptime() - gif.timer) > gif.spf) {
|
||||
sg_begin_pass(&(sg_pass){
|
||||
.action = pass_action,
|
||||
.swapchain = sg_gif.swap
|
||||
});
|
||||
sg_apply_pipeline(sg_gif.pipe);
|
||||
sg_apply_bindings(&sg_gif.bind);
|
||||
sg_draw(0,6,1);
|
||||
sg_end_pass();
|
||||
|
||||
gif.timer = apptime();
|
||||
sg_query_image_pixels(sg_gif.img, crt_post.bind.fs.samplers[0], gif.buffer, gif.w*gif.h*4);
|
||||
msf_gif_frame(&gif_state, gif.buffer, gif.cpf, gif.depth, gif.w * -4);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh) {
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
#ifndef OPENGL_RENDER_H
|
||||
#define OPENGL_RENDER_H
|
||||
|
||||
#include "config.h"
|
||||
#if defined __linux__
|
||||
#define SOKOL_GLCORE33
|
||||
#elif __EMSCRIPTEN__
|
||||
#define SOKOL_GLES3
|
||||
#elif __WIN32
|
||||
#define SOKOL_D3D11
|
||||
#elif __APPLE__
|
||||
#define SOKOL_METAL
|
||||
#endif
|
||||
|
||||
#include "sokol/sokol_gfx.h"
|
||||
#include "HandmadeMath.h"
|
||||
#include "gameobject.h"
|
||||
#include "transform.h"
|
||||
#include "model.h"
|
||||
|
||||
#define RGBA_MAX 255
|
||||
|
||||
|
@ -16,7 +22,8 @@
|
|||
extern struct rgba color_white;
|
||||
extern struct rgba color_black;
|
||||
extern struct rgba color_clear;
|
||||
extern int TOPLEFT;
|
||||
|
||||
extern int renderMode;
|
||||
|
||||
extern HMM_Vec3 dirl_pos;
|
||||
|
||||
|
@ -25,23 +32,35 @@ extern HMM_Mat4 hudproj;
|
|||
extern HMM_Mat4 useproj;
|
||||
extern sg_pass_action pass_action;
|
||||
|
||||
extern sg_sampler std_sampler;
|
||||
extern sg_sampler tex_sampler;
|
||||
extern sg_image screencolor;
|
||||
extern sg_image screendepth;
|
||||
struct draw_p {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
typedef struct viewstate {
|
||||
HMM_Mat4 v;
|
||||
HMM_Mat4 p;
|
||||
HMM_Mat4 vp;
|
||||
} viewstate;
|
||||
struct draw_p3 {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
extern viewstate globalview;
|
||||
#include <chipmunk/chipmunk.h>
|
||||
|
||||
enum RenderMode {
|
||||
LIT,
|
||||
UNLIT,
|
||||
WIREFRAME,
|
||||
DIRSHADOWMAP,
|
||||
OBJECTPICKER
|
||||
};
|
||||
|
||||
void render_init();
|
||||
extern HMM_Vec2 campos;
|
||||
extern float camzoom;
|
||||
|
||||
void openglRender(HMM_Vec2 usesize);
|
||||
void openglRender(struct window *window, gameobject *cam, float zoom);
|
||||
void opengl_rendermode(enum RenderMode r);
|
||||
|
||||
void openglInit3d(struct window *window);
|
||||
void capture_screen(int x, int y, int w, int h, const char *path);
|
||||
|
||||
void gif_rec_start(int w, int h, int cpf, int bitdepth);
|
||||
|
|
|
@ -126,17 +126,6 @@ char *dirname(const char *path)
|
|||
return dir;
|
||||
}
|
||||
|
||||
char *makepath(char *dir, char *file)
|
||||
{
|
||||
int d = strlen(dir) + strlen(file) + 2;
|
||||
char *path = malloc(d);
|
||||
path[0] = 0;
|
||||
strncat(path, dir, d);
|
||||
strncat(path, "/", d);
|
||||
strncat(path, file, d);
|
||||
return path;
|
||||
}
|
||||
|
||||
char *seprint(char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
@ -167,20 +156,7 @@ static int ls_ftw(const char *path, const struct stat *sb, int typeflag)
|
|||
|
||||
time_t file_mod_secs(const char *file) {
|
||||
struct stat attr;
|
||||
mz_uint index;
|
||||
mz_zip_archive_file_stat pstat;
|
||||
|
||||
if ((index = mz_zip_reader_locate_file(&game_cdb, file, NULL, 0)) != -1) {
|
||||
mz_zip_reader_file_stat(&game_cdb, index,&pstat);
|
||||
return pstat.m_time;
|
||||
}
|
||||
else if ((index = mz_zip_reader_locate_file(&corecdb, file, NULL, 0)) != -1) {
|
||||
mz_zip_reader_file_stat(&corecdb, index, &pstat);
|
||||
return pstat.m_time;
|
||||
}
|
||||
else
|
||||
stat(file, &attr);
|
||||
|
||||
return attr.st_mtime;
|
||||
}
|
||||
|
||||
|
@ -274,9 +250,9 @@ void *slurp_file(const char *filename, size_t *size)
|
|||
void *ret;
|
||||
if (!access(filename, R_OK))
|
||||
return os_slurp(filename, size);
|
||||
else if ((ret = mz_zip_reader_extract_file_to_heap(&game_cdb, filename, size, 0)))
|
||||
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)))
|
||||
else if (ret = mz_zip_reader_extract_file_to_heap(&corecdb, filename, size, 0))
|
||||
return ret;
|
||||
|
||||
return NULL;
|
||||
|
@ -347,7 +323,7 @@ int mkpath(char *path, mode_t mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int slurp_write(void *txt, const char *filename, size_t len) {
|
||||
int slurp_write(const char *txt, const char *filename, size_t len) {
|
||||
FILE *f = fopen(filename, "w");
|
||||
if (!f) return 1;
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@ extern int LOADED_GAME;
|
|||
|
||||
void resources_init();
|
||||
char *get_filename_from_path(char *path, int extension);
|
||||
char *dirname(const char *path);
|
||||
char *makepath(char *dir, char *file);
|
||||
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);
|
||||
char **ls(const char *path);
|
||||
|
@ -23,9 +22,11 @@ void pack_start(const char *name);
|
|||
void pack_add(const char *path);
|
||||
void pack_end();
|
||||
|
||||
char *dirname(const char *path);
|
||||
|
||||
void *slurp_file(const char *filename, size_t *size);
|
||||
char *slurp_text(const char *filename, size_t *size);
|
||||
int slurp_write(void *txt, const char *filename, size_t len);
|
||||
int slurp_write(const char *txt, const char *filename, size_t len);
|
||||
|
||||
char *seprint(char *fmt, ...);
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
#ifndef SCRIPT_H
|
||||
#define SCRIPT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "quickjs/quickjs.h"
|
||||
#include <time.h>
|
||||
|
||||
|
@ -30,8 +26,4 @@ void script_call_sym(JSValue sym, int argc, JSValue *argv);
|
|||
|
||||
void script_gc();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,18 +21,18 @@ typedef union NoiseUnion {
|
|||
noiseNDptr pn;
|
||||
} genericNoise;
|
||||
|
||||
double Noise2D(double x, double y);
|
||||
double Noise3D(double x, double y, double z);
|
||||
double Noise4D(double x, double y, double z, double w);
|
||||
extern double Noise2D(double x, double y);
|
||||
extern double Noise3D(double x, double y, double z);
|
||||
extern double Noise4D(double x, double y, double z, double w);
|
||||
|
||||
double GBlur1D(double stdDev, double x);
|
||||
double GBlur2D(double stdDev, double x, double y);
|
||||
extern double GBlur1D(double stdDev, double x);
|
||||
extern double GBlur2D(double stdDev, double x, double y);
|
||||
|
||||
double Noise(genericNoise func, int len, double args[]);
|
||||
extern double Noise(genericNoise func, int len, double args[]);
|
||||
|
||||
double TurbulentNoise(genericNoise func, int direction, int iterations, int len, double args[]);
|
||||
double FractalSumNoise(genericNoise func, int iterations, int len, double args[]);
|
||||
double FractalSumAbsNoise(genericNoise func, int iterations, int len, double args[]);
|
||||
extern double TurbulentNoise(genericNoise func, int direction, int iterations, int len, double args[]);
|
||||
extern double FractalSumNoise(genericNoise func, int iterations, int len, double args[]);
|
||||
extern double FractalSumAbsNoise(genericNoise func, int iterations, int len, double args[]);
|
||||
|
||||
double octave_3d(double x, double y, double z, int octaves, double persistence);
|
||||
|
||||
|
|
306
source/engine/sokol_gfx_ext.h
Normal file
306
source/engine/sokol_gfx_ext.h
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
sokol_gfx_ext.h - extensions for sokol_gfx
|
||||
https://github.com/edubart/sokol_gp
|
||||
*/
|
||||
|
||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_EXT_IMPL)
|
||||
#define SOKOL_GFX_EXT_IMPL
|
||||
#endif
|
||||
|
||||
#ifndef SOKOL_GFX_EXT_INCLUDED
|
||||
#define SOKOL_GFX_EXT_INCLUDED
|
||||
|
||||
#ifndef SOKOL_GFX_INCLUDED
|
||||
#error "Please include sokol_gfx.h before sokol_gfx_ext.h"
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
SOKOL_GFX_API_DECL void sg_query_image_pixels(sg_image img_id, sg_sampler smp_id, void* pixels, int size);
|
||||
SOKOL_GFX_API_DECL void sg_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels, int size);
|
||||
|
||||
#endif // SOKOL_GFX_EXT_INCLUDED
|
||||
|
||||
#ifdef SOKOL_GFX_EXT_IMPL
|
||||
#ifndef SOKOL_GFX_EXT_IMPL_INCLUDED
|
||||
#define SOKOL_GFX_EXT_IMPL_INCLUDED
|
||||
|
||||
#ifndef SOKOL_GFX_IMPL_INCLUDED
|
||||
#error "Please include sokol_gfx.h implementation before sokol_gp.h implementation"
|
||||
#endif
|
||||
|
||||
#if defined(_SOKOL_ANY_GL)
|
||||
|
||||
static void _sg_gl_query_image_pixels(_sg_image_t* img, _sg_sampler_t *smp, void* pixels) {
|
||||
SOKOL_ASSERT(img->gl.target == GL_TEXTURE_2D);
|
||||
SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]);
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
_sg_gl_cache_store_texture_sampler_binding(0);
|
||||
_sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], smp->gl.smp);
|
||||
glGetTexImage(img->gl.target, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
_SG_GL_CHECK_ERROR();
|
||||
_sg_gl_cache_restore_texture_sampler_binding(0);
|
||||
#else
|
||||
static GLuint newFbo = 0;
|
||||
GLuint oldFbo = 0;
|
||||
if(newFbo == 0) {
|
||||
glGenFramebuffers(1, &newFbo);
|
||||
}
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&oldFbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, newFbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img->gl.tex[img->cmn.active_slot], 0);
|
||||
glReadPixels(0, 0, img->cmn.width, img->cmn.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFbo);
|
||||
//glDeleteFramebuffers(1, &newFbo);
|
||||
_SG_GL_CHECK_ERROR();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _sg_gl_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) {
|
||||
SOKOL_ASSERT(pixels);
|
||||
GLuint gl_fb;
|
||||
GLint dims[4];
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_fb);
|
||||
_SG_GL_CHECK_ERROR();
|
||||
glGetIntegerv(GL_VIEWPORT, dims);
|
||||
int cur_height = dims[3];
|
||||
y = origin_top_left ? (cur_height - (y+h)) : y;
|
||||
_SG_GL_CHECK_ERROR();
|
||||
#if defined(SOKOL_GLES2) // use NV extension instead
|
||||
glReadBufferNV(gl_fb == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0);
|
||||
#else
|
||||
glReadBuffer(gl_fb == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0);
|
||||
#endif
|
||||
_SG_GL_CHECK_ERROR();
|
||||
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
_SG_GL_CHECK_ERROR();
|
||||
}
|
||||
|
||||
#elif defined(SOKOL_D3D11)
|
||||
|
||||
static inline void _sgext_d3d11_Texture2D_GetDesc(ID3D11Texture2D* self, D3D11_TEXTURE2D_DESC* pDesc) {
|
||||
self->lpVtbl->GetDesc(self, pDesc);
|
||||
}
|
||||
|
||||
static inline void _sgext_d3d11_SamplerState_GetDesc(ID3D11SamplerState* self, D3D11_SAMPLER_DESC* pDesc) {
|
||||
self->lpVtbl->GetDesc(self, pDesc);
|
||||
}
|
||||
|
||||
static inline void _sgext_d3d11_CopySubresourceRegion(ID3D11DeviceContext* self, ID3D11Resource *pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource *pSrcResource, UINT SrcSubresource, const D3D11_BOX *pSrcBox) {
|
||||
self->lpVtbl->CopySubresourceRegion(self, pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox);
|
||||
}
|
||||
|
||||
static inline void _sgext_d3d11_OMGetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView **ppRenderTargetViews, ID3D11DepthStencilView **ppDepthStencilView) {
|
||||
self->lpVtbl->OMGetRenderTargets(self, NumViews, ppRenderTargetViews, ppDepthStencilView);
|
||||
}
|
||||
|
||||
static inline void _sgext_d3d11_RenderTargetView_GetResource(ID3D11RenderTargetView* self, ID3D11Resource** ppResource) {
|
||||
self->lpVtbl->GetResource(self, ppResource);
|
||||
}
|
||||
|
||||
static void _sg_d3d11_query_image_pixels(_sg_image_t* img, void* pixels) {
|
||||
SOKOL_ASSERT(_sg.d3d11.ctx);
|
||||
SOKOL_ASSERT(img->d3d11.tex2d);
|
||||
HRESULT hr;
|
||||
_SOKOL_UNUSED(hr);
|
||||
|
||||
// create staging texture
|
||||
ID3D11Texture2D* staging_tex = NULL;
|
||||
D3D11_TEXTURE2D_DESC staging_desc = {
|
||||
.Width = (UINT)img->cmn.width,
|
||||
.Height = (UINT)img->cmn.height,
|
||||
.MipLevels = 1,
|
||||
.ArraySize = 1,
|
||||
.Format = img->d3d11.format,
|
||||
.SampleDesc = {
|
||||
.Count = 1,
|
||||
.Quality = 0,
|
||||
},
|
||||
.Usage = D3D11_USAGE_STAGING,
|
||||
.BindFlags = 0,
|
||||
.CPUAccessFlags = D3D11_CPU_ACCESS_READ,
|
||||
.MiscFlags = 0
|
||||
};
|
||||
hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &staging_desc, NULL, &staging_tex);
|
||||
SOKOL_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
// copy pixels to staging texture
|
||||
_sgext_d3d11_CopySubresourceRegion(_sg.d3d11.ctx,
|
||||
(ID3D11Resource*)staging_tex,
|
||||
0, 0, 0, 0,
|
||||
(ID3D11Resource*)img->d3d11.tex2d,
|
||||
0, NULL);
|
||||
|
||||
// map the staging texture's data to CPU-accessible memory
|
||||
D3D11_MAPPED_SUBRESOURCE msr = {.pData = NULL};
|
||||
hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0, D3D11_MAP_READ, 0, &msr);
|
||||
SOKOL_ASSERT(SUCCEEDED(hr));
|
||||
memcpy(pixels, msr.pData, img->cmn.width * img->cmn.height * 4);
|
||||
|
||||
// unmap the texture
|
||||
_sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0);
|
||||
|
||||
if(staging_tex) _sg_d3d11_Release(staging_tex);
|
||||
}
|
||||
|
||||
static void _sg_d3d11_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) {
|
||||
// get current render target
|
||||
ID3D11RenderTargetView* render_target_view = NULL;
|
||||
_sgext_d3d11_OMGetRenderTargets(_sg.d3d11.ctx, 1, &render_target_view, NULL);
|
||||
|
||||
// fallback to window render target
|
||||
if(!render_target_view)
|
||||
render_target_view = (ID3D11RenderTargetView*)_sg.d3d11.cur_pass.render_view;
|
||||
SOKOL_ASSERT(render_target_view);
|
||||
|
||||
// get the back buffer texture
|
||||
ID3D11Texture2D *back_buffer = NULL;
|
||||
_sgext_d3d11_RenderTargetView_GetResource(render_target_view, (ID3D11Resource**)&back_buffer);
|
||||
SOKOL_ASSERT(back_buffer);
|
||||
|
||||
// create a staging texture to copy the screen's data to
|
||||
D3D11_TEXTURE2D_DESC staging_desc;
|
||||
_sgext_d3d11_Texture2D_GetDesc(back_buffer, &staging_desc);
|
||||
staging_desc.Width = w;
|
||||
staging_desc.Height = h;
|
||||
staging_desc.BindFlags = 0;
|
||||
staging_desc.MiscFlags = 0;
|
||||
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
staging_desc.Usage = D3D11_USAGE_STAGING;
|
||||
ID3D11Texture2D *staging_tex = NULL;
|
||||
HRESULT hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &staging_desc, NULL, &staging_tex);
|
||||
SOKOL_ASSERT(SUCCEEDED(hr));
|
||||
_SOKOL_UNUSED(hr);
|
||||
|
||||
// copy the desired portion of the back buffer to the staging texture
|
||||
// y = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h)));
|
||||
|
||||
D3D11_BOX src_box = {
|
||||
.left = (UINT)x,
|
||||
.top = (UINT)y,
|
||||
.front = 0,
|
||||
.right = (UINT)(x + w),
|
||||
.bottom = (UINT)(y + w),
|
||||
.back = 1,
|
||||
};
|
||||
_sgext_d3d11_CopySubresourceRegion(_sg.d3d11.ctx,
|
||||
(ID3D11Resource*)staging_tex,
|
||||
0, 0, 0, 0,
|
||||
(ID3D11Resource*)back_buffer,
|
||||
0, &src_box);
|
||||
|
||||
// map the staging texture's data to CPU-accessible memory
|
||||
D3D11_MAPPED_SUBRESOURCE msr = {.pData = NULL};
|
||||
hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0, D3D11_MAP_READ, 0, &msr);
|
||||
SOKOL_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
memcpy(pixels, msr.pData, w * h * 4);
|
||||
// unmap the texture
|
||||
_sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0);
|
||||
|
||||
if(back_buffer) _sg_d3d11_Release(back_buffer);
|
||||
if(staging_tex) _sg_d3d11_Release(staging_tex);
|
||||
}
|
||||
|
||||
#elif defined(SOKOL_METAL)
|
||||
|
||||
#ifdef TARGET_OS_IPHONE
|
||||
|
||||
static void _sg_metal_commit_command_buffer(){};
|
||||
static void _sg_metal_encode_texture_pixels(int x, int y, int w, int h, bool origin_top_left, id<MTLTexture> mtl_src_texture, void* pixels) {};
|
||||
static void _sg_metal_query_image_pixels(_sg_image_t* img, void* pixels) {};
|
||||
static void _sg_metal_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) {};
|
||||
|
||||
#else
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
|
||||
static void _sg_metal_commit_command_buffer() {
|
||||
SOKOL_ASSERT(!_sg.mtl.in_pass);
|
||||
if(_sg.mtl.cmd_buffer) {
|
||||
#if defined(_SG_TARGET_MACOS)
|
||||
[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)];
|
||||
#endif
|
||||
[_sg.mtl.cmd_buffer commit];
|
||||
[_sg.mtl.cmd_buffer waitUntilCompleted];
|
||||
_sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences];
|
||||
}
|
||||
}
|
||||
|
||||
static void _sg_metal_encode_texture_pixels(int x, int y, int w, int h, bool origin_top_left, id<MTLTexture> mtl_src_texture, void* pixels) {
|
||||
SOKOL_ASSERT(!_sg.mtl.in_pass);
|
||||
_sg_metal_commit_command_buffer();
|
||||
MTLTextureDescriptor* mtl_dst_texture_desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtl_src_texture.pixelFormat width:w height:h mipmapped:NO];
|
||||
mtl_dst_texture_desc.storageMode = MTLStorageModeManaged;
|
||||
mtl_dst_texture_desc.resourceOptions = MTLResourceStorageModeManaged;
|
||||
mtl_dst_texture_desc.usage = MTLTextureUsageShaderRead + MTLTextureUsageShaderWrite;
|
||||
id<MTLTexture> mtl_dst_texture = [mtl_src_texture.device newTextureWithDescriptor:mtl_dst_texture_desc];
|
||||
id<MTLCommandBuffer> cmd_buffer = [_sg.mtl.cmd_queue commandBuffer];
|
||||
id<MTLBlitCommandEncoder> blit_encoder = [cmd_buffer blitCommandEncoder];
|
||||
[blit_encoder copyFromTexture:mtl_src_texture
|
||||
sourceSlice:0
|
||||
sourceLevel:0
|
||||
sourceOrigin:MTLOriginMake(x,(origin_top_left ? y : (mtl_src_texture.height - (y + h))),0)
|
||||
sourceSize:MTLSizeMake(w,h,1)
|
||||
toTexture:mtl_dst_texture
|
||||
destinationSlice:0
|
||||
destinationLevel:0
|
||||
destinationOrigin:MTLOriginMake(0,0,0)
|
||||
];
|
||||
[blit_encoder synchronizeTexture:mtl_dst_texture slice:0 level:0];
|
||||
[blit_encoder endEncoding];
|
||||
[cmd_buffer commit];
|
||||
[cmd_buffer waitUntilCompleted];
|
||||
|
||||
MTLRegion mtl_region = MTLRegionMake2D(0, 0, w, h);
|
||||
[mtl_dst_texture getBytes:pixels bytesPerRow:w * 4 fromRegion:mtl_region mipmapLevel:0];
|
||||
}
|
||||
|
||||
static void _sg_metal_query_image_pixels(_sg_image_t* img, void* pixels) {
|
||||
id<MTLTexture> mtl_src_texture = _sg.mtl.idpool.pool[img->mtl.tex[0]];
|
||||
_sg_metal_encode_texture_pixels(0, 0, mtl_src_texture.width, mtl_src_texture.height, true, mtl_src_texture, pixels);
|
||||
}
|
||||
|
||||
static void _sg_metal_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) {
|
||||
id<CAMetalDrawable> mtl_drawable = (__bridge id<CAMetalDrawable>)_sg.mtl.drawable_cb();
|
||||
_sg_metal_encode_texture_pixels(x, y, w, h, origin_top_left, mtl_drawable.texture, pixels);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void sg_query_image_pixels(sg_image img_id, sg_sampler smp_id, void* pixels, int size) {
|
||||
SOKOL_ASSERT(pixels);
|
||||
SOKOL_ASSERT(img_id.id != SG_INVALID_ID);
|
||||
_sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id);
|
||||
_sg_sampler_t *smp = _sg_lookup_sampler(&_sg.pools, smp_id.id);
|
||||
SOKOL_ASSERT(img);
|
||||
SOKOL_ASSERT(size >= (img->cmn.width * img->cmn.height * 4));
|
||||
_SOKOL_UNUSED(size);
|
||||
#if defined(_SOKOL_ANY_GL)
|
||||
_sg_gl_query_image_pixels(img, smp, pixels);
|
||||
#elif defined(SOKOL_D3D11)
|
||||
_sg_d3d11_query_image_pixels(img, pixels);
|
||||
#elif defined(SOKOL_METAL)
|
||||
_sg_metal_query_image_pixels(img, pixels);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sg_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels, int size) {
|
||||
SOKOL_ASSERT(pixels);
|
||||
SOKOL_ASSERT(size >= w*h);
|
||||
_SOKOL_UNUSED(size);
|
||||
#if defined(_SOKOL_ANY_GL)
|
||||
_sg_gl_query_pixels(x, y, w, h, origin_top_left, pixels);
|
||||
#elif defined(SOKOL_D3D11)
|
||||
_sg_d3d11_query_pixels(x, y, w, h, origin_top_left, pixels);
|
||||
#elif defined(SOKOL_METAL)
|
||||
_sg_metal_query_pixels(x, y, w, h, origin_top_left, pixels);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SOKOL_GFX_EXT_IMPL_INCLUDED
|
||||
#endif // SOKOL_GFX_EXT_IMPL
|
|
@ -195,6 +195,13 @@ void unplug_node(dsp_node *node)
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
float amp;
|
||||
float freq;
|
||||
float phase; /* from 0 to 1, marking where we are */
|
||||
float (*filter)(float phase);
|
||||
} phasor;
|
||||
|
||||
float sin_phasor(float p)
|
||||
{
|
||||
return sin(2*PI*p);
|
||||
|
@ -388,6 +395,7 @@ void filter_iir(struct dsp_iir *iir, soundbyte *buffer, int frames)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
dsp_node *dsp_lpf(float freq)
|
||||
{
|
||||
struct dsp_iir *iir = malloc(sizeof(*iir));
|
||||
|
@ -616,6 +624,11 @@ void dsp_mono(void *p, soundbyte *restrict out, int n)
|
|||
}
|
||||
}
|
||||
|
||||
struct bitcrush {
|
||||
float sr;
|
||||
float depth;
|
||||
};
|
||||
|
||||
#define ROUND(f) ((float)((f>0.0)?floor(f+0.5):ceil(f-0.5)))
|
||||
void filter_bitcrush(struct bitcrush *restrict b, soundbyte *restrict out, int frames)
|
||||
{
|
||||
|
|
|
@ -51,7 +51,7 @@ dsp_node *dsp_hpf(float freq);
|
|||
dsp_node *dsp_lpf(float freq);
|
||||
|
||||
/* atk, dec, sus, rls specify the time, in miliseconds, the phase begins */
|
||||
typedef struct dsp_adsr {
|
||||
struct dsp_adsr {
|
||||
unsigned int atk;
|
||||
double atk_t;
|
||||
unsigned int dec;
|
||||
|
@ -63,7 +63,7 @@ typedef struct dsp_adsr {
|
|||
|
||||
double time; /* Current time of the filter */
|
||||
float out;
|
||||
} adsr;
|
||||
};
|
||||
|
||||
dsp_node *dsp_adsr(unsigned int atk, unsigned int dec, unsigned int sus, unsigned int rls);
|
||||
|
||||
|
@ -77,7 +77,7 @@ dsp_node *dsp_delay(double sec, double decay);
|
|||
dsp_node *dsp_fwd_delay(double sec, double decay);
|
||||
dsp_node *dsp_pitchshift(float octaves);
|
||||
|
||||
typedef struct dsp_compressor {
|
||||
struct dsp_compressor {
|
||||
double ratio;
|
||||
double threshold;
|
||||
float target;
|
||||
|
@ -85,7 +85,7 @@ typedef struct dsp_compressor {
|
|||
double atk_tau;
|
||||
unsigned int rls; /* MIlliseconds */
|
||||
double rls_tau;
|
||||
} compressor;
|
||||
};
|
||||
|
||||
dsp_node *dsp_compressor();
|
||||
|
||||
|
@ -106,19 +106,6 @@ float tri_phasor(float p);
|
|||
dsp_node *dsp_reverb();
|
||||
dsp_node *dsp_sinewave(float amp, float freq);
|
||||
dsp_node *dsp_square(float amp, float freq, int sr, int ch);
|
||||
|
||||
typedef struct {
|
||||
float amp;
|
||||
float freq;
|
||||
float phase; /* from 0 to 1, marking where we are */
|
||||
float (*filter)(float phase);
|
||||
} phasor;
|
||||
|
||||
typedef struct bitcrush {
|
||||
float sr;
|
||||
float depth;
|
||||
} bitcrush;
|
||||
|
||||
dsp_node *dsp_bitcrush(float sr, float res);
|
||||
void dsp_mono(void *p, soundbyte *out, int n);
|
||||
void pan_frames(soundbyte *out, float deg, int frames);
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
#include "tml.h"
|
||||
#include "dsp.h"
|
||||
|
||||
typedef struct dsp_midi_song {
|
||||
struct dsp_midi_song {
|
||||
float bpm;
|
||||
double time;
|
||||
tml_message *midi;
|
||||
tsf *sf;
|
||||
} midi;
|
||||
};
|
||||
|
||||
dsp_node *dsp_midi(const char *midi, tsf *sf);
|
||||
tsf *make_soundfont(const char *sf);
|
||||
|
|
227
source/engine/sprite.c
Normal file
227
source/engine/sprite.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
#include "sprite.h"
|
||||
|
||||
#include "gameobject.h"
|
||||
#include "log.h"
|
||||
#include "render.h"
|
||||
#include "stb_ds.h"
|
||||
#include "texture.h"
|
||||
#include "HandmadeMath.h"
|
||||
|
||||
#include "sprite.sglsl.h"
|
||||
#include "9slice.sglsl.h"
|
||||
|
||||
static sprite **sprites = NULL;
|
||||
|
||||
static sg_shader shader_sprite;
|
||||
static sg_pipeline pip_sprite;
|
||||
static sg_bindings bind_sprite;
|
||||
|
||||
struct sprite_vert {
|
||||
HMM_Vec2 pos;
|
||||
HMM_Vec2 uv;
|
||||
struct rgba color;
|
||||
struct rgba emissive;
|
||||
};
|
||||
|
||||
static int num_spriteverts = 5000;
|
||||
|
||||
static sg_shader slice9_shader;
|
||||
static sg_pipeline slice9_pipe;
|
||||
static sg_bindings slice9_bind;
|
||||
static float slice9_points[8] = {
|
||||
0.0, 0.0,
|
||||
0.0, 1.0,
|
||||
1.0, 0.0,
|
||||
1.0, 1.0
|
||||
};
|
||||
struct slice9_vert {
|
||||
HMM_Vec2 pos;
|
||||
struct uv_n uv;
|
||||
unsigned short border[4];
|
||||
HMM_Vec2 scale;
|
||||
struct rgba color;
|
||||
};
|
||||
|
||||
sprite *sprite_make()
|
||||
{
|
||||
sprite *sp = calloc(sizeof(*sp), 1);
|
||||
sp->pos = (HMM_Vec2){0,0};
|
||||
sp->scale = (HMM_Vec2){1,1};
|
||||
sp->angle = 0;
|
||||
sp->color = color_white;
|
||||
sp->emissive = color_clear;
|
||||
sp->go = NULL;
|
||||
sp->tex = NULL;
|
||||
sp->frame = ST_UNIT;
|
||||
sp->drawmode = DRAW_SIMPLE;
|
||||
sp->enabled = 1;
|
||||
sp->parallax = 1;
|
||||
|
||||
arrpush(sprites,sp);
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
void sprite_free(sprite *sprite)
|
||||
{
|
||||
free(sprite);
|
||||
for (int i = arrlen(sprites)-1; i >= 0; i--)
|
||||
if (sprites[i] == sprite) {
|
||||
arrdelswap(sprites,i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int sprite_count = 0;
|
||||
|
||||
void sprite_flush() { sprite_count = 0; }
|
||||
|
||||
int sprite_sort(sprite **sa, sprite **sb)
|
||||
{
|
||||
sprite *a = *sa;
|
||||
sprite *b = *sb;
|
||||
struct gameobject *goa = a->go;
|
||||
struct gameobject *gob= b->go;
|
||||
if (!goa && !gob) return 0;
|
||||
if (!goa) return -1;
|
||||
if (!gob) return 1;
|
||||
if (goa->drawlayer > gob->drawlayer) return 1;
|
||||
if (gob->drawlayer > goa->drawlayer) return -1;
|
||||
if (*sa > *sb) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void sprite_draw_all() {
|
||||
if (arrlen(sprites) == 0) return;
|
||||
|
||||
sg_apply_pipeline(pip_sprite);
|
||||
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++) {
|
||||
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){
|
||||
.shader = shader_sprite,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[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,
|
||||
});
|
||||
|
||||
bind_sprite.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct sprite_vert) * num_spriteverts,
|
||||
.type = SG_BUFFERTYPE_VERTEXBUFFER,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "sprite vertex buffer",
|
||||
});
|
||||
bind_sprite.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
|
||||
|
||||
slice9_shader = sg_make_shader(slice9_shader_desc(sg_query_backend()));
|
||||
|
||||
slice9_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = slice9_shader,
|
||||
.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
|
||||
}},
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
});
|
||||
|
||||
slice9_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(struct slice9_vert) * 100,
|
||||
.type = SG_BUFFERTYPE_VERTEXBUFFER,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "slice9 buffer"
|
||||
});
|
||||
}
|
||||
|
||||
void tex_draw(struct texture *tex, HMM_Mat3 m, struct rect r, struct rgba color, int wrap, HMM_Vec2 wrapoffset, HMM_Vec2 wrapscale, struct rgba emissive, float parallax) {
|
||||
struct sprite_vert verts[4];
|
||||
float w = tex->width*r.w;
|
||||
float h = tex->height*r.h;
|
||||
|
||||
HMM_Vec2 sposes[4] = {
|
||||
{0,0},
|
||||
{w,0},
|
||||
{0,h},
|
||||
{w,h}
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
verts[i].pos = mat_t_pos(m, sposes[i]);
|
||||
verts[i].color = color;
|
||||
verts[i].emissive = emissive;
|
||||
}
|
||||
|
||||
if (wrap) {
|
||||
r.w *= wrapscale.x;
|
||||
r.h *= wrapscale.y;
|
||||
}
|
||||
|
||||
verts[0].uv.X = r.x;
|
||||
verts[0].uv.Y = r.y+r.h;
|
||||
verts[1].uv.X = r.x+r.w;
|
||||
verts[1].uv.Y = r.y+r.h;
|
||||
verts[2].uv.X = r.x;
|
||||
verts[2].uv.Y = r.y;
|
||||
verts[3].uv.X = r.x+r.w;
|
||||
verts[3].uv.Y = r.y;
|
||||
|
||||
bind_sprite.fs.images[0] = tex->id;
|
||||
|
||||
sg_append_buffer(bind_sprite.vertex_buffers[0], SG_RANGE_REF(verts));
|
||||
sg_apply_bindings(&bind_sprite);
|
||||
|
||||
sg_draw(sprite_count * 4, 4, 1);
|
||||
sprite_count++;
|
||||
}
|
||||
|
||||
transform2d sprite2t(sprite *s)
|
||||
{
|
||||
return (transform2d){
|
||||
.pos = s->pos,
|
||||
.scale = s->scale,
|
||||
.angle = HMM_TurnToRad*s->angle
|
||||
};
|
||||
}
|
||||
|
||||
void sprite_draw(struct sprite *sprite) {
|
||||
if (!sprite->tex) return;
|
||||
transform2d t;
|
||||
if (!sprite->go) t = t2d_unit;
|
||||
else t = go2t(sprite->go);
|
||||
|
||||
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(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(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)
|
||||
{
|
||||
|
||||
}
|
41
source/engine/sprite.h
Normal file
41
source/engine/sprite.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef SPRITE_H
|
||||
#define SPRITE_H
|
||||
|
||||
#include "texture.h"
|
||||
#include "HandmadeMath.h"
|
||||
#include "render.h"
|
||||
#include "transform.h"
|
||||
#include "gameobject.h"
|
||||
|
||||
#define DRAW_SIMPLE 0
|
||||
#define DRAW_TILE 1
|
||||
|
||||
struct sprite {
|
||||
HMM_Vec2 pos;
|
||||
HMM_Vec2 scale;
|
||||
float angle;
|
||||
struct rgba color;
|
||||
struct rgba emissive;
|
||||
gameobject *go;
|
||||
texture *tex;
|
||||
struct rect frame;
|
||||
int enabled;
|
||||
int drawmode;
|
||||
float parallax;
|
||||
unsigned int next;
|
||||
};
|
||||
|
||||
typedef struct sprite sprite;
|
||||
|
||||
sprite *sprite_make();
|
||||
int make_sprite(gameobject *go);
|
||||
void sprite_free(sprite *sprite);
|
||||
void sprite_delete(int id);
|
||||
void sprite_initialize();
|
||||
void sprite_draw(struct sprite *sprite);
|
||||
void sprite_draw_all();
|
||||
void sprite_flush();
|
||||
|
||||
void gui_draw_img(texture *tex, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color);
|
||||
|
||||
#endif
|
|
@ -1,103 +1,22 @@
|
|||
#include "steam.h"
|
||||
#ifndef NSTEAM
|
||||
/*
|
||||
#include <steam/steam_api.h>
|
||||
#include <steam/steam_api_flat.h>
|
||||
|
||||
#include "jsffi.h"
|
||||
|
||||
ISteamUserStats *stats = NULL;
|
||||
ISteamApps *app = NULL;
|
||||
ISteamRemoteStorage *remote = NULL;
|
||||
ISteamUGC *ugc = NULL;
|
||||
|
||||
static JSValue js_steam_init(JSContext *js, JSValue this_v, int argc, JSValue *argv)
|
||||
{
|
||||
SteamErrMsg err;
|
||||
SteamAPI_InitEx(&err);
|
||||
JSValue str = str2js(err);
|
||||
stats = SteamAPI_SteamUserStats();
|
||||
app = SteamAPI_SteamApps();
|
||||
remote = SteamAPI_SteamRemoteStorage();
|
||||
ugc = SteamAPI_SteamUGC();
|
||||
return str;
|
||||
SteamAPI_Init();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_steam_funcs[] = {
|
||||
MIST_FUNC_DEF(steam, init, 1),
|
||||
};
|
||||
|
||||
JSC_SCALL(achievement_get32,
|
||||
int32 data;
|
||||
SteamAPI_ISteamUserStats_GetStatInt32(stats, str, &data);
|
||||
return number2js(data);
|
||||
)
|
||||
|
||||
JSC_SCALL(achievement_get,
|
||||
bool data;
|
||||
SteamAPI_ISteamUserStats_GetAchievement(stats, str, &data);
|
||||
return boolean2js(data);
|
||||
)
|
||||
|
||||
JSC_SCALL(achievement_set,
|
||||
return boolean2js(SteamAPI_ISteamUserStats_SetAchievement(stats, str));
|
||||
)
|
||||
|
||||
JSC_CCALL(achievement_num,
|
||||
return number2js(SteamAPI_ISteamUserStats_GetNumAchievements(stats));
|
||||
)
|
||||
|
||||
JSC_SCALL(achievement_clear, SteamAPI_ISteamUserStats_ClearAchievement(stats, str))
|
||||
|
||||
JSC_SCALL(achievement_user_get,
|
||||
bool a;
|
||||
boolean2js(SteamAPI_ISteamUserStats_GetUserAchievement(stats, js2uint64(argv[1]), str, &a));
|
||||
ret = boolean2js(a);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_achievement_funcs[] = {
|
||||
MIST_FUNC_DEF(achievement, clear, 1),
|
||||
MIST_FUNC_DEF(achievement, get32, 2),
|
||||
MIST_FUNC_DEF(achievement, get, 2),
|
||||
MIST_FUNC_DEF(achievement, set, 1),
|
||||
MIST_FUNC_DEF(achievement, num, 0),
|
||||
MIST_FUNC_DEF(achievement, user_get, 2),
|
||||
};
|
||||
|
||||
JSC_CCALL(cloud_app_enabled,
|
||||
return boolean2js(SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(remote))
|
||||
)
|
||||
JSC_CCALL(cloud_enable, SteamAPI_ISteamRemoteStorage_SetCloudEnabledForApp(remote, js2boolean(self)))
|
||||
JSC_CCALL(cloud_account_enabled, return boolean2js(SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(remote)))
|
||||
|
||||
static const JSCFunctionListEntry js_cloud_funcs[] = {
|
||||
MIST_FUNC_DEF(cloud, app_enabled, 0),
|
||||
MIST_FUNC_DEF(cloud, account_enabled, 0),
|
||||
MIST_FUNC_DEF(cloud, enable, 1)
|
||||
};
|
||||
|
||||
JSC_CCALL(app_owner,
|
||||
uint64_t own = SteamAPI_ISteamApps_GetAppOwner(app);
|
||||
return JS_NewBigUint64(js, own);
|
||||
)
|
||||
static const JSCFunctionListEntry js_app_funcs[] = {
|
||||
MIST_FUNC_DEF(app, owner, 0),
|
||||
};
|
||||
|
||||
JSValue js_init_steam(JSContext *js)
|
||||
{
|
||||
JSValue steam = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, steam, js_steam_funcs, countof(js_steam_funcs));
|
||||
JSValue achievements = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, achievements, js_achievement_funcs, countof(js_achievement_funcs));
|
||||
js_setpropstr(steam, "achievements", achievements);
|
||||
|
||||
JSValue app = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, app, js_app_funcs, countof(js_app_funcs));
|
||||
js_setpropstr(steam, "app", app);
|
||||
|
||||
JSValue cloud = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, cloud, js_cloud_funcs, countof(js_cloud_funcs));
|
||||
js_setpropstr(steam, "cloud", cloud);
|
||||
return steam;
|
||||
}
|
||||
#endif
|
||||
*/
|
|
@ -5,14 +5,15 @@
|
|||
#include "sokol/sokol_gfx.h"
|
||||
#include <math.h>
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#include "resources.h"
|
||||
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include "stb_image_resize2.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
#ifndef NSVG
|
||||
|
@ -121,11 +122,15 @@ struct texture *texture_from_file(const char *path) {
|
|||
unsigned int nw = next_pow2(tex->width);
|
||||
unsigned int nh = next_pow2(tex->height);
|
||||
|
||||
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);
|
||||
|
||||
int mipw, miph;
|
||||
mipw = tex->width;
|
||||
miph = tex->height;
|
||||
|
@ -146,18 +151,18 @@ 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,
|
||||
//.num_mipmaps = mips,
|
||||
.data = sg_img_data
|
||||
});
|
||||
|
||||
for (int i = 1; i < mips; i++)
|
||||
free(mipdata[i]);
|
||||
/*for (int i = 1; i < mips; i++)
|
||||
free(mipdata[i]);*/
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
@ -165,33 +170,12 @@ struct texture *texture_from_file(const char *path) {
|
|||
void texture_free(texture *tex)
|
||||
{
|
||||
if (!tex) return;
|
||||
if (tex->data)
|
||||
free(tex->data);
|
||||
if (tex->delays) arrfree(tex->delays);
|
||||
sg_destroy_image(tex->id);
|
||||
free(tex);
|
||||
}
|
||||
|
||||
struct texture *texture_empty(int w, int h, int n)
|
||||
{
|
||||
texture *tex = calloc(1,sizeof(*tex));
|
||||
tex->data = calloc(w*h*n, sizeof(unsigned char));
|
||||
tex->width = w;
|
||||
tex->height = h;
|
||||
sg_image_data sgdata;
|
||||
sgdata.subimage[0][0] = (sg_range){.ptr = tex->data, .size = w*h*4};
|
||||
tex->id = sg_make_image(&(sg_image_desc){
|
||||
.type = SG_IMAGETYPE_2D,
|
||||
.width = tex->width,
|
||||
.height = tex->height,
|
||||
.usage = SG_USAGE_IMMUTABLE,
|
||||
.num_mipmaps = 1,
|
||||
.data = sgdata,
|
||||
});
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
struct texture *texture_fromdata(void *raw, long size)
|
||||
{
|
||||
struct texture *tex = calloc(1, sizeof(*tex));
|
||||
|
@ -207,6 +191,8 @@ struct texture *texture_fromdata(void *raw, long size)
|
|||
|
||||
tex->data = data;
|
||||
|
||||
int filter = SG_FILTER_NEAREST;
|
||||
|
||||
sg_image_data sg_img_data;
|
||||
|
||||
int mips = mip_levels(tex->width, tex->height)+1;
|
||||
|
@ -279,65 +265,6 @@ double grad (int hash, double x, double y, double z)
|
|||
}*/
|
||||
}
|
||||
|
||||
void texture_save(texture *tex, const char *file)
|
||||
{
|
||||
char *ext = strrchr(file, '.');
|
||||
if (!strcmp(ext, ".png"))
|
||||
stbi_write_png(file, tex->width, tex->height, 4, tex->data, 4*tex->width);
|
||||
else if (!strcmp(ext, ".bmp"))
|
||||
stbi_write_bmp(file, tex->width, tex->height, 4, tex->data);
|
||||
else if (!strcmp(ext, ".tga"))
|
||||
stbi_write_tga(file, tex->width, tex->height, 4, tex->data);
|
||||
else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg"))
|
||||
stbi_write_jpg(file, tex->width, tex->height, 4, tex->data, 5);
|
||||
}
|
||||
|
||||
void blit_image(uint8_t* src, uint8_t* dest, int src_width, int src_height, int dest_width, int dest_height, int sx, int sy, int sw, int sh) {
|
||||
if (sx + sw > dest_width) return;
|
||||
if (sy + sh > dest_height) return;
|
||||
int src_stride = src_width * 4;
|
||||
int dest_stride = dest_width * 4;
|
||||
|
||||
for (int y = 0; y < sw; y++) {
|
||||
for (int x = 0; x < sh; x++) {
|
||||
int src_index = (y * src_stride) + (x * 4);
|
||||
int dest_index = ((y + sy) * dest_stride) + ((x + sx) * 4);
|
||||
|
||||
// Calculate the alpha value for the source pixel
|
||||
uint8_t src_alpha = src[src_index + 3];
|
||||
|
||||
// Calculate the alpha value for the destination pixel
|
||||
uint8_t dest_alpha = dest[dest_index + 3];
|
||||
|
||||
// Calculate the resulting alpha value
|
||||
uint8_t result_alpha = src_alpha + (255 - src_alpha) * dest_alpha / 255;
|
||||
|
||||
// Calculate the resulting RGB values
|
||||
uint8_t result_red = (src[src_index + 0] * src_alpha + dest[dest_index + 0] * (255 - src_alpha) * dest_alpha / 255) / result_alpha;
|
||||
uint8_t result_green = (src[src_index + 1] * src_alpha + dest[dest_index + 1] * (255 - src_alpha) * dest_alpha / 255) / result_alpha;
|
||||
uint8_t result_blue = (src[src_index + 2] * src_alpha + dest[dest_index + 2] * (255 - src_alpha) * dest_alpha / 255) / result_alpha;
|
||||
|
||||
// Set the resulting pixel values
|
||||
dest[dest_index + 0] = result_red;
|
||||
dest[dest_index + 1] = result_green;
|
||||
dest[dest_index + 2] = result_blue;
|
||||
dest[dest_index + 3] = result_alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to draw source image pixels on top of a destination image
|
||||
void texture_blit(texture *dest, texture *src, int x, int y, int w, int h) {
|
||||
blit_image(src->data, dest->data, src->width, src->height, dest->height, dest->width, x, y, w, h);
|
||||
}
|
||||
|
||||
void texture_flip(texture *tex, int y)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int p[512] = {151,160,137,91,90,15,
|
||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||
|
|
|
@ -18,8 +18,7 @@ extern struct rect ST_UNIT;
|
|||
|
||||
/* Represents an actual texture on the GPU */
|
||||
struct texture {
|
||||
sg_image id; /* ID reference for the GPU memory location of the
|
||||
texture */
|
||||
sg_image id; /* ID reference for the GPU memory location of the texture */
|
||||
int width;
|
||||
int height;
|
||||
unsigned char *data;
|
||||
|
@ -40,11 +39,8 @@ typedef struct img_sampler{
|
|||
|
||||
texture *texture_from_file(const char *path);
|
||||
void texture_free(texture *tex);
|
||||
|
||||
struct texture *texture_fromdata(void *raw, long size);
|
||||
texture *texture_empty(int width, int height, int n);
|
||||
void texture_blit(texture *dest, texture *src, int x, int y, int w, int h);
|
||||
void texture_flip(texture *tex, int y);
|
||||
void texture_save(texture *tex, const char *file);
|
||||
|
||||
double perlin(double x, double y, double z);
|
||||
|
||||
|
|
131
source/engine/thirdparty/imgui/imconfig.h
vendored
131
source/engine/thirdparty/imgui/imconfig.h
vendored
|
@ -1,131 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
//#define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
|
||||
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
16056
source/engine/thirdparty/imgui/imgui.cpp
vendored
16056
source/engine/thirdparty/imgui/imgui.cpp
vendored
File diff suppressed because it is too large
Load diff
3378
source/engine/thirdparty/imgui/imgui.h
vendored
3378
source/engine/thirdparty/imgui/imgui.h
vendored
File diff suppressed because it is too large
Load diff
4623
source/engine/thirdparty/imgui/imgui_draw.cpp
vendored
4623
source/engine/thirdparty/imgui/imgui_draw.cpp
vendored
File diff suppressed because it is too large
Load diff
3604
source/engine/thirdparty/imgui/imgui_internal.h
vendored
3604
source/engine/thirdparty/imgui/imgui_internal.h
vendored
File diff suppressed because it is too large
Load diff
4448
source/engine/thirdparty/imgui/imgui_tables.cpp
vendored
4448
source/engine/thirdparty/imgui/imgui_tables.cpp
vendored
File diff suppressed because it is too large
Load diff
9010
source/engine/thirdparty/imgui/imgui_widgets.cpp
vendored
9010
source/engine/thirdparty/imgui/imgui_widgets.cpp
vendored
File diff suppressed because it is too large
Load diff
627
source/engine/thirdparty/imgui/imstb_rectpack.h
vendored
627
source/engine/thirdparty/imgui/imstb_rectpack.h
vendored
|
@ -1,627 +0,0 @@
|
|||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
//
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
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 OR COPYRIGHT HOLDERS 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.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
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.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
1441
source/engine/thirdparty/imgui/imstb_textedit.h
vendored
1441
source/engine/thirdparty/imgui/imstb_textedit.h
vendored
File diff suppressed because it is too large
Load diff
5085
source/engine/thirdparty/imgui/imstb_truetype.h
vendored
5085
source/engine/thirdparty/imgui/imstb_truetype.h
vendored
File diff suppressed because it is too large
Load diff
121
source/engine/thirdparty/sokol/sokol_app.h
vendored
121
source/engine/thirdparty/sokol/sokol_app.h
vendored
|
@ -18,12 +18,11 @@
|
|||
the backend selected for sokol_gfx.h if both are used in the same
|
||||
project):
|
||||
|
||||
#define SOKOL_GLCORE
|
||||
#define SOKOL_GLCORE33
|
||||
#define SOKOL_GLES3
|
||||
#define SOKOL_D3D11
|
||||
#define SOKOL_METAL
|
||||
#define SOKOL_WGPU
|
||||
#define SOKOL_NOAPI
|
||||
|
||||
Optionally provide the following defines with your own implementations:
|
||||
|
||||
|
@ -48,7 +47,7 @@
|
|||
On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport)
|
||||
or __declspec(dllimport) as needed.
|
||||
|
||||
On Linux, SOKOL_GLCORE can use either GLX or EGL.
|
||||
On Linux, SOKOL_GLCORE33 can use either GLX or EGL.
|
||||
GLX is default, set SOKOL_FORCE_EGL to override.
|
||||
|
||||
For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp
|
||||
|
@ -88,7 +87,7 @@
|
|||
- makes the rendered frame visible
|
||||
- provides keyboard-, mouse- and low-level touch-events
|
||||
- platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android
|
||||
- 3D-APIs: Metal, D3D11, GL3.2, GLES3, WebGL, WebGL2, NOAPI
|
||||
- 3D-APIs: Metal, D3D11, GL3.2, GLES3, WebGL, WebGL2
|
||||
|
||||
FEATURE/PLATFORM MATRIX
|
||||
=======================
|
||||
|
@ -98,7 +97,6 @@
|
|||
gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES
|
||||
metal | --- | YES | --- | YES | --- | ---
|
||||
d3d11 | YES | --- | --- | --- | --- | ---
|
||||
noapi | YES | TODO | TODO | --- | TODO | ---
|
||||
KEY_DOWN | YES | YES | YES | SOME | TODO | YES
|
||||
KEY_UP | YES | YES | YES | SOME | TODO | YES
|
||||
CHAR | YES | YES | YES | YES | TODO | YES
|
||||
|
@ -315,15 +313,10 @@
|
|||
objects and values required for rendering. If sokol_app.h
|
||||
is not compiled with SOKOL_WGPU, these functions return null.
|
||||
|
||||
uint32_t sapp_gl_get_framebuffer(void)
|
||||
const uint32_t sapp_gl_get_framebuffer(void)
|
||||
This returns the 'default framebuffer' of the GL context.
|
||||
Typically this will be zero.
|
||||
|
||||
int sapp_gl_get_major_version(void)
|
||||
int sapp_gl_get_minor_version(void)
|
||||
Returns the major and minor version of the GL context
|
||||
(only for SOKOL_GLCORE, all other backends return zero here, including SOKOL_GLES3)
|
||||
|
||||
const void* sapp_android_get_native_activity(void);
|
||||
On Android, get the native activity ANativeActivity pointer, otherwise
|
||||
a null pointer.
|
||||
|
@ -355,7 +348,7 @@
|
|||
sapp_consume_event() from inside the event handler (NOTE that
|
||||
this behaviour is currently only implemented for some HTML5
|
||||
events, support for other platforms and event types will
|
||||
be added as needed, please open a GitHub ticket and/or provide
|
||||
be added as needed, please open a github ticket and/or provide
|
||||
a PR if needed).
|
||||
|
||||
NOTE: Do *not* call any 3D API rendering functions in the event
|
||||
|
@ -1899,10 +1892,6 @@ SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void);
|
|||
|
||||
/* GL: get framebuffer object */
|
||||
SOKOL_APP_API_DECL uint32_t sapp_gl_get_framebuffer(void);
|
||||
/* GL: get major version (only valid for desktop GL) */
|
||||
SOKOL_APP_API_DECL int sapp_gl_get_major_version(void);
|
||||
/* GL: get minor version (only valid for desktop GL) */
|
||||
SOKOL_APP_API_DECL int sapp_gl_get_minor_version(void);
|
||||
|
||||
/* Android: get native activity handle */
|
||||
SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void);
|
||||
|
@ -1970,8 +1959,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
|
|||
#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
|
||||
/* MacOS */
|
||||
#define _SAPP_MACOS (1)
|
||||
#if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE)
|
||||
#error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE")
|
||||
#if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33)
|
||||
#error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33")
|
||||
#endif
|
||||
#else
|
||||
/* iOS or iOS Simulator */
|
||||
|
@ -1989,8 +1978,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
|
|||
#elif defined(_WIN32)
|
||||
/* Windows (D3D11 or GL) */
|
||||
#define _SAPP_WIN32 (1)
|
||||
#if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_NOAPI)
|
||||
#error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE or SOKOL_NOAPI")
|
||||
#if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33)
|
||||
#error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33")
|
||||
#endif
|
||||
#elif defined(__ANDROID__)
|
||||
/* Android */
|
||||
|
@ -2004,7 +1993,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
|
|||
#elif defined(__linux__) || defined(__unix__)
|
||||
/* Linux */
|
||||
#define _SAPP_LINUX (1)
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
#if !defined(SOKOL_FORCE_EGL)
|
||||
#define _SAPP_GLX (1)
|
||||
#endif
|
||||
|
@ -2014,13 +2003,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
|
|||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#else
|
||||
#error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3")
|
||||
#error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3")
|
||||
#endif
|
||||
#else
|
||||
#error "sokol_app.h: Unknown platform"
|
||||
#endif
|
||||
|
||||
#if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3)
|
||||
#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3)
|
||||
#define _SAPP_ANY_GL (1)
|
||||
#endif
|
||||
|
||||
|
@ -2410,11 +2399,11 @@ _SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) {
|
|||
#if defined(SOKOL_METAL)
|
||||
@interface _sapp_macos_view : MTKView
|
||||
@end
|
||||
#elif defined(SOKOL_GLCORE)
|
||||
#elif defined(SOKOL_GLCORE33)
|
||||
@interface _sapp_macos_view : NSOpenGLView
|
||||
- (void)timerFired:(id)sender;
|
||||
@end
|
||||
#endif // SOKOL_GLCORE
|
||||
#endif // SOKOL_GLCORE33
|
||||
|
||||
typedef struct {
|
||||
uint32_t flags_changed_store;
|
||||
|
@ -2556,7 +2545,7 @@ typedef struct {
|
|||
uint8_t raw_input_data[256];
|
||||
} _sapp_win32_t;
|
||||
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
|
||||
#define WGL_SUPPORT_OPENGL_ARB 0x2010
|
||||
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
|
||||
|
@ -2616,7 +2605,7 @@ typedef struct {
|
|||
HWND msg_hwnd;
|
||||
HDC msg_dc;
|
||||
} _sapp_wgl_t;
|
||||
#endif // SOKOL_GLCORE
|
||||
#endif // SOKOL_GLCORE33
|
||||
|
||||
#endif // _SAPP_WIN32
|
||||
|
||||
|
@ -2887,7 +2876,7 @@ typedef struct {
|
|||
_sapp_win32_t win32;
|
||||
#if defined(SOKOL_D3D11)
|
||||
_sapp_d3d11_t d3d11;
|
||||
#elif defined(SOKOL_GLCORE)
|
||||
#elif defined(SOKOL_GLCORE33)
|
||||
_sapp_wgl_t wgl;
|
||||
#endif
|
||||
#elif defined(_SAPP_ANDROID)
|
||||
|
@ -3096,13 +3085,8 @@ _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) {
|
|||
// (or expressed differently: zero is a valid value for gl_minor_version
|
||||
// and can't be used to indicate 'default')
|
||||
if (0 == res.gl_major_version) {
|
||||
#if defined(_SAPP_APPLE)
|
||||
res.gl_major_version = 4;
|
||||
res.gl_minor_version = 1;
|
||||
#else
|
||||
res.gl_major_version = 4;
|
||||
res.gl_minor_version = 3;
|
||||
#endif
|
||||
res.gl_major_version = 3;
|
||||
res.gl_minor_version = 2;
|
||||
}
|
||||
res.html5_canvas_name = _sapp_def(res.html5_canvas_name, "canvas");
|
||||
res.clipboard_size = _sapp_def(res.clipboard_size, 8192);
|
||||
|
@ -3666,7 +3650,7 @@ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) {
|
|||
const int cur_fb_height = (int)roundf(fb_size.height);
|
||||
const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
|
||||
(_sapp.framebuffer_height != cur_fb_height);
|
||||
#elif defined(SOKOL_GLCORE)
|
||||
#elif defined(SOKOL_GLCORE33)
|
||||
const int cur_fb_width = (int)roundf(bounds.size.width * _sapp.dpi_scale);
|
||||
const int cur_fb_height = (int)roundf(bounds.size.height * _sapp.dpi_scale);
|
||||
const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) ||
|
||||
|
@ -3908,7 +3892,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
|
|||
_sapp.macos.window.contentView = _sapp.macos.view;
|
||||
[_sapp.macos.window makeFirstResponder:_sapp.macos.view];
|
||||
_sapp.macos.view.layer.magnificationFilter = kCAFilterNearest;
|
||||
#elif defined(SOKOL_GLCORE)
|
||||
#elif defined(SOKOL_GLCORE33)
|
||||
NSOpenGLPixelFormatAttribute attrs[32];
|
||||
int i = 0;
|
||||
attrs[i++] = NSOpenGLPFAAccelerated;
|
||||
|
@ -4140,7 +4124,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
|
|||
@end
|
||||
|
||||
@implementation _sapp_macos_view
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
- (void)timerFired:(id)sender {
|
||||
_SOKOL_UNUSED(sender);
|
||||
[self setNeedsDisplay:YES];
|
||||
|
@ -4240,7 +4224,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events(void) {
|
|||
|
||||
// helper function to make GL context active
|
||||
static void _sapp_gl_make_current(void) {
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
[[_sapp.macos.view openGLContext] makeCurrentContext];
|
||||
#endif
|
||||
}
|
||||
|
@ -5765,28 +5749,16 @@ _SOKOL_PRIVATE void _sapp_emsc_wgpu_request_adapter_cb(WGPURequestAdapterStatus
|
|||
SOKOL_ASSERT(adapter);
|
||||
_sapp.wgpu.adapter = adapter;
|
||||
size_t cur_feature_index = 1;
|
||||
#define _SAPP_WGPU_MAX_REQUESTED_FEATURES (8)
|
||||
WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = {
|
||||
WGPUFeatureName requiredFeatures[8] = {
|
||||
WGPUFeatureName_Depth32FloatStencil8,
|
||||
};
|
||||
// check for optional features we're interested in
|
||||
// FIXME: ASTC texture compression
|
||||
if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionBC)) {
|
||||
SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
|
||||
requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC;
|
||||
}
|
||||
if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) {
|
||||
SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
|
||||
} else if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) {
|
||||
requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2;
|
||||
}
|
||||
if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionASTC)) {
|
||||
SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
|
||||
requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionASTC;
|
||||
}
|
||||
if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_Float32Filterable)) {
|
||||
SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES);
|
||||
requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable;
|
||||
}
|
||||
#undef _SAPP_WGPU_MAX_REQUESTED_FEATURES
|
||||
|
||||
WGPUDeviceDescriptor dev_desc;
|
||||
_sapp_clear(&dev_desc, sizeof(dev_desc));
|
||||
|
@ -5973,7 +5945,7 @@ int main(int argc, char* argv[]) {
|
|||
// ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████
|
||||
//
|
||||
// >>gl helpers
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
typedef struct {
|
||||
int red_bits;
|
||||
int green_bits;
|
||||
|
@ -6624,7 +6596,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) {
|
|||
|
||||
#endif /* SOKOL_D3D11 */
|
||||
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
_SOKOL_PRIVATE void _sapp_wgl_init(void) {
|
||||
_sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll");
|
||||
if (!_sapp.wgl.opengl32) {
|
||||
|
@ -6922,7 +6894,7 @@ _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) {
|
|||
/* FIXME: DwmIsCompositionEnabled? (see GLFW) */
|
||||
SwapBuffers(_sapp.win32.dc);
|
||||
}
|
||||
#endif /* SOKOL_GLCORE */
|
||||
#endif /* SOKOL_GLCORE33 */
|
||||
|
||||
_SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) {
|
||||
SOKOL_ASSERT(src && dst && (dst_num_bytes > 1));
|
||||
|
@ -7339,10 +7311,7 @@ _SOKOL_PRIVATE void _sapp_win32_timing_measure(void) {
|
|||
// fallback if swap model isn't "flip-discard" or GetFrameStatistics failed for another reason
|
||||
_sapp_timing_measure(&_sapp.timing);
|
||||
#endif
|
||||
#if defined(SOKOL_GLCORE)
|
||||
_sapp_timing_measure(&_sapp.timing);
|
||||
#endif
|
||||
#if defined(SOKOL_NOAPI)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
_sapp_timing_measure(&_sapp.timing);
|
||||
#endif
|
||||
}
|
||||
|
@ -7544,7 +7513,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM
|
|||
// present with DXGI_PRESENT_DO_NOT_WAIT
|
||||
_sapp_d3d11_present(true);
|
||||
#endif
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
_sapp_wgl_swap_buffers();
|
||||
#endif
|
||||
/* NOTE: resizing the swap-chain during resize leads to a substantial
|
||||
|
@ -7957,7 +7926,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
|
|||
_sapp_d3d11_create_device_and_swapchain();
|
||||
_sapp_d3d11_create_default_render_target();
|
||||
#endif
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
_sapp_wgl_init();
|
||||
_sapp_wgl_load_extensions();
|
||||
_sapp_wgl_create_context();
|
||||
|
@ -7985,7 +7954,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
|
|||
Sleep((DWORD)(16 * _sapp.swap_interval));
|
||||
}
|
||||
#endif
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
_sapp_wgl_swap_buffers();
|
||||
#endif
|
||||
/* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */
|
||||
|
@ -10978,7 +10947,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
|
|||
#if !defined(_SAPP_GLX)
|
||||
|
||||
_SOKOL_PRIVATE void _sapp_egl_init(void) {
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
if (!eglBindAPI(EGL_OPENGL_API)) {
|
||||
_SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED);
|
||||
}
|
||||
|
@ -11002,7 +10971,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) {
|
|||
EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
|
||||
const EGLint config_attrs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
||||
#elif defined(SOKOL_GLES3)
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
|
||||
|
@ -11065,7 +11034,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) {
|
|||
}
|
||||
|
||||
EGLint ctx_attrs[] = {
|
||||
#if defined(SOKOL_GLCORE)
|
||||
#if defined(SOKOL_GLCORE33)
|
||||
EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl_major_version,
|
||||
EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version,
|
||||
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
|
@ -11784,24 +11753,6 @@ SOKOL_API_IMPL uint32_t sapp_gl_get_framebuffer(void) {
|
|||
#endif
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL int sapp_gl_get_major_version(void) {
|
||||
SOKOL_ASSERT(_sapp.valid);
|
||||
#if defined(SOKOL_GLCORE)
|
||||
return _sapp.desc.gl_major_version;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL int sapp_gl_get_minor_version(void) {
|
||||
SOKOL_ASSERT(_sapp.valid);
|
||||
#if defined(SOKOL_GLCORE)
|
||||
return _sapp.desc.gl_minor_version;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) {
|
||||
// NOTE: _sapp.valid is not asserted here because sapp_android_get_native_activity()
|
||||
// needs to be callable from within sokol_main() (see: https://github.com/floooh/sokol/issues/708)
|
||||
|
|
4
source/engine/thirdparty/sokol/sokol_audio.h
vendored
4
source/engine/thirdparty/sokol/sokol_audio.h
vendored
|
@ -39,7 +39,6 @@
|
|||
|
||||
- on macOS: AudioToolbox
|
||||
- on iOS: AudioToolbox, AVFoundation
|
||||
- on FreeBSD: asound
|
||||
- on Linux: asound
|
||||
- on Android: link with OpenSLES or aaudio
|
||||
- on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib
|
||||
|
@ -52,7 +51,6 @@
|
|||
|
||||
- Windows: WASAPI
|
||||
- Linux: ALSA
|
||||
- FreeBSD: ALSA
|
||||
- macOS: CoreAudio
|
||||
- iOS: CoreAudio+AVAudioSession
|
||||
- emscripten: WebAudio with ScriptProcessorNode
|
||||
|
@ -782,9 +780,7 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
|
|||
#include "aaudio/AAudio.h"
|
||||
#endif
|
||||
#elif defined(_SAUDIO_LINUX)
|
||||
#if !defined(__FreeBSD__)
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
#define _SAUDIO_PTHREADS (1)
|
||||
#include <pthread.h>
|
||||
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||
|
|
7
source/engine/thirdparty/sokol/sokol_fetch.h
vendored
7
source/engine/thirdparty/sokol/sokol_fetch.h
vendored
|
@ -1071,11 +1071,14 @@ typedef struct sfetch_response_t {
|
|||
sfetch_range_t buffer; // the user-provided buffer which holds the fetched data
|
||||
} sfetch_response_t;
|
||||
|
||||
/* response callback function signature */
|
||||
typedef void(*sfetch_callback_t)(const sfetch_response_t*);
|
||||
|
||||
/* request parameters passed to sfetch_send() */
|
||||
typedef struct sfetch_request_t {
|
||||
uint32_t channel; // index of channel this request is assigned to (default: 0)
|
||||
const char* path; // filesystem path or HTTP URL (required)
|
||||
void (*callback) (const sfetch_response_t*); // response callback function pointer (required)
|
||||
sfetch_callback_t callback; // response callback function pointer (required)
|
||||
uint32_t chunk_size; // number of bytes to load per stream-block (optional)
|
||||
sfetch_range_t buffer; // a memory buffer where the data will be loaded into (optional)
|
||||
sfetch_range_t user_data; // ptr/size of a POD user data block which will be memcpy'd (optional)
|
||||
|
@ -1299,7 +1302,7 @@ typedef struct {
|
|||
uint32_t channel;
|
||||
uint32_t lane;
|
||||
uint32_t chunk_size;
|
||||
void (*callback) (const sfetch_response_t*);
|
||||
sfetch_callback_t callback;
|
||||
sfetch_range_t buffer;
|
||||
|
||||
/* updated by IO-thread, off-limits to user thread */
|
||||
|
|
882
source/engine/thirdparty/sokol/sokol_gfx.h
vendored
882
source/engine/thirdparty/sokol/sokol_gfx.h
vendored
File diff suppressed because it is too large
Load diff
2
source/engine/thirdparty/sokol/sokol_glue.h
vendored
2
source/engine/thirdparty/sokol/sokol_glue.h
vendored
|
@ -43,7 +43,7 @@
|
|||
functions. Use this in the sg_setup() call like this:
|
||||
|
||||
sg_setup(&(sg_desc){
|
||||
.environment = sglue_environment(),
|
||||
.environment = sglue_enviornment(),
|
||||
...
|
||||
});
|
||||
|
||||
|
|
1148
source/engine/thirdparty/sokol/util/sokol_color.h
vendored
1148
source/engine/thirdparty/sokol/util/sokol_color.h
vendored
File diff suppressed because it is too large
Load diff
4567
source/engine/thirdparty/sokol/util/sokol_debugtext.h
vendored
4567
source/engine/thirdparty/sokol/util/sokol_debugtext.h
vendored
File diff suppressed because it is too large
Load diff
1790
source/engine/thirdparty/sokol/util/sokol_fontstash.h
vendored
1790
source/engine/thirdparty/sokol/util/sokol_fontstash.h
vendored
File diff suppressed because it is too large
Load diff
4765
source/engine/thirdparty/sokol/util/sokol_gfx_imgui.h
vendored
4765
source/engine/thirdparty/sokol/util/sokol_gfx_imgui.h
vendored
File diff suppressed because it is too large
Load diff
4259
source/engine/thirdparty/sokol/util/sokol_gl.h
vendored
4259
source/engine/thirdparty/sokol/util/sokol_gl.h
vendored
File diff suppressed because it is too large
Load diff
3150
source/engine/thirdparty/sokol/util/sokol_imgui.h
vendored
3150
source/engine/thirdparty/sokol/util/sokol_imgui.h
vendored
File diff suppressed because it is too large
Load diff
167
source/engine/thirdparty/sokol/util/sokol_memtrack.h
vendored
167
source/engine/thirdparty/sokol/util/sokol_memtrack.h
vendored
|
@ -1,167 +0,0 @@
|
|||
#if defined(SOKOL_IMPL) && !defined(SOKOL_MEMTRACK_IMPL)
|
||||
#define SOKOL_MEMTRACK_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_MEMTRACK_INCLUDED
|
||||
/*
|
||||
sokol_memtrack.h -- memory allocation wrapper to track memory usage
|
||||
of sokol libraries
|
||||
|
||||
Project URL: https://github.com/floooh/sokol
|
||||
|
||||
Optionally provide the following defines with your own implementations:
|
||||
|
||||
SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern)
|
||||
SOKOL_API_DECL - same as SOKOL_MEMTRACK_API_DECL
|
||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
||||
|
||||
If sokol_memtrack.h is compiled as a DLL, define the following before
|
||||
including the declaration or implementation:
|
||||
|
||||
SOKOL_DLL
|
||||
|
||||
USAGE
|
||||
=====
|
||||
Just plug the malloc/free wrapper functions into the desc.allocator
|
||||
struct provided by most sokol header setup functions:
|
||||
|
||||
sg_setup(&(sg_desc){
|
||||
//...
|
||||
.allocator = {
|
||||
.alloc_fn = smemtrack_alloc,
|
||||
.free_fn = smemtrack_free,
|
||||
}
|
||||
});
|
||||
|
||||
Then call smemtrack_info() to get information about current number
|
||||
of allocations and overall allocation size:
|
||||
|
||||
const smemtrack_info_t info = smemtrack_info();
|
||||
const int num_allocs = info.num_allocs;
|
||||
const int num_bytes = info.num_bytes;
|
||||
|
||||
Note the sokol_memtrack.h can only track allocations issued by
|
||||
the sokol headers, not allocations that happen under the hood
|
||||
in system libraries.
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2018 Andre Weissflog
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the
|
||||
use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software in a
|
||||
product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not
|
||||
be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
#define SOKOL_MEMTRACK_INCLUDED (1)
|
||||
#include <stdint.h>
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_MEMTRACK_API_DECL)
|
||||
#define SOKOL_MEMTRACK_API_DECL SOKOL_API_DECL
|
||||
#endif
|
||||
#ifndef SOKOL_MEMTRACK_API_DECL
|
||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_MEMTRACK_IMPL)
|
||||
#define SOKOL_MEMTRACK_API_DECL __declspec(dllexport)
|
||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||
#define SOKOL_MEMTRACK_API_DECL __declspec(dllimport)
|
||||
#else
|
||||
#define SOKOL_MEMTRACK_API_DECL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct smemtrack_info_t {
|
||||
int num_allocs;
|
||||
int num_bytes;
|
||||
} smemtrack_info_t;
|
||||
|
||||
SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void);
|
||||
SOKOL_MEMTRACK_API_DECL void* smemtrack_alloc(size_t size, void* user_data);
|
||||
SOKOL_MEMTRACK_API_DECL void smemtrack_free(void* ptr, void* user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* SOKOL_MEMTRACK_INCLUDED */
|
||||
|
||||
/*=== IMPLEMENTATION =========================================================*/
|
||||
#ifdef SOKOL_MEMTRACK_IMPL
|
||||
#define SOKOL_MEMTRACK_IMPL_INCLUDED (1)
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <string.h> // memset
|
||||
|
||||
#ifndef SOKOL_API_IMPL
|
||||
#define SOKOL_API_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_DEBUG
|
||||
#ifndef NDEBUG
|
||||
#define SOKOL_DEBUG
|
||||
#endif
|
||||
#endif
|
||||
#ifndef _SOKOL_PRIVATE
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
||||
#else
|
||||
#define _SOKOL_PRIVATE static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// per-allocation header used to keep track of the allocation size
|
||||
#define _SMEMTRACK_HEADER_SIZE (16)
|
||||
|
||||
static struct {
|
||||
smemtrack_info_t state;
|
||||
} _smemtrack;
|
||||
|
||||
SOKOL_API_IMPL void* smemtrack_alloc(size_t size, void* user_data) {
|
||||
(void)user_data;
|
||||
uint8_t* ptr = (uint8_t*) malloc(size + _SMEMTRACK_HEADER_SIZE);
|
||||
if (ptr) {
|
||||
// store allocation size (for allocation size tracking)
|
||||
*(size_t*)ptr = size;
|
||||
_smemtrack.state.num_allocs++;
|
||||
_smemtrack.state.num_bytes += (int) size;
|
||||
return ptr + _SMEMTRACK_HEADER_SIZE;
|
||||
}
|
||||
else {
|
||||
// allocation failed, return null pointer
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL void smemtrack_free(void* ptr, void* user_data) {
|
||||
(void)user_data;
|
||||
if (ptr) {
|
||||
uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE;
|
||||
size_t size = *(size_t*)alloc_ptr;
|
||||
_smemtrack.state.num_allocs--;
|
||||
_smemtrack.state.num_bytes -= (int) size;
|
||||
free(alloc_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL smemtrack_info_t smemtrack_info(void) {
|
||||
return _smemtrack.state;
|
||||
}
|
||||
|
||||
#endif /* SOKOL_MEMTRACK_IMPL */
|
1431
source/engine/thirdparty/sokol/util/sokol_shape.h
vendored
1431
source/engine/thirdparty/sokol/util/sokol_shape.h
vendored
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue