main
Dominik Madarász 2023-10-22 17:46:06 +02:00
parent 7465c37c03
commit 3195aae777
177 changed files with 617976 additions and 1 deletions

22
_fwk/.gitignore vendored 100644
View File

@ -0,0 +1,22 @@
.art*.zip
__pycache__
.vs
_deploy
tools/steamcmd
!tools/steamcmd/steamcmd.exe
_builder_output
emsdk
demos/html5/*.data
demos/html5/*.js
demos/html5/*.html
demos/ports/*/*.exe
demos/ports/doom/.doomrc
*.sublime-workspace
engine/v4k.html
*.exe.manifest
v4k.osx
libv4k.*
editor
*.dSYM
.DS_store

6
_fwk/.gitmodules vendored 100644
View File

@ -0,0 +1,6 @@
[submodule "depot"]
path = depot
url = git@dev.v4.games:v4games/depot.git
[submodule "_mirror"]
path = _mirror
url = https://github.com/r-lyeh/FWK

818
_fwk/MAKE.bat 100644
View File

@ -0,0 +1,818 @@
@echo off
setlocal enableDelayedExpansion
cd /d "%~dp0"
rem show help
if "%1"=="-?" goto showhelp
if "%1"=="-h" goto showhelp
if "%1"=="help" (
:showhelp
echo %0 ; compile everything: `make dll dev` alias
echo %0 [help] ; show this screen
echo %0 [docs] ; generate tools/docs/docs.html file
echo %0 [cook] ; cook .zipfiles with tools/cook.ini cookbook
echo %0 [sync] ; sync repo to latest
echo %0 [fwk_prep] ; prepare files for fwk PR
echo %0 [fwk] ; update fwk-mirror fork
echo %0 [lua] ; execute lua script with v4k
echo %0 [html5] ; build HTML5 demo
echo %0 [web] ; run Python webserver in html5 dir
echo %0 [pull] ; pull changes from origin
echo %0 [push] ; prepare for commit, stage changes and commit them
echo %0 [dstat] ; show depot changes
echo %0 [dpush] ; push depot changes
echo %0 [depot] ; sync depot changes
echo %0 [fuse] ; fuse all binaries and cooked zipfiles found together
echo %0 [git] ; prepare for commit
echo %0 [vps] ; upload the release to VPS
echo %0 [tidy] ; clean up temp files
echo %0 [bind] ; generate lua bindings
echo %0 [checkmem] ; check untracked allocators in V4K
echo %0 [split^|join] ; engine/v4k* ^>split^> engine/split/* or engine/split/* ^>join^> engine/v4k*
echo %0 [lua] ; execute lua script with v4k
echo %0 [amalgamation] ; combine engine/v4k* into a single-header file
echo %0 [prep] ; combine split files into a single-header file, ready for use
echo %0 [sln] ; generate a xcode/gmake/ninja/visual studio solution
echo %0 [cl^|tcc^|cc^|gcc^|clang^|clang-cl] [dbg^|dev^|rel] [static^|dll] [nov4k^|nodemos^|editor] [vis] [-- args]
echo cl \
echo tcc ^|
echo cc ^| select compiler. must be accessible in PATH
echo gcc ^| (will be autodetected when no option is provided^)
echo clang ^|
echo clang-cl /
echo dbg \ debug build: [x] ASAN [x] poison [x] asserts [x] profiler [x] symbols [ ] zero optimizations
echo dev ^| develop build: [ ] ASAN [x] poison [x] asserts [x] profiler [x] symbols [*] some optimizations (default^)
echo rel / release build: [ ] ASAN [ ] poison [ ] asserts [ ] profiler [x] symbols (cl,clang-cl only^) [x] many optimizations
echo static \ link v4k as static library
echo dll / link v4k as dynamic library (dll^) (default^)
echo nov4k \ do not compile framework
echo demos ^| do compile demos
echo editor / do compile editor
echo run ^| run compiled .exe
echo vis ^> visualize invokation cmdline.
echo args ^> after `--` separator is found, pass all remaining arguments to compiler as-is
echo run_args ^> after `//` separator is found, pass all remaining arguments to runtime exe as-is
echo.
exit /b
)
rem sync repo to latest
if "%1"=="sync" (
call MAKE.bat tidy
git reset --hard HEAD~1 && git pull
exit /b
)
rem cook asset files
if "%1"=="cook" (
rem generate cooker twice: use multi-threaded version if available (cl). then cook.
rem call tools\tcc tools\cook.c -Iengine engine\v4k.c
rem cl tools\cook.c -Iengine engine\v4k.c
rem cook
tools\cook
exit /b
)
rem generate bindings
if "%1"=="bind" (
rem luajit
tools\luajit tools\luajit_make_bindings.lua > v4k.lua
move /y v4k.lua bind
exit /b
)
if "%1"=="lua" (
pushd bind
luajit "..\%2"
popd
exit /b
)
rem generate documentation
if "%1"=="docs" (
rem set symbols...
git describe --tags --abbrev=0 > info.obj
set /p VERSION=<info.obj
git rev-list --count --first-parent HEAD > info.obj
set /p GIT_REVISION=<info.obj
git rev-parse --abbrev-ref HEAD > info.obj
set /p GIT_BRANCH=<info.obj
date /t > info.obj
set /p LAST_MODIFIED=<info.obj
git --no-pager log --pretty="format:[%%h](https://dev.v4.games/v4games/v4k/commit/%%H): %%s (**%%cN**)" > changelog.txt
rem ...and generate docs
cl tools\docs\docs.c engine\v4k.c -Iengine %2
docs engine\v4k.h --excluded=3rd_glad.h,v4k.h,v4k_compat.h, > v4k.html
move /y v4k.html engine\
del changelog.txt
del info.obj
exit /b
)
rem generate single-header distribution
if "%1"=="amalgamation" (
echo // This file is intended to be consumed by a compiler. Do not read. > v4k.h
echo // **Browse to any of the sources in engine/split/ folder instead** >> v4k.h
echo // ---------------------------------------------------------------- >> v4k.h
echo // #define V4K_IMPLEMENTATION early in **one** C file to unroll the >> v4k.h
echo // implementation. The symbol must be defined in a C (not C++^) file>> v4k.h
echo // ---------------------------------------------------------------- >> v4k.h
echo #pragma once >> v4k.h
type engine\split\3rd_icon_md.h >> v4k.h
type engine\split\3rd_glad.h >> v4k.h
type engine\v4k.h >> v4k.h
echo #ifdef V4K_IMPLEMENTATION >> v4k.h
echo #define V4K_3RD >> v4k.h
type engine\v4k >> v4k.h
type engine\v4k.c >> v4k.h
echo #endif // V4K_IMPLEMENTATION >> v4k.h
move /y v4k.h engine\joint
exit /b
)
rem generate prior files to a git release
if "%1"=="git" (
rem call make.bat dll
call make.bat prep
call make.bat bind
rem call make.bat docs
call make.bat amalgamation
rem call make.bat split
rem rd /q /s engine\split
rem md engine\split
rem move /y v4k_*.? engine\split\
rem move /y 3rd_*.? engine\split\
call make.bat tidy
exit /b
)
if "%1"=="pull" (
git pull
exit /b
)
if "%1"=="depot" (
pushd depot\
git pull
popd
git submodule update --remote --merge depot/
exit /b
)
if "%1"=="dstat" (
pushd depot
git status
popd
exit /b
)
if "%1"=="dpush" (
pushd depot
git add .
if "%2"=="auto" (
git commit -m "asset update"
git push
) else (
git commit
if not "%2"=="local" (
git push
)
)
popd
if not "%2"=="noroot" (
git stash
git add depot
git commit -m "sync depot"
if not "%3"=="local" (
git pull
git push
)
git stash pop
)
exit /b
)
if "%1"=="push" (
call make.bat tidy
git status
if "%2"=="dp" (
call MAKE.bat dpush auto
)
git add .
git commit
if not "%2"=="local" (
git push
)
call make.bat vps
call make.bat tidy
exit /b
)
if "%1"=="prep" (
call make.bat join
call make.bat amalgamation
exit /b
)
rem shortcuts for split & join amalgamation scripts
if "%1"=="split" (
call tools\split
exit /b
)
if "%1"=="join" (
call tools\join
exit /b
)
rem fuse binaries and zipfiles
if "%1"=="fuse" (
setlocal enableDelayedExpansion
if "%2"=="cook" (
del *.zip 2> nul 1> nul & tools\cook --cook-jobs=1
)
for %%i in (*.exe) do set "var=%%i" && if not "!var:~0,6!"=="fused_" ( copy /y !var! fused_!var! 2>nul 1>nul & tools\ark fused_!var! *.zip )
endlocal
exit /b
)
rem check memory api calls
if "%1"=="checkmem" (
findstr /RNC:"[^_xv]realloc[(]" engine\split\v4k*
findstr /RNC:"[^_xv]REALLOC[(]" engine\split\v4k*
findstr /RNC:"[^_xv]MALLOC[(]" engine\split\v4k*
findstr /RNC:"[^_xv]xrealloc[(]" engine\split\v4k*
findstr /RNC:"[^_xv]malloc[(]" engine\split\v4k*
findstr /RNC:"[^_xv]free[(]" engine\split\v4k*
findstr /RNC:"[^_xv]calloc[(]" engine\split\v4k*
findstr /RNC:"[^_xv]strdup[(]" engine\split\v4k*
exit /b
)
if "%1"=="html5" (
pushd demos\html5
call make.bat %2
popd
exit /b
)
if "%1"=="web" (
python demos\html5\host.py --directory demos\html5 --bind 127.0.0.1 8000
exit /b
)
if "%1"=="vps" (
call make.bat docs
tools\pscp -4 -batch -agent -P 22 -l app engine\v4k.html 128.140.14.212:/home/app/microblog/app/static/v4k/index.html
rem tools\pscp -4 -batch -agent -P 22 -l app engine\joint\v4k.h 128.140.14.212:/home/app/microblog/app/static/v4k/v4k.h
exit /b
)
if "%1"=="fwk" (
pushd _mirror
call MAKE.bat sync
popd
call MAKE.bat fwk_prep
start "" fwk_diff.WinMerge
exit /b
)
if "%1"=="fwk_prep" (
if not exist "_fwk" mkdir "_fwk"
if not exist "_fwk\demos" mkdir "_fwk\demos"
if not exist "_fwk\tools" mkdir "_fwk\tools"
if not exist "_fwk\tools\editor" mkdir "_fwk\tools\editor"
if not exist "_fwk\engine" mkdir "_fwk\engine"
if not exist "_fwk\engine\art" mkdir "_fwk\engine\art"
if not exist "_fwk\engine\art\shaders" mkdir "_fwk\engine\art\shaders"
if not exist "_fwk\engine\split" mkdir "_fwk\engine\split"
setlocal enabledelayedexpansion
xcopy /y "*" "_fwk"
xcopy /y "engine\split\3rd_*" "_fwk\engine\split"
xcopy /y "engine\art\shaders\*" "_fwk\engine\art\shaders"
xcopy /y "demos" "_fwk\demos"
rem xcopy /y/E "tools "_fwk\tools"
for %%f in ("engine\split\v4k*") do (
set "filename=%%~nf"
set "newname=fwk!filename:v4k=!%%~xf"
echo Copying and renaming "%%f" to "_fwk\engine\split\!newname!"
copy "%%f" "_fwk\engine\split\!newname!"
)
for %%f in (_fwk\engine\split\*) do (
set "filename=%%~nxf"
if /i not "!filename:~0,4!"=="3rd_" (
echo Processing: %%f
tools\fwkren.exe %%f from
) else (
echo Skipping %%f
)
)
for %%f in (_fwk\demos\*.c) do (
set "filename=%%~nxf"
echo Processing: %%f
tools\fwkren.exe %%f from
)
rem for %%f in (_fwk\tools\*) do (
rem set "filename=%%~nxf"
rem echo Processing: %%f
rem tools\fwkren.exe %%f from
rem )
rem for %%f in (_fwk\tools\editor\*.c) do (
rem set "filename=%%~nxf"
rem echo Processing: %%f
rem tools\fwkren.exe %%f from
rem )
rem tools\fwkren.exe tools\cook.ini from
echo All done.
endlocal
exit /b
)
if "%1"=="back" (
if not exist "_fwk" exit /b
setlocal enabledelayedexpansion
xcopy /y "_fwk" "."
xcopy /y "_fwk\engine\split\3rd_*" "engine\split"
xcopy /y "_fwk\engine\art\shaders\*" "engine\art\shaders"
xcopy /y "_fwk\demos" "demos"
rem xcopy /y/E "_fwk\tools "tools"
for %%f in ("_fwk\engine\split\fwk*") do (
set "filename=%%~nf"
set "newname=v4k!filename:fwk=!%%~xf"
echo Copying and renaming "%%f" to "engine\split\!newname!"
copy "%%f" "engine\split\!newname!"
)
for %%f in (engine\split\*) do (
set "filename=%%~nxf"
if /i not "!filename:~0,4!"=="3rd_" (
echo Processing: %%f
tools\fwkren.exe %%f to
) else (
echo Skipping %%f
)
)
for %%f in (demos\*.c) do (
set "filename=%%~nxf"
echo Processing: %%f
tools\fwkren.exe %%f to
)
rem for %%f in (tools\*.c) do (
rem set "filename=%%~nxf"
rem echo Processing: %%f
rem tools\fwkren.exe %%f to
rem )
rem for %%f in (tools\editor\*.c) do (
rem set "filename=%%~nxf"
rem echo Processing: %%f
rem tools\fwkren.exe %%f to
rem )
rem tools\fwkren.exe tools\cook.ini to
echo All done.
endlocal
exit /b
)
rem copy demos to root folder. local changes are preserved
rem echo n | copy /-y demos\*.c 1> nul 2> nul
rem tidy environment
if "%1"=="tidy" (
move /y ??-*.png demos > nul 2> nul
move /y ??-*.c demos > nul 2> nul
del bind\v4k.dll > nul 2> nul
del .temp*.* > nul 2> nul
del *.zip > nul 2> nul
del *.mem > nul 2> nul
del *.exp > nul 2> nul
del *.exe.manifest > nul 2> nul
del tools\*.exp > nul 2> nul
del *.lib > nul 2> nul
del *.exe > nul 2> nul
del *.obj > nul 2> nul
del tools\*.obj > nul 2> nul
del *.o > nul 2> nul
del *.a > nul 2> nul
del *.pdb > nul 2> nul
del *.ilk > nul 2> nul
del *.png > nul 2> nul
del *.mp4 > nul 2> nul
del *.def > nul 2> nul
del *.dll > nul 2> nul
del 3rd_*.* > nul 2> nul
del v4k_*.* > nul 2> nul
del v4k.html > nul 2> nul
del changelog.txt > nul 2> nul
rem del ??-*.* > nul 2> nul
del temp_*.* > nul 2> nul
rd /q /s .vs > nul 2> nul
rd /q /s _debug > nul 2> nul
rd /q /s _devel > nul 2> nul
rd /q /s _release > nul 2> nul
rd /q /s _fwk > nul 2> nul
rd /q /s _cache > nul 2> nul
rd /q /s _deploy > nul 2> nul
rem rd /q /s _project > nul 2> nul
del tcc.bat > nul 2> nul
del sh.bat > nul 2> nul
exit /b
)
set cc=%cc%
set dll=dll
set build=dev
set args=-Iengine
set run_args=
set other=
set v4k=yes
set hello=no
set demos=no
set lab=no
set editor=no
set vis=no
set proj=no
set rc=0
set run=no
:parse_args
if "%1"=="--" shift && goto parse_compiler_args
if "%1"=="//" shift && goto parse_runtime_args
if "%1"=="dll" set "dll=%1" && goto loop
if "%1"=="static" set "dll=%1" && goto loop
if "%1"=="dbg" set "build=%1" && goto loop
if "%1"=="dev" set "build=%1" && goto loop
if "%1"=="rel" set "build=%1" && goto loop
if "%1"=="ret" set "build=%1" && goto loop
if "%1"=="debug" set "build=dbg" && goto loop
if "%1"=="devel" set "build=dev" && goto loop
if "%1"=="develop" set "build=dev" && goto loop
if "%1"=="developer" set "build=dev" && goto loop
if "%1"=="development" set "build=dev" && goto loop
if "%1"=="release" set "build=rel" && goto loop
if "%1"=="vis" set "vis=yes" && goto loop
if "%1"=="nov4k" set "v4k=no" && goto loop
if "%1"=="nodemos" set "demos=no" && goto loop
if "%1"=="demos" set "demos=yes" && set "hello=no" && goto loop
if "%1"=="lab" set "lab=yes" && set "hello=no" && goto loop
if "%1"=="noeditor" set "editor=no" && goto loop
if "%1"=="hello" set "hello=yes" && goto loop
if "%1"=="editor" set "editor=yes" && set "v4k=no" && set "hello=no"&& goto loop
if "%1"=="run" set "run=yes" && goto loop
if "%1"=="all" set "v4k=yes" && set "demos=yes" && set "lab=yes" && set "editor=yes" && set "hello=yes" && goto loop
if "%1"=="tcc" set "cc=%1" && goto loop
if "%1"=="cl" set "cc=%1" && goto loop
if "%1"=="vc" set "cc=cl" && goto loop
if "%1"=="cc" set "cc=%1" && goto loop
if "%1"=="gcc" set "cc=%1" && goto loop
if "%1"=="clang" set "cc=%1" && goto loop
if "%1"=="clang-cl" set "cc=%1" && goto loop
if "%1"=="proj" set "proj=yes" && goto loop
if not "%1"=="" set "other=!other! %1" && set "editor=no" && set "demos=no"
:loop
if not "%1"=="" shift && goto parse_args
:parse_compiler_args
if not "%1"=="" set "args=!args! %1" && shift && goto parse_compiler_args
:parse_runtime_args
if not "%1"=="" set "run_args=!run_args! %1" && shift && goto parse_runtime_args
set vs=00
rem detect setup
if "!cc!"=="" (
echo Detecting VS 2022/2019/2017/2015/2013 x64 ...
set cc=cl
if exist "%VS170COMNTOOLS%/../../VC/Auxiliary/Build/vcvarsx86_amd64.bat" (
@call "%VS170COMNTOOLS%/../../VC/Auxiliary/Build/vcvarsx86_amd64.bat" > nul && set "vs=22"
) else if exist "%VS160COMNTOOLS%/../../VC/Auxiliary/Build/vcvarsx86_amd64.bat" (
@call "%VS160COMNTOOLS%/../../VC/Auxiliary/Build/vcvarsx86_amd64.bat" > nul && set "vs=19"
) else if exist "%VS150COMNTOOLS%/../../VC/Auxiliary/Build/vcvarsx86_amd64.bat" (
@call "%VS150COMNTOOLS%/../../VC/Auxiliary/Build/vcvarsx86_amd64.bat" > nul && set "vs=17"
) else if exist "%VS140COMNTOOLS%/../../VC/bin/x86_amd64/vcvarsx86_amd64.bat" (
@call "%VS140COMNTOOLS%/../../VC/bin/x86_amd64/vcvarsx86_amd64.bat" > nul && set "vs=15"
) else if exist "%VS120COMNTOOLS%/../../VC/bin/x86_amd64/vcvarsx86_amd64.bat" (
@call "%VS120COMNTOOLS%/../../VC/bin/x86_amd64/vcvarsx86_amd64.bat" > nul && set "vs=13"
) else if exist "%ProgramFiles%/microsoft visual studio/2022/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" (
@call "%ProgramFiles%/microsoft visual studio/2022/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" > nul && set "vs=22"
) else if exist "%ProgramFiles(x86)%/microsoft visual studio/2019/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" (
@call "%ProgramFiles(x86)%/microsoft visual studio/2019/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" > nul && set "vs=19"
) else if exist "%ProgramFiles(x86)%/microsoft visual studio/2017/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" (
@call "%ProgramFiles(x86)%/microsoft visual studio/2017/community/VC/Auxiliary/Build/vcvarsx86_amd64.bat" > nul && set "vs=17"
) else (
echo Detecting Mingw64 ...
set cc=gcc
where /q gcc.exe || ( echo Detecting TCC ... && set "cc=tcc" )
)
)
rem solution. @todo: lin/osx
if "!proj!"=="yes" if not "%vs%"=="00" pushd tools && premake5 vs20%vs% & popd
if "!proj!"=="yes" if "%vs%"=="00" pushd tools && premake5 vs2013 & popd
if "!proj!"=="yes" pushd tools && premake5 ninja & popd
if "!proj!"=="yes" pushd tools && premake5 gmake & popd & exit /b
rem --- pipeline
rem cl tools/ass2iqe.c /Fetools/ass2iqe.exe /nologo /openmp /O2 /Oy /MT /DNDEBUG assimp.lib
rem cl tools/iqe2iqm.cpp /Fetools/iqe2iqm.exe /nologo /openmp /O2 /Oy /MT /DNDEBUG
rem cl tools/mid2wav.c /Fetools/mid2wav.exe /nologo /openmp /O2 /Oy /MT /DNDEBUG
rem cl tools/xml2json.c /Fetools/xml2json.exe /nologo /openmp /O2 /Oy /MT /DNDEBUG
rem --- pipeline
rem gcc tools/ass2iqe.c -o tools/ass2iqe.exe -w -lassimp
rem gcc tools/iqe2iqm.cpp -o tools/iqe2iqm.exe -w -lstdc++
rem gcc tools/mid2wav.c -o tools/mid2wav.exe -w
rem gcc tools/xml2json.c -o tools/xml2json.exe -w
rem --- different strategies for release builds
rem 4.6s 6.9MiB (default)
rem 33.7s 6.6MiB /Ox /Oy /MT /DNDEBUG
rem 35.8s 5.3MiB /O2 /Oy /MT /DNDEBUG
rem 17.9s 4.6MiB /O1 /MT /DNDEBUG /GL /GF /arch:AVX2
rem 17.8s 4.6MiB /Os /Ox /O2 /Oy /MT /DNDEBUG /GL /GF /arch:AVX2
rem 18.8s 4.6MiB /Os /Ox /O2 /Oy /MT /DNDEBUG /GL /GF /Gw /link /OPT:ICF /LTCG
rem 18.0s 4.6MiB /openmp /Os /Ox /O2 /Oy /MT /DNDEBUG /GL /GF /Gw /arch:AVX2 /link /OPT:ICF /LTCG
if "!cc!"=="cl" (
if "!dll!"=="static" (
set export=/c
set import=v4k.obj
) else (
set export=/DAPI=EXPORT /LD
set import=/DAPI=IMPORT v4k.lib
)
if "!build!"=="ret" (
set args=-DENABLE_RETAIL -Dmain=WinMain !args!
set args=/nologo /Zi /MT /openmp /DNDEBUG=3 !args! /Os /Ox /O2 /Oy /GL /GF /Gw /arch:AVX2 /link /OPT:ICF /LTCG
)
if "!build!"=="rel" (
set args=/nologo /Zi /MT /openmp /DNDEBUG=2 !args! /Os /Ox /O2 /Oy /GL /GF /Gw /arch:AVX2 /link /OPT:ICF /LTCG
)
if "!build!"=="dev" (
set args=/nologo /Zi /MT /openmp /DNDEBUG=1 !args! && REM /Os /Ox /O2 /Oy /GL /GF /Gw /arch:AVX2
)
if "!build!"=="dbg" (
set args=/nologo /Zi /MT /DEBUG !args! /Od /fsanitize=address
rem make -- /RTC1, or make -- /Zi /fsanitize=address /DEBUG
)
set o=/Fe:
set echo=REM
) else if "!cc!"=="clang-cl" (
if "!dll!"=="static" (
set export=/c
set import=v4k.obj
) else (
set export=/DAPI=EXPORT /LD
set import=/DAPI=IMPORT v4k.lib
)
set warnings_fwkc=-Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare
set warnings_demos=-Wno-empty-body -Wno-format-security -Wno-pointer-sign
set warnings=!warnings_fwkc! !warnings_demos!
if "!build!"=="ret" (
set args=-DENABLE_RETAIL -Dmain=WinMain !args!
set args=!warnings! /nologo /Zi /MT /openmp /DNDEBUG=3 !args! /Os /Ox /O2 /Oy /GF /Gw /arch:AVX2
)
if "!build!"=="rel" (
set args=!warnings! /nologo /Zi /MT /openmp /DNDEBUG=2 !args! /Os /Ox /O2 /Oy /GF /Gw /arch:AVX2
)
if "!build!"=="dev" (
set args=!warnings! /nologo /Zi /MT /openmp /DNDEBUG=1 !args! && REM /Os /Ox /O2 /Oy /GF /Gw /arch:AVX2
)
if "!build!"=="dbg" (
set args=!warnings! /nologo /Zi /MT /DEBUG !args! /Od /fsanitize=address
)
set o=-o
set echo=echo
) else if "!cc!"=="tcc" (
if "!dll!"=="static" (
set export=-c
set import=v4k.o
) else (
set export=-DAPI=EXPORT -shared
set import=-DAPI=IMPORT v4k.def
)
if "!build!"=="ret" (
set args=-DENABLE_RETAIL -Dmain=WinMain !args!
set args=-O3 -DNDEBUG=3 !args!
)
if "!build!"=="rel" (
set args=-O2 -DNDEBUG=2 !args!
)
if "!build!"=="dev" (
set args=-O1 -DNDEBUG=1 -g !args!
)
if "!build!"=="dbg" (
set args=-O0 -g !args!
)
set o=-o
set echo=echo
) else ( rem if "!cc!"=="gcc" or "clang"
set libs=-lws2_32 -lgdi32 -lwinmm -ldbghelp -lole32 -lshell32 -lcomdlg32
if "!dll!"=="static" (
set export=-c
set import=v4k.o -Wl,--allow-multiple-definition
) else (
set export=-DAPI=EXPORT -shared -o v4k.dll -Wl,--out-implib,v4k.a
set import=-DAPI=IMPORT v4k.a
)
set args=-Wno-implicit-function-declaration !libs! !args!
if "!build!"=="ret" (
set args=-DENABLE_RETAIL !args!
set args=-O3 -DNDEBUG=3 !args!
)
if "!build!"=="rel" (
rem @todo see: https://stackoverflow.com/questions/866721/how-to-generate-gcc-debug-symbol-outside-the-build-target
set args=-O2 -DNDEBUG=2 !args!
)
if "!build!"=="dev" (
set args=-O1 -DNDEBUG=1 -g !args!
)
if "!build!"=="dbg" (
set args=-O0 -g !args!
)
set o=-o
set echo=echo
)
echo build=!build!, type=!dll!, cc=!cc!, other=!other!, args=!args!
echo import=!import!, export=!export!
rem set BUILD_VERSION symbol
git describe --tags --abbrev=0 > info.obj
set /p VERSION=<info.obj
git rev-list --count --first-parent HEAD > info.obj
set /p GIT_REVISION=<info.obj
git rev-parse --abbrev-ref HEAD > info.obj
set /p GIT_BRANCH=<info.obj
date /t > info.obj
set /p LAST_MODIFIED=<info.obj
set args=-DBUILD_VERSION="\"!GIT_BRANCH!-!GIT_REVISION!-!build!-!dll!\"" !args!
if "!cc!"=="tcc" set "cc=call tools\tcc"
rem detect wether user-defined sources use single-header distro
rem if so, remove API=IMPORT flags and also do not produce v4k.dll by default
if not "!other!"=="" (
>nul find "V4K_IMPLEMENTATION" !other! && (
set import=
set v4k=no
)
)
rem framework
if "!v4k!"=="yes" (
tools\file2hash engine\v4k.c engine\v4k.h engine\v4k. engine\joint\v4k.h -- !build! !import! !export! !args! !dll! > nul
set cache=_cache\.!errorlevel!
md _cache 2>nul >nul
rem cache for `make rel` cl:48s->25s, tcc:3.3s->1.8s
if exist !cache!.o copy /y !cache!.o v4k.o 2>nul >nul
if exist !cache!.obj copy /y !cache!.obj v4k.obj 2>nul >nul
if exist !cache!.lib copy /y !cache!.lib v4k.lib 2>nul >nul
if exist !cache!.dll copy /y !cache!.dll v4k.dll 2>nul >nul
if exist !cache!.def copy /y !cache!.def v4k.def 2>nul >nul
if exist !cache!.pdb copy /y !cache!.pdb v4k.pdb 2>nul >nul
if not exist "!cache!" (
!echo! v4k && !cc! engine\v4k.c !export! !args! && if "!dll!"=="dll" copy /y v4k.dll bind\v4k.dll > nul || set rc=1
echo. > !cache!
if exist v4k.o copy /y v4k.o !cache!.o 2>nul >nul
if exist v4k.obj copy /y v4k.obj !cache!.obj 2>nul >nul
if exist v4k.lib copy /y v4k.lib !cache!.lib 2>nul >nul
if exist v4k.dll copy /y v4k.dll !cache!.dll 2>nul >nul
if exist v4k.def copy /y v4k.def !cache!.def 2>nul >nul
if exist v4k.pdb copy /y v4k.pdb !cache!.pdb 2>nul >nul
) else (
rem cached. do not compile...
echo v4k.c ^(cached^)
if "!dll!"=="dll" copy /y !cache!.dll bind\v4k.dll > nul || set rc=1
)
)
rem editor
if "!editor!"=="yes" (
set edit=-DCOOK_ON_DEMAND -DUI_LESSER_SPACING -DUI_ICONS_SMALL
rem if "!vis!"=="yes" echo !cc! !o! editor.exe tools\editor\editor.c !edit! !import! !args!
rem !echo! editor && !cc! !o! editor.exe tools\editor\editor.c !edit! !import! !args! || set rc=1
rem !echo! editor2 && !cc! !o! editor2.exe tools\editor\editor2.c !edit! !args! || set rc=1
!echo! v4k && !cc! engine\v4k.c !export! !edit! !args! || set rc=1
if "!cc!"=="cl" (
set plug_export=/LD
) else if "!cc!"=="clang-cl" (
set plug_export=/LD
) else (
set plug_export=-shared
)
for %%f in ("workbench\plugins\*.c") do (
!echo! %%~nf && !cc! !o! %%~nf.dll %%f -Iworkbench !plug_export! !args! !import! || set rc=1
)
!echo! workbench && !cc! !o! workbench.exe workbench\workbench.c -Iworkbench !args! !import! || set rc=1
)
rem demos
if "!demos!"=="yes" (
!echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1
!echo! 00-script && !cc! !o! 00-script.exe demos\00-script.c !import! !args! || set rc=1
!echo! 00-hello && !cc! !o! 00-hello.exe demos\00-hello.c !import! !args! || set rc=1
!echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1
!echo! 01-demo2d && !cc! !o! 01-demo2d.exe demos\01-demo2d.c !import! !args! || set rc=1
!echo! 01-easing && !cc! !o! 01-easing.exe demos\01-easing.c !import! !args! || set rc=1
!echo! 01-font && !cc! !o! 01-font.exe demos\01-font.c !import! !args! || set rc=1
!echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1
!echo! 02-frustum && !cc! !o! 02-frustum.exe demos\02-frustum.c !import! !args! || set rc=1
!echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1
!echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1
!echo! 04-lod && !cc! !o! 04-lod.exe demos\04-lod.c !import! !args! || set rc=1
!echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1
!echo! 06-controller && !cc! !o! 06-controller.exe demos\06-controller.c !import! !args! || set rc=1
!echo! 06-material && !cc! !o! 06-material.exe demos\06-material.c !import! !args! || set rc=1
!echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1
!echo! 07-netsync && !cc! !o! 07-netsync.exe demos\07-netsync.c !import! !args! || set rc=1
!echo! 08-audio && !cc! !o! 08-audio.exe demos\08-audio.c !import! !args! || set rc=1
!echo! 08-video && !cc! !o! 08-video.exe demos\08-video.c !import! !args! || set rc=1
!echo! 09-cubemap && !cc! !o! 09-cubemap.exe demos\09-cubemap.c !import! !args! || set rc=1
!echo! 09-shadertoy && !cc! !o! 09-shadertoy.exe demos\09-shadertoy.c !import! !args! || set rc=1
)
rem lab
if "!lab!"=="yes" (
for %%f in ("demos\99-*") do (
set limport=!import!
>nul find "V4K_IMPLEMENTATION" demos\%%~nf.c && (
set limport=
)
!echo! %%~nf && !cc! !o! %%~nf.exe demos\%%~nf.c !limport! !args! || set rc=1
)
)
rem hello
if "!hello!"=="yes" (
!echo! hello && !cc! !o! hello.exe hello.c !args! || set rc=1
)
rem user-defined apps
if not "!other!"=="" (
if "!vis!"=="yes" echo !cc! !other! !import! !args!
rem if "!cc!"=="cl" (
rem if "!build!"=="rel" (
rem set "import=!import! engine\v4k_win32_rel_glue.c"
rem set "args=!args! /SUBSYSTEM:WINDOWS"
rem )
rem )
!echo! !other! && !cc! !other! !import! !args! || set rc=1
)
if "!run!"=="yes" (
set exename=hello.exe
if not "!other!"=="" (
for /f "tokens=*" %%a in ("!other!") do set exename=%%~na.exe
)
echo run !exename! !run_args!
!exename! !run_args! || set rc=1
)
rem PAUSE only if double-clicked from Windows explorer
(((echo.%cmdcmdline%)|%WINDIR%\system32\find.exe /I "%~0")>nul)&&pause
cmd /c exit !rc!

273
_fwk/MAKE.sh 100644
View File

@ -0,0 +1,273 @@
#!/bin/bash
# linux + osx -----------------------------------------------------------------
cd `dirname $0`
# copy demos to root folder. local changes are preserved
# cp -n demos/*.c .
# rem tests
# clang editor.c -I. -lm -lX11 -g -fsanitize=address,undefined && ./a.out
# cl editor.c -I. -fsanitize=address /DEBUG /Zi && editor
# tidy environment
if [ "$1" = "tidy" ]; then
rm 0?-* 2> /dev/null
rm v4k.o 2> /dev/null
rm .art*.zip 2> /dev/null
rm demos/lua/.art*.zip 2> /dev/null
rm demos/lua/libv4k* 2> /dev/null
rm demos/html5/.art*.zip 2> /dev/null
rm v4k_*.* 2> /dev/null
rm 3rd_*.* 2> /dev/null
rm libv4k* 2> /dev/null
rm -rf *.dSYM 2> /dev/null
rm *.png 2> /dev/null
rm *.mp4 2> /dev/null
rm editor 2> /dev/null
rm temp_* 2> /dev/null
rm hello 2> /dev/null
exit
fi
# shortcuts for split & join scripts
if [ "$1" = "split" ]; then
sh tools/split.bat
exit
fi
if [ "$1" = "join" ]; then
sh tools/join.bat
exit
fi
# cook
if [ "$1" = "cook" ]; then
cc -o cook tools/cook.c -Iengine
./cook
exit
fi
# sync
if [ "$1" = "sync" ]; then
git reset --hard HEAD^1 && git pull
sh MAKE.bat tidy
exit
fi
export dll=dll
export build=dev
export args=
export cc=cc
export app=$1
while [ $# -ge 1 ]; do
if [ "$1" = "help" ]; then
echo sh MAKE.bat
echo sh MAKE.bat [gcc,clang,tcc] [dbg,dev,rel] [dll,static]
echo sh MAKE.bat [tidy]
echo sh MAKE.bat [split,join]
echo sh MAKE.bat [cook]
echo sh MAKE.bat [proj]
exit
fi
if [ "$1" = "dll" ]; then
export dll=dll
fi
if [ "$1" = "static" ]; then
export dll=static
fi
if [ "$1" = "dbg" ]; then
export build=dbg
export flags="-O0 -g"
fi
if [ "$1" = "dev" ]; then
export build=dev
export flags="-O1 -g"
fi
if [ "$1" = "rel" ]; then
export build=rel
export flags="-O3 -DNDEBUG"
fi
if [ "$1" = "gcc" ]; then
export cc=gcc
fi
if [ "$1" = "clang" ]; then
export cc=clang
fi
if [ "$1" = "tcc" ]; then
export cc="tcc -D__STDC_NO_VLA__"
fi
if [ "$1" = "proj" ]; then
if [ "$(uname)" != "Darwin" ]; then
chmod +x tools/premake5.linux
tools/premake5.linux gmake
tools/premake5.linux ninja
exit
fi
if [ "$(uname)" = "Darwin" ]; then
chmod +x tools/premake5.osx
tools/premake5.osx xcode4
tools/premake5.osx ninja
exit
fi
fi
if [ "$1" = "--" ]; then
shift
export args=$*
shift $#
fi
if [ $# -ge 1 ]; then
shift
fi
done
if [ "$(uname)" != "Darwin" ]; then
# setup (ArchLinux)
[ ! -f ".setup" ] && sudo pacman -S --noconfirm tcc && echo>.setup
# setup (Debian, Ubuntu, etc)
[ ! -f ".setup" ] && sudo apt-get -y update
[ ! -f ".setup" ] && sudo apt-get -y install tcc libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev && echo>.setup # absolute minimum
# sudo apt-get -y install clang xorg-dev # memorable, around 100 mib
# sudo apt-get -y install clang xorg-dev libglfw3-dev libassimp-dev gcc # initial revision
# sudo apt-get -y install ffmpeg || (sudo apt-get install snapd && sudo snap install ffmpeg) # variant
# pipeline
#cc tools/ass2iqe.c -o tools/ass2iqe.linux -lm -ldl -lpthread -w -g -lassimp
#cc tools/iqe2iqm.cpp -o tools/iqe2iqm.linux -lm -ldl -lpthread -w -g -lstdc++
#cc tools/mid2wav.c -o tools/mid2wav.linux -lm -ldl -lpthread -w -g
# change permissions of precompiled tools binaries because of 'Permission denied' runtime error (@procedural)
chmod +x tools/ass2iqe.linux
chmod +x tools/cook.linux
chmod +x tools/cuttlefish.linux
chmod +x tools/ffmpeg.linux
chmod +x tools/furnace.linux
chmod +x tools/iqe2iqm.linux
chmod +x tools/mid2wav.linux
chmod +x tools/mod2wav.linux
chmod +x tools/PVRTexToolCLI.linux
chmod +x tools/sfxr2wav.linux
chmod +x tools/xlsx2ini.linux
chmod +x tools/premake5.linux
chmod +x tools/ninja.linux
chmod +x demos/lua/luajit.linux
export args="-lm -ldl -lpthread -lX11 -w -Iengine/ $args"
echo build=$build, type=$dll, cc=$cc, args=$args
# framework (as dynamic library)
if [ "$dll" = "dll" ]; then
echo libv4k.so && $cc -o libv4k.so engine/v4k.c -shared -fPIC $flags $args
cp libv4k.so demos/lua/
export import="libv4k.so -Wl,-rpath,./"
else
# framework (static)
echo v4k && $cc -c engine/v4k.c -w $flags $args
export import=v4k.o
fi
# editor
echo editor && $cc -o editor tools/editor/editor.c $flags $import $args &
# demos
echo hello && $cc -o hello hello.c $flags $args &
echo 00-loop && $cc -o 00-loop demos/00-loop.c $flags $import $args &
echo 00-script && $cc -o 00-script demos/00-script.c $flags $import $args &
echo 01-demo2d && $cc -o 01-demo2d demos/01-demo2d.c $flags $import $args &
echo 01-ui && $cc -o 01-ui demos/01-ui.c $flags $import $args &
echo 01-easing && $cc -o 01-easing demos/01-easing.c $flags $import $args &
echo 01-font && $cc -o 01-font demos/01-font.c $flags $import $args &
echo 02-ddraw && $cc -o 02-ddraw demos/02-ddraw.c $flags $import $args &
echo 02-frustum && $cc -o 02-frustum demos/02-frustum.c $flags $import $args &
echo 03-anims && $cc -o 03-anims demos/03-anims.c $flags $import $args &
echo 04-actor && $cc -o 04-actor demos/04-actor.c $flags $import $args &
echo 06-scene && $cc -o 06-scene demos/06-scene.c $flags $import $args &
echo 07-netsync && $cc -o 07-netsync demos/07-netsync.c $flags $import $args &
echo 06-material && $cc -o 06-material demos/06-material.c $flags $import $args &
echo 07-network && $cc -o 07-network demos/07-network.c $flags $import $args &
echo 08-audio && $cc -o 08-audio demos/08-audio.c $flags $import $args &
echo 08-video && $cc -o 08-video demos/08-video.c $flags $import $args &
echo 09-cubemap && $cc -o 09-cubemap demos/09-cubemap.c $flags $import $args &
echo 09-shadertoy && $cc -o 09-shadertoy demos/09-shadertoy.c $flags $import $args
fi
if [ "$(uname)" = "Darwin" ]; then
# setup (osx)
export SDKROOT=$(xcrun --show-sdk-path)
# brew install glfw
# pipeline
#cc tools/ass2iqe.c -o tools/ass2iqe.osx -w -g -lassimp
#cc tools/iqe2iqm.cpp -o tools/iqe2iqm.osx -w -g -lstdc++
#cc tools/mid2wav.c -o tools/mid2wav.osx -w -g
# change permissions of precompiled tools binaries because of 'Permission denied' runtime error (@procedural)
chmod +x tools/ass2iqe.osx
chmod +x tools/cook.osx
chmod +x tools/cuttlefish.osx
chmod +x tools/ffmpeg.osx
chmod +x tools/furnace.osx
chmod +x tools/iqe2iqm.osx
chmod +x tools/mid2wav.osx
chmod +x tools/mod2wav.osx
chmod +x tools/PVRTexToolCLI.osx
chmod +x tools/sfxr2wav.osx
chmod +x tools/xlsx2ini.osx
chmod +x tools/premake5.osx
chmod +x tools/ninja.osx
chmod +x demos/lua/luajit.osx
export args="-w -Iengine/ -framework cocoa -framework iokit -framework CoreFoundation -framework CoreAudio -framework AudioToolbox $args $flags"
echo build=$build, type=$dll, cc=$cc, args=$args
# framework (as dynamic library)
if [ "$dll" = "dll" ]; then
echo libv4k && cc -ObjC -dynamiclib -o libv4k.dylib engine/v4k.c $flags $args
cp libv4k.dylib demos/lua
export import=libv4k.dylib
else
# framework
echo v4k && cc -c -ObjC engine/v4k.c $flags $args
export import=v4k.o
fi
# User-defined apps
if [ -n "$app" ]; then
echo "$app" && $cc -ObjC "$app" libv4k.dylib $args -o "v4k.osx" || rc=1
# echo "$app" && $cc -ObjC "$app" engine/v4k.c $args -o "v4k.osx" || rc=1
fi
# if [ "$run" == "yes" ]; then
# exename="$1.osx"
# if [ -n "$other" ]; then
# exename=$(basename "$other" .exe)
# fi
# echo "run $exename $run_args"
# ./"$exename" $run_args || rc=1
# fi
# # editor
# echo editor && cc -o editor tools/editor/editor.c $import $flags $args &
# # demos
# echo hello && cc -o hello -ObjC hello.c $flags $args &
# echo 00-loop && cc -o 00-loop demos/00-loop.c $import $flags $args &
# echo 00-script && cc -o 00-script demos/00-script.c $import $flags $args &
# echo 01-demo2d && cc -o 01-demo2d demos/01-demo2d.c $import $flags $args &
# echo 01-ui && cc -o 01-ui demos/01-ui.c $import $flags $args &
# echo 01-easing && cc -o 01-easing demos/01-easing.c $import $flags $args &
# echo 01-font && cc -o 01-font demos/01-font.c $import $flags $args &
# echo 02-ddraw && cc -o 02-ddraw demos/02-ddraw.c $import $flags $args &
# echo 02-frustum && cc -o 02-frustum demos/02-frustum.c $import $flags $args &
# echo 03-anims && cc -o 03-anims demos/03-anims.c $import $flags $args &
# echo 04-actor && cc -o 04-actor demos/04-actor.c $import $flags $args &
# echo 06-scene && cc -o 06-scene demos/06-scene.c $import $flags $args &
# echo 07-netsync && cc -o 07-netsync demos/07-netsync.c $import $flags $args &
# echo 06-material && cc -o 06-material demos/06-material.c $import $flags $args &
# echo 07-network && cc -o 07-network demos/07-network.c $import $flags $args &
# echo 08-audio && cc -o 08-audio demos/08-audio.c $import $flags $args &
# echo 08-video && cc -o 08-video demos/08-video.c $import $flags $args &
# echo 09-cubemap && cc -o 09-cubemap demos/09-cubemap.c $import $flags $args &
# echo 09-shadertoy && cc -o 09-shadertoy demos/09-shadertoy.c $import $flags $args
fi
exit

185
_fwk/README.md 100644
View File

@ -0,0 +1,185 @@
<h1 align="center">V·4·K</h1>
<p align="center">
Envision, Prototype, Perfect<br/>
</p>
## Features
- [x] Pipeline: configurable and integrated [asset pipeline](tools/cook.ini).
- [x] Embedded: single-file header, all dependencies included.
- [x] Compiler: MSVC, MINGW64, TCC, GCC, clang, clang-cl and emscripten.
- [x] Linkage: Both static linkage and dynamic .dll/.so/.dylib support.
- [x] Platform: Windows, Linux and OSX. Partial HTML5/Web support.
- [x] DS: hash, sort, array/vector, map, set.
- [x] Math: rand, noise, ease, vec2/3/4, mat33/34/44, quat.
- [x] Geometry: ray, line, plane, aabb, sphere, capsule, triangle, poly and frustum.
- [x] Window: windowed, soft/hard fullscreen, msaa, icon, cursor handling.
- [x] Input: keyboard, mouse and gamepads.
- [x] Script: Lua scripting, Luajit bindings.
- [x] Network: downloads (HTTPS) and sockets (TCP/UDP). <!-- [*] Object, GameObject, W/ECS -->
- [x] Network: Game sync module
- [x] AI: Swarm/Boids.
- [x] UI: button, list, slider, toggle, checkbox, editbox, dialog, color, image, menu, window, notify...
- [x] Font: TTF, OTF and TTC. Basic syntax highlighter. Glyph ranges. Atlasing.
- [x] Localization/I18N: XLSX and INI. Unicode.
- [x] Image: JPG, PNG, BMP, PSD, PIC, PNM, ICO.
- [x] Texture: KTX/2, PVR, DDS, ASTC, BASIS, HDR, TGA.
- [x] Texel: Depth, R, RG, RGB, RGBA, BC1/2/3/4/5/6/7, PVRI/II, ETC1/2, ASTC.
- [x] Audio: WAV/FLAC, OGG/MP1/MP3, FUR, MOD/XM/S3M/IT, SFXR and MID+SF2/SF3.
- [x] Video: MP4, MPG, OGV, MKV, WMV and AVI. Also, MP4 recording with MPEG-1 fallback.
- [x] Model: IQM/E, GLTF/2, GLB, FBX, OBJ, DAE, BLEND, MD3/5, MS3D, SMD, X, 3DS, BVH, DXF, LWO.
- [x] Render: PBR (metallic-roughness) workflow. <!-- @todo: merge demo_pbr.c rendering code into v4k_render.c -->
- [x] Render: Cubemaps, panoramas and spherical harmonics. Rayleigh/Mie scattering.
- [x] Render: Post-effects (SSAO,FXAA1/3,CRT,Contrast,Grain,Outline,Vignette...).
- [x] Render: 3D Anims, skeletal anims, hardware skinning and instanced rendering.
- [x] Render: 3D Debugdraw, batching and vectorial font.
- [x] Render: 2D Sprites, spritesheets, AA zooming and batching.
- [x] Render: 2D Tilemaps and tilesets: TMX, TSX.
- [x] Render: Compute shaders and SSBO support.
- [x] Render: Geometry shaders.
- [x] Compression: DEFLATE, LZMA, LZ4, ULZ, BALZ, BCM, CRUSH, LZW3, LZSS and PPP.
- [x] Virtual filesystem: ZIP, PAK, TAR and DIR.
- [x] Level data: JSON, JSON5, SJSON, XML, INI.
- [x] Disk cache.
- [x] Scene handling.
- [x] Profiler, stats and leaks finder.
- [x] [Documentation (wip)](https://v4k.dev).
## Hello V4K
```C
#include "v4k.h" // Minimal C sample
int main() {
window_create(75.0, 0); // 75% size, no extra flags
while( window_swap() && !input(KEY_ESC) ) { // game loop
puts("hello V4K from C!");
}
}
```
```lua
local v4k = require("v4k") -- Minimal Lua sample
v4k.window_create(75.0,0) -- 75% size, no extra flags
while v4k.window_swap() and v4k.input(v4k.KEY_ESC) == 0 do -- game loop
print("hello V4K from Lua!")
end
```
## Quickstart
- Double-click `MAKE.bat` to quick start.
- `MAKE.bat all` to build everything.
- `MAKE.bat proj` to generate solutions/makefiles.
- `MAKE.bat help` for a bunch of options.
- `MAKE.bat hello.c` to build a single executable.
- Alternatively,
```bat
echo win/vc && cl hello.c
echo win/clang-cl && clang-cl hello.c
echo win/tcc && tools\tcc hello.c -m64
echo win/mingw && gcc hello.c -lws2_32 -lwinmm -ldbghelp -lole32 -luser32 -lgdi32 -lcomdlg32
echo win/clang && clang hello.c -lws2_32 -lwinmm -ldbghelp -lole32 -luser32 -lgdi32 -lcomdlg32
echo linux && cc hello.c -lm -ldl -lpthread -lX11
echo linux/tcc && tcc hello.c -lm -ldl -lpthread -lX11 -D__STDC_NO_VLA__
echo osx && cc -ObjC hello.c -framework cocoa -framework iokit -framework audiotoolbox
```
## Cook
- Assets need to be cooked before being consumed in your application. The [tools/](tools/) folder contains all the related binaries to perform any asset processing plus the [cookbook](tools/cook.ini) to do so.
- Your game will cook all your assets as long as the [`tools/`](tools/) folder is next to your executable. Alternatively, cook them all just by invoking supplied [`tools/cook` standalone binary](tools/).
- In both cases, assets will be cooked and packed into .zipfiles next to your executable, then mounted before entering game loop. These .zipfiles plus your executable are the only required files when releasing your game.
## Extra tips
- Any ico/png file named after the executable name will be automatically used as app icon.
- Similar to the ico/png case above, the cooked .zipfiles can be named after the main executable as well.
- Dropped files into game window will be imported & saved into [`import/`](engine/art/import/) folder.
- Update the gamepad controller database by upgrading the [`gamecontrollerdb.txt`](engine/art/input/) file.
- Depending on your IDE, you might need to browse to [`engine/split/`](engine/split/) sources when debugging FWK.
- Cook assets on demand, as opposed to cook all existing assets on depot, by using `--cook-on-demand` flag.
- Linux/OSX users can optionally install wine and use the Windows tools instead (by using `--cook-wine` flag).
- Disable automatic cooking by using `--cook-jobs=0` flag (not recommended).
- Generate a project solution by dropping `engine/v4k.h, v4k.c and v4k` files into it.
- Auto-generated Luajit and Python bindings can be found in the [`engine/bind/`](engine/bind/) folder.
<!-- - On windows + vc, you can use `make bindings` or `make docs` to generate everything prior to a release -->
<!-- - Note: Windows: Assimp.dll may need [this package installed](https://www.microsoft.com/en-us/download/confirmation.aspx?id=30679).-->
## Credits
**Original** Big big thanks to [r-lyeh](https://github.com/r-lyeh/fwk) for the amazing [FWK](https://github.com/r-lyeh/FWK) base!<br/><br/>
**Artwork**
[Dean Evans, Raijin](https://youtu.be/RRvYkrrpMKo?t=147) for the Map song (c),
[FMS_Cat](https://gist.github.com/FMS-Cat/a1ccea3ce866c34706084e3526204f4f) for nicest VHS/VCR shader around (MIT),
[Goblin165cm](https://sketchfab.com/3d-models/halloween-little-witch-ccc023590bfb4789af9322864e42d1ab) for witch 3D model (CC BY 4.0),
[Nuulbee](https://sketchfab.com/3d-models/kgirls01-d2f946f58a8040ae993cda70c97b302c) for kgirls01 3D model (CC BY-NC-ND 4.0),
[Quaternius](https://www.patreon.com/quaternius) for the lovely 3D robots (CC0),
[Rotting Pixels](https://opengameart.org/content/2d-castle-platformer-tileset-16x16) for castle-tileset (CC0),
[Tom Lewandowski](https://QuestStudios.com) for his MIDI recordings (c),
[Rye Terrell](https://github.com/wwwtyro/glsl-atmosphere) for nicest rayleigh/mie scattering shader around (CC0),
**Tools**
[Aaron Barany](https://github.com/akb825/Cuttlefish) for cuttlefish (APACHE2),
[Arseny Kapoulkine](https://github.com/zeux/pugixml/) for pugixml (MIT),
[Assimp authors](https://github.com/assimp/assimp) for assimp (BSD3),
[Bernhard Schelling](https://github.com/schellingb/TinySoundFont) for tml.h (Zlib) and tsf.h (MIT),
[Christian Collins](http://www.schristiancollins.com) for GeneralUser GS soundfont (PD),
[FFMPEG authors](https://www.ffmpeg.org/) for ffmpeg (LGPL21),
[Imagination](https://developer.imaginationtech.com/pvrtextool/) for pvrtextoolcli (ITL),
[Krzysztof Gabis](https://github.com/kgabis/ape) for split.py/join.py (MIT),
[Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo) for iqm.cpp (PD),
[Martín Lucas Golini](https://github.com/SpartanJ/eepp/commit/8552941da19380d7a629c4da80a976aec5d39e5c) for emscripten-fs.html (CC0),
[Mattias Gustavsson](https://github.com/mattiasgustavsson/libs) for mid.h (PD),
[Michael Schmoock](http://github.com/willsteel/lcpp) for lcpp (MIT),
[Morgan McGuire](https://casual-effects.com/markdeep/) for markdeep (BSD2),
[Olivier Lapicque, Konstanty Bialkowski](https://github.com/Konstanty/libmodplug) for libmodplug (PD),
[Polyglot Team](https://docs.google.com/spreadsheets/d/17f0dQawb-s_Fd7DHgmVvJoEGDMH_yoSd8EYigrb0zmM/edit) for polyglot gamedev (CC0),
[Tildearrow](https://github.com/tildearrow/furnace/) for Furnace (GPL2),
[Tomas Pettersson](http://www.drpetter.se/) for sfxr (PD),
[Tor Andersson](https://github.com/ccxvii/asstools) for assiqe.c (BSD),
**Runtime**
[Andreas Mantler](https://github.com/ands) for their math library (PD),
[Barerose](https://github.com/barerose) for swrap (CC0) and math library (CC0),
[Camilla Löwy](https://github.com/elmindreda) for glfw3 (Zlib),
[Dave Rand](https://tools.ietf.org/html/rfc1978) for ppp (PD),
[David Herberth](https://github.com/dav1dde/) for glad generated code (PD),
[David Reid](https://github.com/mackron) for miniaudio (PD),
[Dominic Szablewski](https://github.com/phoboslab/pl_mpeg) for pl_mpeg (MIT),
[Dominik Madarász](https://github.com/zaklaus) for json5 parser (PD),
[Eduard Suica](https://github.com/eduardsui/tlse) for tlse (PD),
[Evan Wallace](https://github.com/evanw) for their math library (CC0),
[Gargaj+cce/Peisik](https://github.com/gargaj/foxotron) for Foxotron/PBR shaders (UNLICENSE),
[Guilherme Lampert](https://github.com/glampert) for their math library (PD),
[Guillaume Vareille](http://tinyfiledialogs.sourceforge.net) for tinyfiledialogs (ZLIB),
[Haruhiko Okumura](https://oku.edu.mie-u.ac.jp/~okumura/compression/) for lzss (PD),
[Igor Pavlov](https://www.7-zip.org/) for LZMA (PD),
[Ilya Muravyov](https://github.com/encode84) for bcm, balz, crush, ulz, lz4x (PD),
[Jon Olick](https://www.jonolick.com/) for jo_mp1 and jo_mpeg (PD),
[Joonas Pihlajamaa](https://github.com/jokkebk/JUnzip) for JUnzip library (PD),
[Juliette Focault](https://github.com/juliettef/IconFontCppHeaders/blob/main/IconsMaterialDesign.h) for the generated MD header (ZLIB),
[Kristoffer Grönlund](https://github.com/krig) for their math library (CC0),
[Lee Salzman](https://github.com/lsalzman/iqm/tree/5882b8c32fa622eba3861a621bb715d693573420/demo) for IQM spec & player (PD),
[Lee Salzman, V.Hrytsenko, D.Madarász](https://github.com/zpl-c/enet/) for enet (MIT),
[Libtomcrypt](https://github.com/libtom/libtomcrypt) for libtomcrypt (Unlicense),
[Lua authors](https://www.lua.org/) for Lua language (MIT),
[Mattias Gustavsson](https://github.com/mattiasgustavsson/libs) for thread.h and https.h (PD),
[Micha Mettke](https://github.com/vurtun) for their math library (PD),
[Micha Mettke, Chris Willcocks, Dmitry Hrabrov](https://github.com/vurtun/nuklear) for nuklear (PD),
[Michael Galetzka](https://github.com/Cultrarius/Swarmz) for swarmz (UNLICENSE),
[Morten Vassvik](https://github.com/vassvik/mv_easy_font) for mv_easy_font (Unlicense),
[Mārtiņš Možeiko](https://gist.github.com/mmozeiko/68f0a8459ef2f98bcd879158011cc275) for A* pathfinding (PD),
[Omar Cornut, vaiorabbit](https://github.com/ocornut/imgui/pull/3627) for tables of unicode ranges (MIT-0),
[Rabia Alhaffar](https://github.com/Rabios/ice_libs) for ice_batt.h (PD),
[Rich Geldreich](https://github.com/richgel999/miniz) for miniz (PD),
[Ross Williams](http://ross.net/compression/lzrw3a.html) for lzrw3a (PD),
[Samuli Raivio](https://github.com/bqqbarbhg/bq_websocket) for bq_websocket (PD),
[Sean Barrett](https://github.com/nothings) for stb_image, stb_image_write, stb_sprintf, stb_truetype and stb_vorbis (PD),
[Sebastian Steinhauer](https://github.com/kieselsteini) for sts_mixer (PD),
[Stan Melax, Cloud Wu](https://web.archive.org/web/20031204035320/http://www.melax.com/polychop/gdmag.pdf) for polychop C algorithm (PD),
[Stefan Gustavson](https://github.com/stegu/perlin-noise) for simplex noise (PD),
[Sterling Orsten](https://github.com/sgorsten) for their math library (UNLICENSE),
[Tor Andersson](https://github.com/ccxvii/minilibs) for xml.c (PD),
[Wolfgang Draxinger](https://github.com/datenwolf) for their math library (WTFPL2),
<!--
- [DavidLam](https://en.wikipedia.org/wiki/Tokamak_(software) "for tokamak physics engine (ZLIB)")
- [ID Software, David St-Louis](https://github.com/Daivuk/PureDOOM "for PureDOOM (Doom License)")
- [Miloslav Číž](https://codeberg.org/drummyfish/Anarch "for Anarch (CC0)")
- [Rxi](https://github.com/rxi/autobatch "for lovely sprites & cats demo (MIT)")
-->
## Unlicense
This software is released into the [public domain](https://unlicense.org/). Also dual-licensed as [0-BSD](https://opensource.org/licenses/0BSD) or [MIT (No Attribution)](https://github.com/aws/mit-0) for those countries where public domain is a concern (sigh). Any contribution to this repository is implicitly subjected to the same release conditions aforementioned.

View File

@ -0,0 +1,7 @@
#include "fwk.h" // Minimal C sample
int main() {
window_create(75.0, 0); // 75% size, no extra flags
while( window_swap() && !input(KEY_ESC) ) { // game loop
puts("hello FWK from C!");
}
}

View File

@ -0,0 +1,31 @@
#include "fwk.h"
#define SCRIPT(...) #__VA_ARGS__
#if 0 // teal
script_run("local tl=require(\"tl\")\ntl.loader()");
script_run("addsub = require(\"s2\"); print (addsub.add(10, 20))");
s2.tl:
local function add(a: number, b: number): number
return a + b
end
local s = add(1,2)
print(s)
#endif
int main() {
script_init();
script_run(SCRIPT(
window_create(75.0,0);
while window_swap() do
ddraw_grid(10);
if ui_panel("Hello from Lua!", 0) then
ui_panel_end();
end;
end;
));
// script test (lua)
script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" );
}

86
_fwk/demos/00-ui.c 100644
View File

@ -0,0 +1,86 @@
// hello ui: config, window, system, ui, video
// - rlyeh, public domain.
//
// Compile with:
// `make demos\01-ui.c` (windows)
// `sh MAKE.bat demos/01-ui.c` (linux, osx)
#include "fwk.h"
int main() {
float app_volume = 1.00f;
unsigned app_size = flag("--fullscreen") ? 100 : 75;
unsigned app_flags = flag("--msaa") ? WINDOW_MSAA4 : 0;
unsigned app_target_fps = optioni("--fps", 60); // --fps=30, --fps=45, etc. defaults to 60.
// window api (fullscreen or 75% sized, optional MSAA flags)
window_create(app_size, app_flags);
window_title(__FILE__);
window_fps_lock(app_target_fps);
// load video
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_LOOP );
// app loop
while( window_swap() ) {
// input controls
if( input(KEY_ESC) ) break;
// profile api
texture_t *textures;
profile( "Video decoder" ) {
// video api: decode frame and get associated textures (audio is sent to audiomixer automatically)
textures = video_decode( v );
// fullscreen video
// if(video_is_rgb(v)) fullscreen_quad_rgb( textures[0], 1.3f );
// else fullscreen_quad_ycbcr( textures, 1.3f );
}
// create menubar on top
int choice1 = ui_menu("File;Shell;Exit");
int choice2 = ui_menu("Help;About");
if( choice1 == 1 ) system(ifdef(win32, "start \"\" cmd", ifdef(osx, "open sh", "xdg-open sh")));
if( choice1 == 2 ) exit(0);
// showcase a few ui widgets
ui_demo(0);
// create ui panel
if( ui_panel("myPanel", PANEL_OPEN) ) {
// Print some numbers
ui_section("Stats");
ui_label2("FPS", va("%5.2f", window_fps()));
ui_separator();
// add some buttons
ui_section("Buttons");
if( ui_button("Screenshot") ) window_screenshot(__FILE__ ".png"), ui_notify(0,ICON_MD_WARNING "Screenshot");
if( ui_button("Record Video") ) window_record(__FILE__ ".mp4"), ui_notify(0,ICON_MD_WARNING "Recoding video");
if( ui_button("Toggle fullscreen") ) window_fullscreen( !window_has_fullscreen() );
ui_separator();
// some more video controls
ui_section("Video");
if( ui_button("Rewind") ) video_seek(v, video_position(v) - 3);
if( ui_button("Pause") ) video_pause(v, video_is_paused(v) ^ 1);
if( ui_button("Forward") ) video_seek(v, video_position(v) + 3);
if( ui_slider2("Volume", &app_volume, va("%.2f", app_volume))) audio_volume_master(app_volume);
// end of panel. must be enclosed within same if() branch.
ui_panel_end();
}
// create window
static int open = 1;
if( ui_window("myWindow", &open) ) {
// present decoded texture in a widget, without any label (NULL)
ui_texture( NULL, textures[0] );
// end of window. must be enclosed within same if() branch.
ui_window_end();
}
}
}
// this demo supersedes following old sources:
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-hello.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-video.c

View File

@ -0,0 +1,155 @@
// 2d demo: window, audio, camera, font, tiled, render, fx, spritesheet, input, ui. @todo: spine
// - rlyeh, public domain.
//
// Compile with:
// `make demos\01-demo2d.c` (windows)
// `sh MAKE.bat demos/01-demo2d.c` (linux, osx)
#include "fwk.h"
void demo_kids(vec3 offs) {
// init
static texture_t kids; do_once kids = texture( "spriteSheetExample.png", TEXTURE_LINEAR );
static vec3 pos[2] = {{490,362},{442,362}}, vel[2] = {0};
static int row[2] = {0,3}, frame[2] = {0};
static int inputs[2][4] = {{KEY_W,KEY_A,KEY_S,KEY_D},{KEY_UP,KEY_LEFT,KEY_DOWN,KEY_RIGHT}};
// move
for( int i = 0; i < countof(pos); ++i ) {
vel[i].x = input(inputs[i][3]) - input(inputs[i][1]);
vel[i].y = input(inputs[i][2]) - input(inputs[i][0]);
pos[i].x = fmod(pos[i].x + vel[i].x, window_width() + 128);
pos[i].y = fmod(pos[i].y + vel[i].y, window_height() + 128);
frame[i] += vel[i].x || vel[i].y;
}
// render
for( int i = 0; i < countof(pos); ++i ) {
int col = frame[i] / 10, num_frame = row[i] * 4 + col % 4; // 4x4 tilesheet
float position[3] = {pos[i].x,pos[i].y,pos[i].y}, offset[2]={0,0}, scale[2]={0.5,0.5};
float spritesheet[3]={num_frame,4,4};
sprite_sheet(kids,
spritesheet, // num_frame in a 4x4 spritesheet
position, 0, // position(x,y,depth:sort-by-Y), angle
offset, scale, // offset(x,y), scale(x,y)
false, WHITE, false // is_additive, tint color, resolution-independent
);
}
}
void demo_hud() {
// draw pixel-art hud, 16x16 ui element, scaled and positioned in resolution-independant way
static texture_t inputs; do_once inputs = texture( "prompts_tilemap_34x24_16x16x1.png", TEXTURE_LINEAR );
float spritesheet[3] = {17,34,24}, offset[2] = {0, - 2*absf(sin(window_time()*5))}; // sprite cell and animation
float scale[2] = {3, 3}, tile_w = 16 * scale[0], tile_h = 16 * scale[1]; // scaling
float position[3] = {window_width() - tile_w, window_height() - tile_h, window_height() }; // position in screen-coordinates (x,y,z-index)
sprite_sheet(inputs, spritesheet, position, 0/*deg*/, offset, scale, false, WHITE, 1);
}
int main() {
// window (80% sized, MSAA x4 flag)
window_create(80.0, WINDOW_MSAA4);
window_title(__FILE__);
// tiled map
tiled_t tmx = tiled(vfs_read("castle.tmx"));
// tmx.parallax = true;
// spine model
//spine_t *spn = spine("goblins.json", "goblins.atlas", 0);
// camera 2d
camera_t cam = camera();
cam.position = vec3(window_width()/2, window_height()/2, 3); // at(CX,CY) zoom(x3)
camera_enable(&cam);
// audio (both clips & streams)
audio_t clip1 = audio_clip( "coin.wav" );
audio_t clip2 = audio_clip( "pew.sfxr" );
audio_t stream1 = audio_stream( "larry.mid" );
audio_t stream2 = audio_stream( "waterworld-map.fur" );
audio_t BGM = stream1;
audio_play(BGM, 0);
// font config: faces (6 max) and colors (10 max)
#define FONT_CJK FONT_FACE3
#define FONT_YELLOW FONT_COLOR2
#define FONT_LIME FONT_COLOR3
font_face(FONT_CJK, "mplus-1p-medium.ttf", 48.f, FONT_JP|FONT_2048); // CJK|FONT_2048|FONT_OVERSAMPLE_Y);
font_color(FONT_YELLOW, RGB4(255,255,0,255));
font_color(FONT_LIME, RGB4(128,255,0,255));
// fx: load all post fx files in all subdirs. enable a few filters by default
fx_load("fx**.fs");
fx_enable(fx_find("fxCRT2.fs"), 1);
fx_enable(fx_find("fxGrain.fs"), 1);
fx_enable(fx_find("fxContrast.fs"), 1);
fx_enable(fx_find("fxVignette.fs"), 1);
// demo loop
while (window_swap() && !input_down(KEY_ESC)) {
// handle input
if( input_down(KEY_F5) ) window_reload();
if( input_down(KEY_F11) ) window_fullscreen( !window_has_fullscreen() );
// camera panning (x,y) & zooming (z)
if( !ui_hover() && !ui_active() ) {
if( input(MOUSE_L) ) cam.position.x += input_diff(MOUSE_X);
if( input(MOUSE_L) ) cam.position.y += input_diff(MOUSE_Y);
cam.position.z += input_diff(MOUSE_W) * 0.1;
}
// apply post-fxs from here
fx_begin();
profile("Rendering") {
vec3 center = add3(cam.position,vec3(-window_width()/1,-window_height()/2,0));
// render tiled map
tiled_render(tmx, center);
//
demo_kids(vec3(0,0,1));
demo_hud();
// render spine model
// spine_animate(spn, !window_has_pause() * window_delta());
// spine_render(spn, vec3(cam.position.x, cam.position.y, 1), true );
// sprite_flush();
}
// subtitle sample
font_print(
FONT_BOTTOM FONT_CENTER
FONT_CJK FONT_H1
FONT_YELLOW "私はガラスを食べられます。" FONT_LIME "それは私を傷つけません。\n"
);
// post-fxs end here
fx_end();
// ui
if( ui_panel("Audio", 0)) {
if( ui_label2_toolbar("BGM: Track #1", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("BGM: Track #2", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(clip1, 0);
if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(clip2, 0);
ui_panel_end();
}
if( ui_panel("Tiled", 0)) {
ui_float("Zoom in", &cam.position.z);
tiled_ui(&tmx);
ui_panel_end();
}
/*if( ui_panel("Spine", 0)) {
spine_ui(spn);
ui_panel_end();
}*/
}
}
// this demo supersedes following old sources:
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-audio.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-font.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-spine.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-sprite.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-tiled.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-tilemap.c

View File

@ -0,0 +1,57 @@
#include "fwk.h"
struct {
float (*ease)(float);
const char *name;
} easings[] = {
{ease_linear, "ease_linear"},
{ease_out_sine, "ease_out_sine"},
{ease_out_quad, "ease_out_quad"},
{ease_out_cubic, "ease_out_cubic"},
{ease_out_quart, "ease_out_quart"},
{ease_out_quint, "ease_out_quint"},
{ease_out_expo, "ease_out_expo"},
{ease_out_circ, "ease_out_circ"},
{ease_out_back, "ease_out_back"},
{ease_out_elastic, "ease_out_elastic"},
{ease_out_bounce, "ease_out_bounce"},
{ease_in_sine, "ease_in_sine"},
{ease_in_quad, "ease_in_quad"},
{ease_in_cubic, "ease_in_cubic"},
{ease_in_quart, "ease_in_quart"},
{ease_in_quint, "ease_in_quint"},
{ease_in_expo, "ease_in_expo"},
{ease_in_circ, "ease_in_circ"},
{ease_in_back, "ease_in_back"},
{ease_in_elastic, "ease_in_elastic"},
{ease_in_bounce, "ease_in_bounce"},
{ease_inout_sine, "ease_inout_sine"},
{ease_inout_quad, "ease_inout_quad"},
{ease_inout_cubic, "ease_inout_cubic"},
{ease_inout_quart, "ease_inout_quart"},
{ease_inout_quint, "ease_inout_quint"},
{ease_inout_expo, "ease_inout_expo"},
{ease_inout_circ, "ease_inout_circ"},
{ease_inout_back, "ease_inout_back"},
{ease_inout_elastic, "ease_inout_elastic"},
{ease_inout_bounce, "ease_inout_bounce"},
{ease_inout_perlin, "ease_inout_perlin"},
};
int main() {
window_create(0.75, WINDOW_SQUARE);
while(window_swap()) {
static double timer = 0; timer = fmod(timer+window_delta(), 2); // loops every 2s
static int open = 1;
if( ui_window("ease", &open) ) {
float linear_delta = timer / 2.f; // delta is [0..1]
for( int i = 0; i < countof(easings); ++i) {
float nonlinear_delta = easings[i].ease(linear_delta);
// visualize
ui_slider( easings[i].name, &nonlinear_delta );
}
ui_window_end();
}
}
}

View File

@ -0,0 +1,111 @@
#include "fwk.h"
int main() {
window_create(75.0, WINDOW_MSAA8);
// style: our aliases
#define FONT_REGULAR FONT_FACE1
#define FONT_ITALIC FONT_FACE2
#define FONT_BOLD FONT_FACE3
#define FONT_JAPANESE FONT_FACE4
#define FONT_MONOSPACE FONT_FACE5
#define FONT_GRAY FONT_COLOR2
#define FONT_ORANGE FONT_COLOR3
#define FONT_LIME FONT_COLOR4
#define FONT_GREEN FONT_COLOR5
#define FONT_CYAN FONT_COLOR6
#define FONT_LARGEST FONT_H1
#define FONT_LARGE FONT_H2
#define FONT_MEDIUM FONT_H3
#define FONT_NORMAL FONT_H4
#define FONT_SMALL FONT_H5
#define FONT_TINY FONT_H6
// style: atlas size, unicode ranges and font faces (up to 6 faces)
font_face(FONT_REGULAR, "B612""-Regular.ttf", 48.f, FONT_EU|FONT_AR|FONT_RU|FONT_2048);
font_face(FONT_ITALIC, "B612""-Italic.ttf", 48.f, FONT_EU|FONT_AR|FONT_RU|FONT_2048);
font_face(FONT_BOLD, "B612""-Bold.ttf", 48.f, FONT_EU|FONT_AR|FONT_RU|FONT_2048);
font_face(FONT_JAPANESE, "mplus-1p-medium.ttf", 48.f, FONT_JP|FONT_2048); // CJK|FONT_2048|FONT_OVERSAMPLE_Y);
font_face(FONT_MONOSPACE, "B612Mono""-Regular.ttf", 24.f, FONT_EU|FONT_512);
// style: colors (up to 10 colors)
font_color(FONT_GRAY, RGB4(100,100,100,255));
font_color(FONT_ORANGE, RGB4(255,192,0,255));
font_color(FONT_LIME, RGB4(192,255,0,255));
font_color(FONT_GREEN, RGB4(0,255,192,255));
font_color(FONT_CYAN, RGB4(0,192,255,255));
// prepare color highlighting for following code snippet
const char *source =
FONT_MONOSPACE FONT_LARGEST
"int main(int argc, char **argv) {\n"
" for( int i = 0; i < 10; ++i)\n"
" puts(\"hello world\");\n"
" return 0;\n"
"}\n";
const void *colors = font_colorize(source, "void,int,char", "if,else,for,do,while,return,switch,case,break,default,");
// demo loop
while( window_swap() && !input(KEY_ESC) ) {
ddraw_grid(0);
// initial spacing
font_goto(0, 50);
// print a code snippet with syntax highlighting
font_highlight(source, colors);
// print a few strings with markup codes
font_print(
FONT_REGULAR
FONT_LARGEST FONT_GRAY "The quick "
FONT_LARGE FONT_LIME "brown "
FONT_MEDIUM FONT_GRAY "fox "
FONT_NORMAL "jumps over "
FONT_SMALL "the lazy "
FONT_TINY "dog.\n");
font_print(
FONT_REGULAR FONT_LARGE FONT_CYAN
"Now is the time for all " FONT_ITALIC "good men " FONT_REGULAR "to come to the aid of " FONT_BOLD "the party.\n");
font_print(
FONT_ITALIC FONT_LARGE FONT_GREEN
"Ég get etið gler án þess að meiða mig!\n");
font_print(
FONT_BOLD FONT_LARGE FONT_ORANGE
"Эх, чужак! Общий съём цен шляп (юфть)—вдрызг!.\n");
font_print(
FONT_JAPANESE
"私はガラスを食べられます。それは私を傷つけません。\n");
font_print( "This text ");
font_print( "should display concatenated, ");
font_print( "as there are no linefeeds.\n" );
// i18n: pangrams.txt file, line browser
static int counter = 0;
static array(char*) lines; do_once lines = strsplit( vfs_read("pangrams.txt"), "\r\n" );
counter += input_down(KEY_RIGHT)-input_down(KEY_LEFT);
counter += counter < 0 ? array_count(lines) : 0;
font_print( va("<< %s >>\n", lines[counter % array_count(lines)]) );
// this does not work yet. you cant chain alignments yet...
//font_print(FONT_TOP "Top" FONT_MIDDLE "Middle" FONT_BASELINE "Baseline" FONT_BOTTOM "Bottom\n");
//font_print(FONT_LEFT "Left" FONT_CENTER "Center" FONT_RIGHT "Right\n");
// ... alignment must be the first tag in a string for now. this is a temporary hack.
font_print(FONT_LEFT "left");
font_print(FONT_CENTER "center");
font_print(FONT_RIGHT "right");
font_print(FONT_TOP FONT_CENTER "top\n");
font_print(FONT_MIDDLE FONT_RIGHT "middle\n");
font_print(FONT_BASELINE FONT_RIGHT "baseline\n");
font_print(FONT_BOTTOM FONT_CENTER "bottom\n");
}
}

View File

@ -0,0 +1,218 @@
// sprite routines
// - rlyeh,
//
// credits: original lovely demo by rxi (MIT License).
// see https://github.com/rxi/autobatch/tree/master/demo/cats
#include "fwk.h"
texture_t kids, catImage, shadowImage, inputs;
int NUM_SPRITES = 100, NUM_SPRITES_CHANGED = 1;
typedef struct Cat {
int cat, flip;
double x, y;
double vx, vy;
double animSpeed;
double moveTimer;
double elapsed;
} Cat;
void demo_cats() {
static array(Cat) cats = 0;
// init
if( NUM_SPRITES_CHANGED ) {
NUM_SPRITES_CHANGED = 0;
array_resize(cats, NUM_SPRITES); int i = 0;
for each_array_ptr(cats, Cat, c) {
randset(i++);
c->x = randf() * window_width();
c->y = randf() * window_height();
c->vx = c->vy = 0;
c->cat = randi(0, 4);
c->flip = randf() < 0.5;
c->animSpeed = 0.8 + randf() * 0.3;
c->moveTimer = 0;
c->elapsed = 0;
}
}
// move
const float dt = 1/120.f;
const int appw = window_width(), apph = window_height();
enum { yscale = 1 };
for( int i = 0; i < NUM_SPRITES; ++i ) {
Cat *c = &cats[i];
// Add velocity to position //and wrap to screen
c->x += yscale * c->vx * dt; // % ;
c->y += yscale * c->vy * dt; // % (int)window_height();
if( c->x < 0 ) c->x += appw; else if( c->x > appw ) c->x -= appw;
if( c->y < 0 ) c->y += apph; else if( c->y > apph ) c->y -= apph;
// Faster animation if walking
int boost = c->vx == 0 && c->vy == 0 ? 1 : 3;
// Update elapsed time
c->elapsed += dt * boost;
// Update move timer -- if we hit zero then change or zero velocity
c->moveTimer -= dt * boost;
if (c->moveTimer < 0) {
if (randf() < .2) {
c->vx = (randf() * 2 - 1) * 30 * 2;
c->vy = (randf() * 2 - 1) * 15 * 2;
c->flip = c->vx < 0;
} else {
c->vx = c->vy = 0;
}
c->moveTimer = 1 + randf() * 5;
}
}
// render
uint32_t white = rgba(255,255,255,255);
uint32_t alpha = rgba(255,255,255,255*0.6);
for( int i = 0; i < NUM_SPRITES; ++i ) {
Cat *c = &cats[i];
// Get current animation frame (8x4 tilesheet)
double e = c->elapsed * c->animSpeed;
double frame_num = c->cat * 8 + floor( ((int)(e * 8)) % 4 );
frame_num = c->vx != 0 || c->vy != 0 ? frame_num + 4 : frame_num;
// Get x scale based on flip flag
int xscale = yscale * (c->flip ? -1 : 1);
// Draw
float angle = 0; //fmod(window_time()*360/5, 360);
float scale[2] = { 2*xscale, 2*yscale };
float position[3] = { c->x,c->y,c->y }, no_offset[2] = {0,0}, spritesheet[3] = { frame_num,8,4 };
sprite_sheet(catImage,
spritesheet, // frame_number in a 8x4 spritesheet
position, angle, // position(x,y,depth: sort by Y), angle
no_offset, scale, // offset(x,y), scale(x,y)
0,white,0 // is_additive, tint color, resolution independant
);
float position_neg_sort[3] = { c->x,c->y,-c->y }, offset[2] = {-1,5}, no_spritesheet[3] = {0,0,0};
sprite_sheet(shadowImage,
no_spritesheet, // no frame_number (0x0 spritesheet)
position_neg_sort, angle, // position(x,y,depth: sort by Y), angle
offset, scale, // offset(x,y), scale(x,y)
0,alpha,0 // is_additive, tint color, resolution independant
);
}
}
void demo_kids() {
static int angle; //++angle;
static int *x, *y, *v;
// init
if( NUM_SPRITES_CHANGED ) {
NUM_SPRITES_CHANGED = 0;
y = (int*)REALLOC(y, 0 );
x = (int*)REALLOC(x, NUM_SPRITES * sizeof(int) );
y = (int*)REALLOC(y, NUM_SPRITES * sizeof(int) );
v = (int*)REALLOC(v, NUM_SPRITES * sizeof(int) );
for( int i = 0; i < NUM_SPRITES; ++i ) {
randset(i);
x[i] = randi(0, window_width());
y[i] = randi(0, window_height());
v[i] = randi(1, 3);
}
}
// config
const int appw = window_width(), apph = window_height();
// move & render
for( int i = 0; i < NUM_SPRITES; ++i ) {
y[i] = (y[i] + v[i]) % (apph + 128);
int col = ((x[i] / 10) % 4); // 4x4 tilesheet
int row = ((y[i] / 10) % 4);
int num_frame = col * 4 + row;
float position[3] = {x[i],y[i],y[i]}, offset[2]={0,0}, scale[2]={1,1}, spritesheet[3]={num_frame,4,4};
sprite_sheet(kids,
spritesheet, // num_frame in a 4x4 spritesheet
position, angle, // position(x,y,depth: sort by Y), angle
offset, scale, // offset(x,y), scale(x,y)
0, ~0u, 0 // is_additive, tint color, resolution independant
);
}
}
int main(int argc, char **argv) {
window_create(75.f, 0);
window_title("FWK - Sprite");
window_color( SILVER );
// options
int do_cats = 1;
NUM_SPRITES = optioni("--num_sprites,-N", NUM_SPRITES);
if(do_cats) NUM_SPRITES/=2; // cat-sprite+cat-shadow == 2 sprites
// load sprites and sheets
kids = texture( "spriteSheetExample.png", TEXTURE_LINEAR );
catImage = texture( "cat.png", TEXTURE_LINEAR ); //
shadowImage = texture( "cat-shadow.png", TEXTURE_LINEAR );
inputs = texture( "prompts_tilemap_34x24_16x16x1.png", TEXTURE_LINEAR );
// load all fx files, including subdirs
fx_load("fx**.fs");
// init camera (x,y) (z = zoom)
camera_t cam = camera();
cam.position = vec3(window_width()/2,window_height()/2,1);
camera_enable(&cam);
while(window_swap()) {
if( input(KEY_F5)) window_reload();
if( input(KEY_F11)) window_fullscreen( window_has_fullscreen() ^ 1);
if( input(KEY_ESC) ) break;
// camera panning (x,y) & zooming (z)
if( !ui_hover() && !ui_active() ) {
if( input(MOUSE_L) ) cam.position.x -= input_diff(MOUSE_X);
if( input(MOUSE_L) ) cam.position.y -= input_diff(MOUSE_Y);
cam.position.z += input_diff(MOUSE_W) * 0.1; // cam.p.z += 0.001f; for tests
}
// apply post-fxs from here
fx_begin();
profile("Sprite batching") {
if(do_cats) demo_cats(); else demo_kids();
}
// flush retained renderer, so we ensure the fbos are up to date before fx_end()
profile("Sprite flushing") {
sprite_flush();
}
// post-fxs end here
fx_end();
// draw pixel-art hud, 16x16 ui element, scaled and positioned in resolution-independant way
{
vec3 old_pos = camera_get_active()->position;
sprite_flush();
camera_get_active()->position = vec3(window_width()/2,window_height()/2,1);
float zindex = window_height(); // large number, on top
float spritesheet[3] = {17,34,24}, offset[2] = {0, - 2*absf(sin(window_time()*5))}; // sprite cell and animation
float scale[2] = {3, 3}, tile_w = 16 * scale[0], tile_h = 16 * scale[1]; // scaling
float position[3] = {window_width() - tile_w, window_height() - tile_h, zindex }; // position in screen-coordinates
sprite_sheet(inputs, spritesheet, position, 0/*rotation*/, offset, scale, false/*is_additive*/, WHITE/*color*/, false/*resolution_independant*/);
sprite_flush();
camera_get_active()->position = old_pos;
}
if( ui_panel("Sprite", 0) ) {
const char *labels[] = {"Kids","Cats"};
if( ui_list("Sprite type", labels, countof(labels), &do_cats) ) NUM_SPRITES_CHANGED = 1;
if( ui_int("Number of Sprites", &NUM_SPRITES) ) NUM_SPRITES_CHANGED = 1;
if( ui_clampf("Zoom", &cam.position.z, 0.1, 10));
ui_panel_end();
}
}
}

View File

@ -0,0 +1,104 @@
// ddraw demo: fps camera, renderdd, collide, math, ui, fx, boids
// - rlyeh, public domain.
//
// Compile with:
// `make demos\02-ddraw.c` (windows)
// `sh MAKE.bat demos/02-ddraw.c` (linux, osx)
#include "fwk.h"
int main() {
bool do_boids_demo = 1;
bool do_colliders_demo = 1;
bool do_debugdraw_demo = 1;
// 75% size, MSAAx2
window_create(75.0, WINDOW_MSAA2);
window_title(__FILE__);
// camera that points to origin
camera_t cam = camera();
// load skybox folder (no flags)
skybox_t sky = skybox("cubemaps/stardust", 0);
// load all postfx files in all subdirs
fx_load("fx**.fs");
// main loop
while( window_swap() ) {
// input handler
if (input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen()^1 );
if (input_down(KEY_ESC) ) break;
// fps camera
profile("FPS camera") {
if( input(GAMEPAD_CONNECTED) ) {
vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 );
vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 );
vec2 mouse = scale2(vec2(filtered_rpad.x, filtered_rpad.y), 1.0f);
vec3 wasdec = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( true );
} else {
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
}
}
// draw skybox
skybox_render(&sky, cam.proj, cam.view);
// world
ddraw_grid(0);
// boids
static swarm_t sw;
if( do_boids_demo ) profile("boids") {
do_once sw = swarm();
do_once array_push(sw.steering_targets, vec3(0,0,0));
do_once for(int i = 0; i < 100; ++i)
array_push(sw.boids, boid(scale3(rnd3(),10), rnd3())); // pos,vel
// move
sw.steering_targets[0] = cam.position;
swarm_update(&sw, window_delta());
// draw
for (int j = 0, end = array_count(sw.boids); j < end; ++j) {
vec3 dir = norm3(sub3(sw.boids[j].position, sw.boids[j].prev_position));
ddraw_boid(sw.boids[j].position, dir);
}
}
// showcase many debugdraw shapes
if( do_debugdraw_demo ) {
ddraw_demo();
}
// showcase many colliding tests
if( do_colliders_demo ) {
collide_demo();
}
// ui
if( ui_panel("App", 0) ) {
ui_bool("Boids demo", &do_boids_demo);
ui_bool("Collide demo", &do_colliders_demo);
ui_bool("DebugDraw demo", &do_debugdraw_demo);
ui_panel_end();
}
if( ui_panel("Swarm", 0) ) {
ui_swarm(&sw);
ui_panel_end();
}
}
}
// this demo supersedes following old sources:
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-collide.c

View File

@ -0,0 +1,49 @@
#include "fwk.h"
int main() {
window_create(0.75, 0);
camera_t cam = camera();
camera_t cam2 = camera();
int spin = 1;
while( window_swap() ) {
// controls
if(input_down(KEY_SPACE)) spin^=1;
// setup scene
cam.fov = 30;
cam.position = vec3(180,180,180);
camera_enable(&cam);
// spin 2nd camera
double t = window_time(), c = cos(t), s = sin(t);
if(spin)
camera_teleport(&cam2, vec3(c * 100, 100, s * 100));
camera_lookat(&cam2, vec3(0,0,0));
// render world (ground and cubes only in frustum of cam2)
mat44 projview; multiply44x2(projview, cam2.proj, cam2.view);
frustum f = frustum_build(projview);
ddraw_ground(0);
int drawn = 0, total = 0;
for(int z = -300; z < 300; z += 5) {
for(int x = -300; x < 300; x += 5) {
vec3 min = vec3(x, 0, z);
vec3 max = add3(min, vec3(2.5,2.5,2.5));
if( frustum_test_aabb(f, aabb(min, max)) ) {
ddraw_aabb( min, max );
++drawn;
}
++total;
}
}
// debug
ddraw_camera(&cam2);
font_print(va(FONT_RIGHT "%d/%d cubes drawn", drawn, total));
}
}

View File

@ -0,0 +1,179 @@
// anims demo: input, mesh, anim, render, fx, ui, instancing
// - rlyeh, public domain.
//
// Compile with:
// `make demos\03-anims.c` (windows)
// `sh MAKE.bat demos/03-anims.c` (linux, osx)
//
// @todo: instanced poses, instanced render_bones
// @todo: ik, modify bone transform (bone space)
#include "fwk.h"
array(mat44) M; // instanced transforms
int main() {
bool do_showaabb = 0;
bool do_showbones = 0;
bool do_showmodel = 1;
bool do_showgizmo = 1;
// 75% sized, MSAAx2
window_create(75, WINDOW_MSAA2);
window_title(__FILE__);
camera_t cam = camera();
skybox_t sky = skybox("cubemaps/stardust", 0);
model_t mdl = model("George.fbx", MODEL_RIMLIGHT); // kgirls01.fbx
anims_t a = animations("George.fbx", 0); // kgirl/animlist.txt
// 32*32 max instances
int NUM_INSTANCES = 1;
array_resize(M, 32*32);
for(int z = 0, i = 0; z < 32; ++z) {
for(int x = 0; x < 32; ++x, ++i) {
vec3 pos = vec3(-x*3,0,-z*3);
vec3 rot = vec3(0,-90,0); // kgirl: 0,0,0
vec3 sca = vec3(1,1,1); // kgirl: 2,2,2
compose44(M[i], pos, eulerq(rot), sca);
}
}
// shader_bind(mdl.program);
// shader_vec3("u_rimcolor", vec3(0.12,0.23,0.34));
// shader_vec3("u_rimrange", vec3(0.06,0.74,0.5));
// load all postfx files in all subdirs
fx_load("fx**.fs");
while( window_swap() && !input(KEY_ESC) ) {
// fps camera
if( input(GAMEPAD_CONNECTED) ) {
vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 );
vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), 0.15f/*do_gamepad_deadzone*/ + 1e-3 );
vec2 mouse = scale2(vec2(filtered_rpad.x, filtered_rpad.y), 1.0f);
vec3 wasdec = scale3(vec3(filtered_lpad.x, input(GAMEPAD_LT) - input(GAMEPAD_RT), filtered_lpad.y), 1.0f);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( true );
} else {
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
}
// skeletal update
static bool is_dragging_slider = 0;
vec2i anim = vec2i( a.anims[ a.inuse ].from, a.anims[ a.inuse ].to );
profile("Skeletal update") {
float delta = window_delta() * 30 * a.speed * !is_dragging_slider; // 30fps anim timer
if(!window_has_pause()) mdl.curframe = model_animate_clip(mdl, mdl.curframe + delta, anim.min, anim.max, a.anims[a.inuse].flags & ANIM_LOOP );
}
// render
fx_begin();
// Skybox
profile("Skybox") {
skybox_render(&sky, cam.proj, cam.view);
}
// ground
ddraw_ground(0);
ddraw_flush();
// characters
profile("Skeletal render") {
if( do_showmodel ) model_render_instanced(mdl, cam.proj, cam.view, M /*mdl.pivot*/, 0, NUM_INSTANCES);
if( do_showbones ) model_render_skeleton(mdl, M[0] /*mdl.pivot*/);
if( do_showaabb ) {
aabb box = model_aabb(mdl, M[0] /*mdl.pivot*/);
ddraw_aabb(box.min, box.max);
}
if( do_showgizmo ) {
static vec3 p = {0,0,0}, r = {0,-90,0}, s = {1,1,1};
gizmo(&p, &r, &s);
compose44(mdl.pivot, p, eulerq(r), s);
}
}
fx_end();
if ( ui_panel("Rim lighting", 0) ) {
static vec3 rimcolor = {0.2,0.2,0.2};
static vec3 rimrange = {0.11,0.98,0.5};
ui_color3f("Color", &rimcolor.x);
ui_clampf("Low", &rimrange.x, 0, 1);
ui_clampf("High", &rimrange.y, 0, 1);
ui_clampf("Mix", &rimrange.z, 0, 1);
// ui_vec
shader_bind(mdl.program);
shader_vec3("u_rimcolor", rimcolor);
shader_vec3("u_rimrange", rimrange);
ui_panel_end();
}
if( ui_panel("Animation", PANEL_OPEN) ) {
if( ui_bool("Show aabb", &do_showaabb) );
if( ui_bool("Show bones", &do_showbones) );
if( ui_bool("Show models", &do_showmodel) );
if( ui_bool("Show gizmo", &do_showgizmo) );
ui_separator();
if( ui_int("Instances", &NUM_INSTANCES)) NUM_INSTANCES = clampi(NUM_INSTANCES, 1, array_count(M));
ui_separator();
ui_label(va("Anim %s [%d.. %.2f ..%d]", a.anims[ a.inuse ].name, anim.min, mdl.curframe, anim.max ));
// normalize curframe into [0..1] range
is_dragging_slider = 0;
float range = (mdl.curframe - anim.min) / ((anim.max - anim.min) + !(anim.max - anim.min));
if( ui_slider2("Frame", &range, va("%.2f/%d %02d%%", mdl.curframe - anim.min, anim.max - anim.min, (int)(range * 100.f))) ) {
mdl.curframe = range * (anim.max - anim.min) + anim.min;
is_dragging_slider = 1;
}
ui_slider2("Speed", &a.speed, va("x%.2f", a.speed));
ui_separator();
for( int i = 0; i < array_count(a.anims); ++i ) {
bool selected = a.inuse == i;
float progress = selected ? (mdl.curframe - anim.min) * 100.f / (anim.max - anim.min) : 0.f;
const char *caption = va("%s%s%s %.2f%%", selected ? "*":"", a.anims[i].name, a.anims[i].flags & ANIM_LOOP ? " (Loop)":"", progress);
int choice = ui_label2_toolbar(caption, va("%s %s %s", ICON_MD_REPLAY_CIRCLE_FILLED, a.inuse == i && a.speed <= 0 ? ICON_MD_NOT_STARTED : ICON_MD_PAUSE_CIRCLE, ICON_MD_PLAY_CIRCLE) );
if( choice == 1 ) { // play/pause
if( mdl.curframe >= anim.max ) mdl.curframe = anim.min; // rewind animation
a.speed = 1.0f;
a.inuse = i;
}
if( choice == 2 ) { // pause/advance-frame
if(a.speed <= 0) mdl.curframe = (int)mdl.curframe + 1;
a.speed = 0.0f;
a.inuse = i;
}
if( choice == 3 ) { // loop on/off
if( a.anims[i].flags & ANIM_LOOP )
a.anims[ i ].flags &= ~ANIM_LOOP;
else
a.anims[ i ].flags |= ANIM_LOOP;
a.inuse = i;
mdl.curframe = anim.min; // restart anim
}
}
ui_panel_end();
}
}
}
// @todo: controller demo: root motion on/off
// @todo: controller demo: anim controller.lua
// this demo supersedes following old sources:
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-anims.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-instanced.c

View File

@ -0,0 +1,150 @@
// actor controller demo: anims, anim blending, input, math
// - rlyeh, public domain.
//
// Compile with:
// `make demos\04-actor.c` (windows)
// `sh MAKE.bat demos/04-actor.c` (linux, osx)
#include "fwk.h"
int main() {
// create window (75% sized, MSAAx4)
window_create(75, WINDOW_MSAA4);
window_title(__FILE__);
// set up our players
struct player_t {
const char *name;
model_t mdl;
anim_t idle, run; // anim clips
float keys[4], scale; // up,down,left,right
vec2 inertia; // [forward,yaw]
vec3 pivot, speed; // [pitch,yaw,roll] [turn speed,forward speed,anim speed fps]
vec3 pos, dir, pad; // [position] [facing dir] [gamepad accumulator]
bool notified;
float brain[4]; // AI
} player[3] = {
{ "PLAYER-1", model("kgirls01.fbx", 0), loop(0,60,0.25,0), loop(66,85,0.25,0), // idle anim [0..60], run anim [66..85]
{KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT}, 2, {0.90,0.80}, {-100}, {3, 0.30, 30}, {0}, {1} },
{ "PLAYER-2", model("george.fbx", 0), loop(0,100,0.25,0), loop(372,396,0.25,0), // idle anim [0..100], run anim [372..396]
{KEY_I,KEY_K,KEY_J,KEY_L}, 1, {0.95,0.90}, {-90,-90}, {1.75, 0.25, 24}, {-5}, {1} },
{ "PLAYER-3", model("alien.fbx", 0), loop(110,208,0.25,0), loop(360,380,0.25,0), // idle anim [110..208], run anim [360..380]
{KEY_W,KEY_S,KEY_A,KEY_D}, 0.85, {0.85,0.75}, {-90,-90}, {3.5, 0.35, 60}, {5}, {1} }
};
// camera that points to origin, skybox, and a background tune
camera_t cam = camera();
skybox_t sky = skybox("cubemaps/stardust", 0);
audio_play( audio_stream("waterworld-map.fur"), 0 );
// game loop
while( window_swap() ) {
// world: skybox, position markers and ground grid
skybox_render(&sky, cam.proj, cam.view);
for( int i = 0; i < countof(player); ++i )
ddraw_position_dir(player[i].pos, player[i].dir, 1.0f);
ddraw_grid(0);
ddraw_flush();
// move and render players
for( int i = 0; i < countof(player); ++i ) {
struct player_t *p = &player[i];
// capture inputs
p->brain[0] = input(p->keys[0]);
p->brain[1] = input(p->keys[1]);
p->brain[2] = input(p->keys[2]);
p->brain[3] = input(p->keys[3]);
// setup waypoints for PLAYER-1
static array(vec3) points;
if( input_down(MOUSE_L) && !ui_hover() ) {
vec3 pt = editor_pick(input(MOUSE_X), input(MOUSE_Y));
hit *h = ray_hit_plane(ray(cam.position, pt), plane(vec3(0,0,0),vec3(0,1,0)));
if(h) array_push(points, h->p);
}
// ddraw waypoints
ddraw_color(YELLOW);
for( int i = 1; i < array_count(points); ++i) ddraw_line(points[i-1],points[i]);
for( int i = 0; i < array_count(points); ++i) ddraw_circle(points[i], vec3(0,1,0), 1); // prism(points[i], 1, 0, vec3(0,1,0), 4);
ddraw_color(RED);
for( int i = 0; i < array_count(points); ++i) ddraw_point(points[i]);
ddraw_color(WHITE);
// move thru waypoints (PLAYER-1 only)
if( i == 0 && array_count(points) ) {
struct player_t *p = &player[i];
vec3 dst = points[0];
vec3 vector1 = norm3(vec3(p->dir.x,0,p->dir.z));
vec3 vector2 = norm3(sub3(dst,p->pos));
float angle = atan2(vector2.z, vector2.x) - atan2(vector1.z, vector1.x);
angle *= 180 / C_PI;
// range [0, 360)
// if (angle < 0) { angle += 2 * 180; }
// range (-180, 180]
if (angle > 180) { angle -= 2 * 180; }
else if (angle <= -180) { angle += 2 * 180; }
float dist = len3(sub3(p->pos, dst));
if(dist < 1) {
// goal
array_pop_front(points);
}
else {
if( dist < 10 && fabsf(angle) > 10 ) {
// spin only
p->brain[ angle < 0 ? 2 : 3 ] = 1;
}
else {
// spin
p->brain[ angle < 0 ? 2 : 3 ] = 1;
// move forward
p->brain[ 0 ] = 1;
}
}
}
// accumulate movement
float yaw = p->brain[2] - p->brain[3];
float fwd = p->brain[0] - p->brain[1]; if(fwd<0) fwd = 0;
p->pad.x = p->pad.x * p->inertia.y + yaw * (1-p->inertia.y);
p->pad.y = p->pad.y * p->inertia.x + fwd * (1-p->inertia.x);
// rotate yaw dir, then apply into position
p->dir = rotatey3(p->dir, p->speed.x * p->pad.x);
p->pos = add3(p->pos, scale3(p->dir, p->speed.y * p->pad.y));
// animate clips and blend
anim_t *primary = fwd ? &p->run : &p->idle;
anim_t *secondary = fwd ? &p->idle : &p->run;
model_animate_blends(p->mdl, primary, secondary, window_delta() * p->speed.z);
// render model. transforms on top of initial pivot and scale
mat44 M; compose44(M, p->pos, eulerq(add3(p->pivot,vec3(atan2(p->dir.z,p->dir.x)*180/C_PI,0,0))),vec3(p->scale,p->scale,p->scale));
model_render(p->mdl, cam.proj, cam.view, M, 0);
// ui
if( yaw||fwd ) if( !p->notified ) p->notified = 1, ui_notify(0, va(ICON_MD_GAMEPAD " %s joined the game.", p->name));
ddraw_text(p->pos, 0.01, va("%s: %6.3f", fwd?"run":"idle", (fwd ? p->run : p->idle).timer ));
}
// look at the players that are moving; center of their triangle otherwise.
float A = len3(player[0].pad); if(A<0.01) A=0;
float B = len3(player[1].pad); if(B<0.01) B=0;
float C = len3(player[2].pad); if(C<0.01) C=0;
float weight = A + B + C;
if( weight ) A /= weight, B /= weight, C /= weight; else A = B = C = 0.33333;
vec3 target = add3(add3(scale3(player[0].pos,A), scale3(player[1].pos,B)), scale3(player[2].pos,C));
// smooth target before sending to camera
static vec3 smooth; camera_lookat(&cam, smooth = mix3(target,smooth,!weight ? 0.98 : 0.95));
// ui
if( ui_panel("Controls", 0) ) {
ui_label2("Girl", ICON_MD_MOUSE " Set Waypoint");
ui_label2("Girl", ICON_MD_GAMEPAD " CURSOR keys");
ui_label2("Alien", ICON_MD_GAMEPAD " W,A,S,D keys");
ui_label2("Robot", ICON_MD_GAMEPAD " I,J,K,L keys");
ui_panel_end();
}
}
}

234
_fwk/demos/04-lod.c 100644

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,222 @@
// scene demo
// - rlyeh, public domain
#include "fwk.h"
int main() {
// options
bool do_twosided = 1;
bool do_wireframe = 0;
bool do_billboard_x = 0, do_billboard_y = 0, do_billboard_z = 0;
// window (80% sized, MSAA x4 flag)
window_create(80, WINDOW_MSAA4);
window_title(__FILE__);
// load all postfx files in all subdirs
fx_load("fx**.fs");
// scene loading
#define SCENE(...) #__VA_ARGS__
const char *my_scene = SCENE([
{
skybox: 'cubemaps/stardust/',
},
{
position:[-5.0,-2.0,2.0],
rotation: [90.0,0.0,180.0],
scale:0.20,
//anchor/pivot:[],
// vertex:'p3 t2',
mesh:'models/witch/witch.obj',
texture:'models/witch/witch_diffuse.tga.png',
// swapzy:true,
flipuv:false,
},
{
position:[-5.0,-2.0,2.0],
rotation: [90.0,0.0,180.0],
scale:2.20,
//anchor/pivot:[],
// vertex:'p3 t2',
mesh:'models/witch/witch_object.obj',
texture:'models/witch/witch_object_diffuse.tga.png',
// swapzy:true,
flipuv:false,
},
]);
int num_spawned = scene_merge(my_scene);
object_t *obj1 = scene_index(0);
object_t *obj2 = scene_index(1);
// manual spawn & loading
model_t m1 = model("kgirl/kgirls01.fbx", 0); //MODEL_NO_ANIMS);
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB);
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, t1);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10,0,-10));
object_pivot(obj3, vec3(-90+180,180,0));
// camera
camera_t cam = camera();
cam.speed = 0.2f;
// demo loop
while (window_swap())
{
// input
if( input_down(KEY_ESC) ) break;
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
window_cursor( !active );
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
// queue model scale bounces
float t = fmod(window_time(), 0.3) / 0.3;
float s = 1.0;//0.01f * ease_ping_pong(t, ease_in_cubic,ease_out_cubic);
object_scale(obj1, vec3(0.20f - s,0.20f + s,0.20f - s));
object_scale(obj2, vec3(0.20f - s,0.20f + s,0.20f - s));
// queue model billboard
object_billboard(obj1, (do_billboard_x << 2)|(do_billboard_y << 1)|(do_billboard_z << 0));
object_billboard(obj2, (do_billboard_x << 2)|(do_billboard_y << 1)|(do_billboard_z << 0));
// queue model rotation
//object_rotate(obj3, vec3(0,1*window_time() * 20,0));
// flush render scene (background objects: skybox)
profile("Scene background") {
scene_render(SCENE_BACKGROUND);
}
// queue debug drawcalls
profile("Debugdraw") {
ddraw_ground(0);
ddraw_color(YELLOW);
ddraw_text(vec3(+1,+1,-1), 0.04f, va("(%f,%f,%f)", cam.position.x,cam.position.y,cam.position.z));
ddraw_color(YELLOW);
ddraw_flush();
}
// apply post-fxs from here
fx_begin();
// render scene (foreground objects) with post-effects
profile("Scene foreground") {
int scene_flags = 0;
scene_flags |= do_wireframe ? SCENE_WIREFRAME : 0;
scene_flags |= do_twosided ? 0 : SCENE_CULLFACE;
scene_render(SCENE_FOREGROUND | scene_flags);
}
profile("Skeletal update") if(!window_has_pause()) {
float delta = window_delta() * 30 ; // 30fps anim
m1.curframe = model_animate(m1, m1.curframe + delta);
ddraw_text(vec3(-10,5,-10), 0.05, va("Frame: %.1f", m1.curframe));
}
// post-fxs end here
fx_end();
// queue ui
if( ui_panel("Camera", 0)) {
if( ui_float("Speed", &cam.speed) ) {}
if( ui_float3("Position", &cam.position.x) ) {}
ui_panel_end();
}
if( ui_panel("Scene", 0)) {
if(ui_toggle("Billboard X", &do_billboard_x)) {}
if(ui_toggle("Billboard Y", &do_billboard_y)) {}
if(ui_toggle("Billboard Z", &do_billboard_z)) {}
if(ui_separator()) {}
if(ui_bool("Wireframe", &do_wireframe)) {}
if(ui_bool("Two sided", &do_twosided)) {}
ui_panel_end();
}
}
}
#if 0
// ----------------------------------------------------------------------------
// material demo
// - rlyeh, public domain
//
// @todo: object_print(obj, "");
// create camera
camera_t cam = camera();
// load video, RGB texture, no audio
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_NO_AUDIO | VIDEO_LOOP ); video_seek(v, 30);
// load texture
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB);
texture_t t2 = texture("matcaps/material3", 0);
// load model
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS);
// spawn object1 (diffuse)
object_t* obj1 = scene_spawn();
object_model(obj1, m1);
object_diffuse(obj1, t1);
object_scale(obj1, vec3(3,3,3));
object_move(obj1, vec3(-10+5*0,0,-10));
object_pivot(obj1, vec3(0,90,0));
// spawn object2 (matcap)
object_t* obj2 = scene_spawn();
object_model(obj2, m2);
object_diffuse(obj2, t2);
object_scale(obj2, vec3(3,3,3));
object_move(obj2, vec3(-10+5*2,0,-10));
object_pivot(obj2, vec3(0,90,0));
// spawn object2 (video)
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, video_textures(v)[0]);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10+5*1,0,-10));
object_pivot(obj3, vec3(0,90,0));
// @todo: add shadertoy material
static model_t cube; do_once cube = model("cube.obj", 0);
static shadertoy_t s; do_once s = shadertoy("shadertoys/4ttGWM.fs", 256);
model_set_texture(cube, shadertoy_render(&s, window_delta())->tx);
model_render(cube, cam.proj, cam.view, cube.pivot, 0);
while(window_swap() && !input(KEY_ESC)) {
// draw environment
viewport_color( RGB3(22,22,32) );
ddraw_grid(0);
ddraw_flush();
// update video
video_decode( v );
// draw scene
scene_render(SCENE_FOREGROUND);
}
// load static scene
// model_t sponza = model("sponza.obj", MODEL_MATCAPS);
// model_set_texture(sponza, texture("matcaps/normals", 0));
// translation44(sponza.pivot, 0,-1,0);
// rotate44(sponza.pivot, -90,1,0,0);
// scale44(sponza.pivot, 10,10,10);
// model_render(sponza, cam.proj, cam.view, sponza.pivot, 0);
// this demo supersedes following old sources:
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-material.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-shadertoy.c
#endif

View File

@ -0,0 +1,280 @@
// full controller demo: anims, input, collide; @todo: gamepad, input opts, easing on hits, notify on gamepad connect
// - rlyeh, public domain.
//
// Compile with:
// `make demos\99-controller.c` (windows)
// `sh MAKE.bat demos/99-controller.c` (linux, osx)
#include "fwk.h"
int main() {
// 75% window, MSAAx2 flag
window_create(75, WINDOW_MSAA2);
// fx: load all post fx files in all subdirs
fx_load("fx**.fs");
// create a camera
camera_t cam = camera();
camera_enable(&cam);
// config 3d model #1
model_t witch = model("witch/witch.obj", 0);
model_set_texture(witch, texture("witch/witch_diffuse.tga.png", 0));
mat44 witch_pivot; vec3 witch_p = {-5,0,-5}, witch_r={-180,180,0}, witch_s={0.1,-0.1,0.1};
// config 3d model #2
model_t girl = model("kgirl/kgirls01.fbx", 0);
mat44 girl_pivot; vec3 girl_p = {0,0,0}, girl_r = {270,0,0}, girl_s = {2,2,2};
// skybox
skybox_t sky = skybox("cubemaps/stardust", 0);
// BGM
audio_play( audio_stream("waterworld-map.fur"), 0 );
// editor loop
while( window_swap() ) {
// game camera
profile("Game.Camera") {
camera_t *cam = camera_get_active();
static vec3 source;
do_once source = cam->position;
vec3 target = add3(girl_p, vec3(0,10,0));
target = add3(target, scale3(norm3(sub3(source, target)), 10.0));
source = mix3(source, target, 1-0.99f);
camera_teleport(cam, source);
camera_lookat(cam, vec3(girl_p.x,0,girl_p.z));
}
// render begin (postfx)
fx_begin();
// skybox
skybox_render(&sky, cam.proj, cam.view);
// world
ddraw_grid(0);
ddraw_flush();
// models
compose44(girl.pivot, girl_p, eulerq(girl_r), girl_s);
model_render(girl, cam.proj, cam.view, girl.pivot, 0);
compose44(witch.pivot, witch_p, eulerq(witch_r), witch_s);
model_render(witch, cam.proj, cam.view, witch.pivot, 0);
// render end (postfx)
fx_end();
// input controllers
double GAME_JUMP_DOWN = input_down(KEY_Z);
double GAME_FIRE_DOWN = input_down(KEY_X);
double GAME_JUMP = input(KEY_Z);
double GAME_FIRE = input(KEY_X);
double GAME_LEFT = input(KEY_J);
double GAME_RIGHT = input(KEY_L);
double GAME_UP = input(KEY_I);
double GAME_DOWN = input(KEY_K);
double GAME_AXISX = input(KEY_L) - input(KEY_J);
double GAME_AXISY = input(KEY_I) - input(KEY_K);
if( !input_anykey() ) {
if( input(GAMEPAD_CONNECTED) ) {
vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), 0.15f /*15% deadzone*/);
GAME_JUMP_DOWN = input_down(GAMEPAD_A);
GAME_FIRE_DOWN = input_down(GAMEPAD_B) || input_down(GAMEPAD_X) || input_down(GAMEPAD_Y);
GAME_JUMP = input(GAMEPAD_A);
GAME_FIRE = input(GAMEPAD_B) || input(GAMEPAD_X) || input(GAMEPAD_Y);
GAME_AXISX = filtered_lpad.x;
GAME_AXISY = filtered_lpad.y;
}
}
// animation controllers
profile("Game.Animate scene") if( !window_has_pause() ) {
float delta = window_delta() * 30; // 30fps anim
// animate girl
girl.curframe = model_animate(girl, girl.curframe + delta);
// jump controller: jump duration=1.5s, jump height=6 units, anims (expo->circ)
float jump_delta = 1.0;
static double jump_timer = 0, jump_ss = 1.5, jump_h = 6;
if( GAME_JUMP_DOWN ) if( jump_timer == 0 ) jump_timer = time_ss();
jump_delta = clampf(time_ss() - jump_timer, 0, jump_ss) * (1.0/jump_ss);
if( jump_delta >= 1 ) { jump_timer = 0; }
float y = ease_ping_pong( jump_delta, ease_out_expo, ease_out_circ);
girl_p.y = y * jump_h;
// punch controller
float punch_delta = 1;
if( jump_delta >= 1 ) {
static vec3 origin;
static double punch_timer = 0, punch_ss = 0.5;
if( GAME_FIRE_DOWN ) if( punch_timer == 0 ) punch_timer = time_ss(), origin = girl_p;
punch_delta = clampf(time_ss() - punch_timer, 0, punch_ss) * (1.0/punch_ss);
if( punch_delta >= 1 ) { punch_timer = 0; }
else {
float x = ease_out_expo( punch_delta );
vec3 fwd = rotate3q(vec3(0,0,1), eulerq(vec3(girl_r.x - 170,girl_r.y,girl_r.z)));
vec3 mix = mix3(girl_p, add3(origin,scale3(fwd,x*2)), x);
girl_p.x = mix.x, girl_p.z = mix.z;
}
}
int modern_controller = 1;
int running = 0;
// girl controller
// locomotion vars
float speed = 0.2f * delta;
float yaw_boost = GAME_AXISY > 0 ? 1.0 : 1.75;
if(punch_delta < 1) yaw_boost = 0.0; // if firing...
else if(punch_delta <= 0.1) yaw_boost = 4.0; // unless initial punch chaining, extra yaw
// old fashioned locomotion controller (boat controller)
if(!modern_controller) {
running = GAME_AXISY > 0;
girl_r.x -= 170;
quat q = eulerq(girl_r); // += custom.pivot
vec3 rgt = rotate3q(vec3(1,0,0), q);
vec3 up = rotate3q(vec3(0,1,0), q);
vec3 fwd = rotate3q(vec3(0,0,1), q);
vec3 dir = scale3(fwd, speed * GAME_AXISY * (GAME_AXISY > 0 ? 2.0 : 0.5));
girl_r.x += speed * 20.0 * yaw_boost * GAME_AXISX; // yaw
girl_p = add3(girl_p, dir);
girl_r.x += 170;
}
// modern locomotion controller (mario 3d)
if(modern_controller) {
running = GAME_AXISX != 0 || GAME_AXISY != 0;
camera_t *cam = camera_get_active();
vec3 fwd = sub3(girl_p, cam->position); fwd.y = 0; fwd = norm3(fwd);
vec3 rgt = norm3(cross3(fwd, vec3(0,1,0)));
// target
vec3 dir = add3(
scale3(fwd, GAME_AXISY),
scale3(rgt, GAME_AXISX)
); dir.y = 0; dir = norm3(dir);
// smoothing
static vec3 olddir; do_once olddir = dir;
dir = mix3(dir, olddir, 1 - (yaw_boost / 4.0) * 0.85);
olddir = dir;
// vis
// ddraw_arrow(girl_p, add3(girl_p,scale3(dir,10)));
// apply direction
girl_p = add3(girl_p, scale3(dir, speed * 2));
// apply rotation
{
girl_r.x -= 170;
quat q = eulerq(girl_r);
vec3 fwdg = rotate3q(vec3(0,0,1), q);
girl_r.x += 170;
//float cosAngle = dot3(dir,fwdg);
//float angle = acos(cosAngle) * TO_DEG;
float angle = TO_DEG * ( atan2(fwdg.z, fwdg.x) - atan2(dir.z, dir.x));
if( !isnan(angle) ) {
girl_r.x -= angle;
while(girl_r.x> 180) girl_r.x-=360;
while(girl_r.x<-180) girl_r.x+=360;
}
}
}
// anim loops
if( jump_delta < 1 ) { // jump/kick anim
#if 0
girl.curframe = clampf(girl.curframe, 184, 202);
if( girl.curframe > 202-4 && GAME_FIRE_DOWN ) girl.curframe = 184+4;
#else
#define loopf(frame, min, max) (frame < min ? min : frame > max ? min + frame - max : frame)
if(girl.curframe >= 203)
girl.curframe = loopf(girl.curframe, 203, 220);
else
girl.curframe = clampf(girl.curframe, 184, 202);
if( girl.curframe > 202-4 && girl.curframe < 208 && GAME_FIRE_DOWN ) girl.curframe = 203;
#endif
}
else if( punch_delta < 1 ) { // punch anim
girl.curframe = clampf(girl.curframe, 90, 101);
if( girl.curframe > 101-6 && GAME_FIRE_DOWN ) girl.curframe = 101-6;
}
else if( running ) {
// loop running anim
if( girl.curframe < 65 ) girl.curframe = 65;
if( girl.curframe > 85 ) girl.curframe = 65;
}
else { // loop idle anim
if( girl.curframe > 59 ) girl.curframe = 0;
}
}
// Game collisions
profile("Game.collisions") {
bool punching = girl.curframe >= 90 && girl.curframe < 101;
bool air_kicking = girl.curframe >= 184 && girl.curframe < 202;
bool jump_kicking = girl.curframe >= 203 && girl.curframe < 220;
bool attacking = punching || air_kicking || jump_kicking;
if( attacking ) {
aabb boxg = model_aabb(girl, girl_pivot);
aabb boxw = model_aabb(witch, witch_pivot);
#if 0 // halve aabb. ok
{
vec3 diag = sub3(boxg.max, boxg.min);
vec3 halve = scale3(diag, 0.25);
vec3 center = scale3(add3(boxg.min, boxg.max), 0.5);
boxg.min = sub3(center, halve);
boxg.max = add3(center, halve);
}
#endif
hit* h = aabb_hit_aabb(boxg, boxw);
if( h && GAME_FIRE ) {
vec3 dir = norm3(sub3(witch_p, girl_p));
witch_p = add3(witch_p, mul3(dir,vec3(1,0,1)));
}
}
}
// ui
if( ui_panel("Input", 0) ) { // @todo: showcase input binding
ui_section("Controllers");
ui_label("Gamepad #1");
ui_label("Keys I/J/K/L + Z/X");
ui_panel_end();
}
}
}
// vec2 do_gamepad_polarity = vec2(+1,+1);
// vec2 do_gamepad_sensitivity = vec2(0.1f,0.1f);
// vec2 do_mouse_polarity = vec2(+1,-1);
// vec2 do_mouse_sensitivity = vec2(0.2f,0.2f);
// float do_gamepad_deadzone = 0.15f;//
//
// if(ui_separator()) {}
// if(ui_slider("Gamepad deadzone", &do_gamepad_deadzone)) {}
// if(ui_float2("Gamepad polarity", do_gamepad_polarity.v2)) {}
// if(ui_float2("Gamepad sensitivity", do_gamepad_sensitivity.v2)) {}
// if(ui_separator()) {}
// if(ui_float2("Mouse polarity", do_mouse_polarity.v2)) {}
// if(ui_float2("Mouse sensitivity", do_mouse_sensitivity.v2)) {}//

View File

@ -0,0 +1,74 @@
// material demo
// - rlyeh, public domain
//
// @todo: object_print(obj, "");
#include "fwk.h"
int main() {
// create the window
window_create( 0.75f, WINDOW_MSAA8 );
window_color( GRAY );
// create camera
camera_t cam = camera();
// load video, RGB texture, no audio
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_NO_AUDIO | VIDEO_LOOP ); video_seek(v, 30);
// load texture
texture_t t1 = texture("kgirl/g01_texture.png", TEXTURE_RGB);
texture_t t2 = texture("matcaps/material3", 0);
// load model
model_t m1 = model("suzanne.obj", MODEL_NO_ANIMATIONS);
model_t m2 = model("suzanne.obj", MODEL_NO_ANIMATIONS|MODEL_MATCAPS);
// spawn object1 (diffuse)
object_t* obj1 = scene_spawn();
object_model(obj1, m1);
object_diffuse(obj1, t1);
object_scale(obj1, vec3(3,3,3));
object_move(obj1, vec3(-10+5*0,0,-10));
object_pivot(obj1, vec3(0,90,0));
// spawn object2 (matcap)
object_t* obj2 = scene_spawn();
object_model(obj2, m2);
object_diffuse(obj2, t2);
object_scale(obj2, vec3(3,3,3));
object_move(obj2, vec3(-10+5*2,0,-10));
object_pivot(obj2, vec3(0,90,0));
// spawn object2 (video)
object_t* obj3 = scene_spawn();
object_model(obj3, m1);
object_diffuse(obj3, video_textures(v)[0]);
object_scale(obj3, vec3(3,3,3));
object_move(obj3, vec3(-10+5*1,0,-10));
object_pivot(obj3, vec3(0,90,0));
// create point light
light_t* l = scene_spawn_light();
light_type(l, LIGHT_POINT);
while(window_swap() && !input(KEY_ESC)) {
// draw environment
ddraw_grid(0);
// update video
video_decode( v );
// update light position
light_teleport(l, cam.position);
// draw scene
scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND|SCENE_UPDATE_SH_COEF);
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouselook = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouselook.x,mouselook.y);
window_cursor( !active );
}
}

View File

@ -0,0 +1,129 @@
#include "fwk.h"
enum { MAX_NPCS = 5, MAX_CLIENTS = 5 };
struct player_t {
uint64_t seen_until;
float x,y,z,angle;
uint32_t color;
};
struct npc_t {
float x,y,z;
uint32_t color;
};
struct world_t {
struct player_t player[MAX_CLIENTS];
struct npc_t npc[MAX_NPCS];
} world = {0};
void show_notification(char *msg) {
printf("notif %s\n", msg);
ui_notify("server", msg);
}
void bind_netbuffers(int64_t self_id) {
uint32_t colors[] = { ORANGE,GREEN,RED,CYAN,PURPLE,YELLOW,GRAY,PINK,AQUA };
for (int64_t i=0; i<MAX_NPCS; ++i) {
// as an example, let only server to set initial pos
if (self_id==0) {
world.npc[i].x = i*3.f + 4.f;
}
world.npc[i].color = colors[i%(sizeof colors / sizeof colors[0])];
network_buffer(&world.npc[i], sizeof(struct npc_t), NETWORK_RECV, 0);
}
for (int64_t i=0; i<MAX_CLIENTS; ++i) {
world.player[i].color = colors[i%(sizeof colors / sizeof colors[0])];
network_buffer(&world.player[i], sizeof(struct player_t), (i!=self_id ? NETWORK_RECV : NETWORK_SEND) | NETWORK_UNRELIABLE, i /* each client owns exactly 1 buffer */);
};
// register server->client rpc
if (self_id > 0) {
network_rpc("void show_notification(char*)", show_notification);
}
}
int main() {
// ifdef(win32, FreeConsole()); // tty_detach()
// network setup
network_create(MAX_CLIENTS, "127.0.0.1", 0, flag("--client") ? NETWORK_CONNECT : 0);
int64_t self_id = network_get(NETWORK_RANK);
bind_netbuffers(self_id);
// game setup
camera_t cam = camera();
window_create( 0.35f, WINDOW_MSAA8|WINDOW_SQUARE );
struct player_t *self = &world.player[self_id];
network_put(NETWORK_SEND_MS, 33); // 0.033 s
// game loop
while( window_swap() && !input(KEY_ESC) ) {
// network sync
char **event = network_sync(0); // timeout_ms:0
while(*event) {
int code;
char *msg;
int ev = network_event(*event++, &code, &msg);
printf( "network event id: %d err: %d msg: %s\n", ev, code, msg );
}
self_id = network_get(NETWORK_RANK);
if (network_get(NETWORK_LIVE) == 0) {
network_create(MAX_CLIENTS, "127.0.0.1", 0, flag("--client") ? NETWORK_CONNECT|NETWORK_NOFAIL : 0);
self_id = network_get(NETWORK_RANK);
if (self_id != -1) {
bind_netbuffers(self_id);
}
continue;
}
/* quick hack to broadcast notif from host */
if (self_id == 0 && input_down(KEY_F3)) {
printf("rpc %s\n", "show_notification \"hi, sailor!\"");
network_rpc_send(rand()%4, "show_notification \"hi, sailor!\"");
}
// camera tracking
cam.position = vec3(self->x,100,self->z);
camera_lookat(&cam, vec3(self->x,0,self->z));
// input - move player
float iy = input(KEY_UP) - input(KEY_DOWN);
float ix = input(KEY_RIGHT) - input(KEY_LEFT);
if( iy || ix ) {
self->x += iy*window_delta()*15;
self->z += ix*window_delta()*15;
}
self->seen_until = date_epoch() + 4;
// npc - update npc movement on server-side
if (self_id == 0) {
for (int i = 0; i < MAX_NPCS; ++i) {
struct npc_t *n = &world.npc[i];
n->z = sinf(window_time())*4.f;
}
}
// background - draw grid
ddraw_ground(0);
// foreground - draw all players
for( int id = 0; id < MAX_CLIENTS; ++id ) {
struct player_t *p = &world.player[id];
if (p->seen_until < date_epoch()) continue; /* skip inactive players */
ddraw_color( p->color );
ddraw_capsule(vec3(p->x,0,p->z), vec3(p->x,2,p->z), 1);
ddraw_text(vec3(p->x,4,p->z), 0.01, va("player #%d", id));
}
for( int id = 0; id < MAX_NPCS; ++id ) {
struct npc_t *p = &world.npc[id];
ddraw_color( p->color );
ddraw_capsule(vec3(p->x,0,p->z), vec3(p->x,2,p->z), 1);
ddraw_text(vec3(p->x,4,p->z), 0.01, va("npc #%d", id));
}
// stats
window_title(va("player #%lld", self_id));
}
}

View File

@ -0,0 +1,77 @@
// network demo: sockets, downloads, script, @todo: ecs, vm, obj, teal
// - rlyeh, public domain.
//
// Compile with:
// `make demos\07-network.c` (windows)
// `sh MAKE.bat demos/07-network.c` (linux, osx)
#include "fwk.h"
volatile int client_socket = -1;
volatile int server_socket = -1;
int server_echo(int cli) {
ui_notify("Server: client connected", va("Address %s:%s", tcp_host(cli), tcp_port(cli)));
char buf[128];
int len = tcp_recv(cli, buf, 128);
if( len > 0 ) tcp_send(cli, buf, len); // echo
tcp_close(cli);
return 1;
}
int server_thread(void *userdata) {
server_socket = tcp_bind("0.0.0.0", "8080", 10); // port "0" for auto
for(;;) {
tcp_peek(server_socket, server_echo);
sleep_ms(100);
}
}
void client_message(const char *msg) {
client_socket = tcp_open("127.0.0.1", "8080");
if( client_socket >= 0 ) {
tcp_send(client_socket, msg, strlen(msg));
char buf[128];
int rlen = tcp_recv(client_socket, buf, 128);
if(rlen > 0) ui_notify("Client", va("Received '%.*s' from server", rlen, buf));
// tcp_close(client_socket);
}
}
int main() {
window_create(0.85, 0);
window_title(__FILE__);
while(window_swap()) {
static int ui_open; ui_open = 1;
if( ui_window("Network", &ui_open) ) {
if( ui_button("Server: listen") ) { do_once thread(server_thread, NULL); }
ui_label(server_socket >= 0 ? "Listening at 8080 port" : "Not ready");
ui_separator();
if( ui_button("Client: send message") ) client_message("Hi");
ui_label(client_socket >= 0 ? va("Connected to server. My address is %s:%s", tcp_host(client_socket), tcp_port(client_socket)) : "Not connected");
ui_separator();
if( ui_button("Download test (https)")) {
array(char) webfile = download("https://www.google.com/"); // @leak
ui_notify("Download test", va("%d bytes downloaded from google.com", array_count(webfile)));
}
ui_separator();
if( ui_button("Script test")) {
script_run( "ui_notify(\"Script test\", \"Hello from \" .. _VERSION)");
script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" );
}
ui_window_end();
}
}
}
// this demo supersedes following old sources:
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-demo.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-script.c
// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-socket.c

View File

@ -0,0 +1,35 @@
// audio demo
// - rlyeh, public domain
#include "fwk.h"
int main() {
// window (80% sized, MSAA x4 flag)
window_create(80, WINDOW_MSAA4 | WINDOW_SQUARE);
// audio (both streams & clips)
audio_t stream1 = audio_stream( "larry.mid" );
audio_t stream2 = audio_stream( "waterworld-map.fur" );
audio_t BGM = stream1;
audio_play(BGM, 0);
audio_t SFX1 = audio_clip( "coin.wav" );
audio_t SFX2 = audio_clip( "pew.sfxr" );
audio_play(SFX1, 0);
// demo loop
while (window_swap() && !input_down(KEY_ESC)) {
static int open = 1;
if( ui_window("Audio", &open)) {
static float bgm = 1, sfx = 1, master = 1;
if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm);
if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx);
if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master);
if( ui_label2_toolbar("BGM: Leisure Suit Larry", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("BGM: Waterworld Map", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(SFX1, 0);
if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(SFX2, 0);
ui_window_end();
}
}
}

View File

@ -0,0 +1,44 @@
// video player
// - rlyeh, public domain
#include "fwk.h"
int main() {
// 75% window, msaa x2
window_create( 75, WINDOW_MSAA2 );
// load video
int is_rgb = flag("--rgb") ? 1 : 0;
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_LOOP | (is_rgb ? VIDEO_RGB : VIDEO_YCBCR) );
while( window_swap() ) {
// decode video frame and get associated textures (audio is automatically sent to audiomixer)
texture_t *textures;
profile( "Video decoder" ) {
textures = video_decode( v );
}
// present decoded textures as a fullscreen composed quad
profile( "Video quad" ) {
if(is_rgb) fullscreen_quad_rgb( textures[0], 1.3f );
else fullscreen_quad_ycbcr( textures, 1.3f );
}
// input controls
if( input(KEY_ESC) ) break;
// ui video
if( ui_panel("Video", 0) ) {
if( ui_button("Rewind") ) video_seek(v, video_position(v) - 3);
if( ui_button("Pause") ) video_pause(v, video_is_paused(v) ^ 1);
if( ui_button("Forward") ) video_seek(v, video_position(v) + 3);
ui_panel_end();
}
// audio
if( ui_panel("Audio", 0)) {
static float master = 1;
if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master);
ui_panel_end();
}
}
}

View File

@ -0,0 +1,91 @@
#include "fwk.h"
int SKY_DIR = 0;
const char *SKY_DIRS[] = {
"cubemaps/bridge3/",
"cubemaps/colors/",
"cubemaps/colors2/",
"hdr/Tokyo_BigSight_1k.hdr",
};
int OBJ_MDL = 0;
const char *OBJ_MDLS[] = {
"meshes/sphere.obj",
"meshes/suzanne.obj",
"meshes/gazebo.obj",
};
int main(int argc, char** argv) {
window_create(85, WINDOW_MSAA8);
camera_t cam = camera();
skybox_t sky = {0};
model_t mdl = {0};
bool initialized = 0;
bool must_reload = 0;
while( window_swap()) {
if (input_down(KEY_ESC)) break;
// reloading
if( must_reload ) {
must_reload = 0;
skybox_destroy(&sky);
model_destroy(mdl);
initialized = 0;
}
if( !initialized ) {
initialized = 1;
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
mdl = model(OBJ_MDLS[OBJ_MDL], 0);
rotation44(mdl.pivot, 0, 1,0,0); // @fixme: -90,1,0,0 -> should we rotate SHMs as well? compensate rotation in shader?
}
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
window_cursor( !active );
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouse.x,mouse.y);
// render
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
{
if (flag("--mie")) {
// skybox_sh_reset(&sky);
skybox_mie_calc_sh(&sky, 4.0f);
// float x = cosf((float)window_time())*4;
// skybox_sh_add_light(&sky, vec3(0.3,0.3,0.3), vec3(0,1,0), 16*absf(cosf((float)window_time()*2))+2);
// skybox_sh_add_light(&sky, vec3(0.6,0,0), vec3(x,1,0), 2);
}
skybox_render(&sky, cam.proj, cam.view);
shader_bind(mdl.program);
shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh);
shader_int("u_textured", false);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
}
if( ui_panel("Scene", 0)) {
if( ui_list("Skybox", SKY_DIRS, countof(SKY_DIRS), &SKY_DIR) ) {
must_reload = 1;
}
if( ui_list("Model", OBJ_MDLS, countof(OBJ_MDLS), &OBJ_MDL) ) {
must_reload = 1;
}
ui_separator();
for (int i = 0; i < 9; i++) {
vec3 remap = scale3(add3(sky.cubemap.sh[i], vec3(1,1,1)), 127.5f); // -1..+1 -> 0..255
ui_color3(va("SH Coefficient [%d]", i), &remap.x);
sky.cubemap.sh[i] = sub3(scale3(remap, 1/127.5f), vec3(1,1,1));
}
ui_panel_end();
}
}
}

View File

@ -0,0 +1,38 @@
// shadertoy viewer
// - rlyeh, public domain
#include "fwk.h"
int main() {
window_create(75, 0); // WINDOW_MSAA8);
array(char*) list = 0;
for each_array( file_list("demos/art/shadertoys/**.fs"), char*, dir ) {
array_push(list, STRDUP(file_name(dir)));
}
shadertoy_t sh = shadertoy(*list, 0); // 0:no flags
while(window_swap()) {
// selector
static int selected = 0;
int prev = input_down(KEY_UP) || input_down(KEY_LEFT);
int next = input_down(KEY_DOWN) || input_down(KEY_RIGHT);
if( prev ) if( selected > 0 ) sh = shadertoy( list[--selected], 0 );
if( next ) if( selected < array_count(list) - 1 ) sh = shadertoy( list[++selected], 0 );
// draw
shadertoy_render(&sh, window_delta());
// UI
if( ui_panel("Shadertoy", 1)) {
for( int i = 0; i < array_count(list); ++i ) {
bool in_use = i == selected;
if( ui_bool(list[i], &in_use) ) {
sh = shadertoy( list[selected = i], 0 );
}
}
ui_panel_end();
}
}
}

43
_fwk/demos/99-bt.c 100644
View File

@ -0,0 +1,43 @@
#include "fwk.h"
int yes() {
return puts("yes"),1;
}
int no() {
return puts("no"),0;
}
int hi() {
return puts("hi"),1;
}
int run_bt(void *ud) {
bt_t *b = (bt_t*)ud;
int rc=bt_run(b);
printf("rc:%d\n", rc);
ui_notify("bt done", va("status: %d", rc));
return 0;
}
int main() {
window_create(50, WINDOW_SQUARE); // WINDOW_MSAA4);
window_title(__FILE__);
bt_addfun("yes", yes);
bt_addfun("no", no);
bt_addfun("hi", hi);
bt_t b = bt("bt1.ini", 0);
// game loop
while( window_swap() && !input(KEY_ESC) ) {
if( ui_panel("AI", 0) ) {
int choice = ui_buttons(2, "BT Run", "BT Reload");
if(choice == 1) thread(run_bt, &b);
if(choice == 2) b = bt("bt1.ini", 0);
ui_separator();
ui_bt(&b);
ui_panel_end();
}
font_print(va(FONT_TOP FONT_RIGHT "bt.node: %d bytes", (int)sizeof(bt_t)));
}
}

View File

@ -0,0 +1,47 @@
// hello ui: config, window, system, ui, video
// - rlyeh, public domain.
//
// Compile with:
// `make demos\01-ui.c` (windows)
// `sh MAKE.bat demos/01-ui.c` (linux, osx)
#include "fwk.h"
int main() {
window_create(80, 0);
window_title(__FILE__);
camera_t cam = camera();
vec3 route[] = {
vec3(0.0f, 0.0f, 5.0f),
vec3(0.0f, 0.0f, 10.0f),
vec3(0.0f, 2.0f, 20.0f),
vec3(0.0f, 8.0f, 30.0f),
vec3(0.0f, 12.0f, 40.0f),
vec3(0.0f, 0.0f, 80.0f),
};
#define route_len (sizeof route / sizeof route[0])
// app loop
while( window_swap() ) {
// input controls
if( input(KEY_ESC) ) break;
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
ddraw_ground(0);
for (int i = 0; i < route_len; i++) {
}
}
}

View File

@ -0,0 +1,36 @@
#include "fwk.h"
int main() {
// 75% sized, MSAAx2
window_create(50, WINDOW_SQUARE);
window_title(__FILE__);
unsigned TEX_WIDTH = 1000;
unsigned comp = compute(vfs_read("shaders/compute-test.glsl"));
texture_t tex = texture_create(TEX_WIDTH, TEX_WIDTH, 4, 0, TEXTURE_LINEAR|TEXTURE_FLOAT);
shader_bind(comp);
shader_image(tex, 0, 0, 0, BUFFER_READ);
struct {
float f;
} data;
unsigned buf = ssbo_create(&data, sizeof(data), STREAM_DRAW);
while ( window_swap() && !input_down(KEY_ESC) ){
if (input(KEY_F5)) window_reload();
shader_bind(comp);
data.f = (float)window_time();
ssbo_bind(buf, 1);
ssbo_update(0, sizeof(data), &data);
compute_dispatch(TEX_WIDTH/10, TEX_WIDTH/10, 1);
write_barrier_image();
fullscreen_quad_rgb(tex, 2.2);
}
return 0;
}

View File

@ -0,0 +1,261 @@
// framework demo
// - rlyeh, public domain
#include "fwk.h"
int main() {
// options
bool do_about = 0;
float do_scale = 0.10f;
bool do_debugdraw = 0;
float do_gamepad_deadzone = 0.15f;
vec2 do_gamepad_polarity = vec2(+1,+1);
vec2 do_gamepad_sensitivity = vec2(0.1f,0.1f);
vec2 do_mouse_polarity = vec2(+1,-1);
vec2 do_mouse_sensitivity = vec2(0.2f,0.2f);
bool do_billboard_x = 0, do_billboard_y = 0, do_billboard_z = 0;
// window (80% sized, MSAA x4 flag)
window_create(80, WINDOW_MSAA4);
window_title(__FILE__);
// load all fx files, including subdirs
fx_load("fx**.fs");
// load skybox
skybox_t sky = skybox(flag("--mie") ? 0 : "cubemaps/stardust", 0); // --mie for rayleigh/mie scattering
// load static scene
model_t sponza;
int do_sponza = flag("--sponza");
if( do_sponza ) {
sponza = model("sponza.obj", 0); // MODEL_NO_TEXTURES);
translation44(sponza.pivot, 0,-1,0);
rotate44(sponza.pivot, -90,1,0,0);
scale44(sponza.pivot, 10,10,10);
}
model_t shaderball;
int do_shaderball = flag("--shaderball");
if( do_shaderball ) {
shaderball = model("shaderball.glb", 0);
translation44(shaderball.pivot, 0,0,-10);
rotate44(shaderball.pivot, -90,1,0,0);
scale44(shaderball.pivot, 0.02,0.02,0.02);
}
// animated models loading
int model_flags = flag("--matcaps") ? MODEL_MATCAPS : 0;
model_t girl = model("kgirl/kgirls01.fbx", model_flags);
model_t alien = model("alien/alien_helmet.fbx", model_flags); rotation44(alien.pivot, -90,1,0,0);
model_t george = model("robots/george.fbx", model_flags);
model_t leela = model("robots/leela.fbx", model_flags);
model_t mike = model("robots/mike.fbx", model_flags);
model_t stan = model("robots/stan.fbx", model_flags);
model_t robots[4] = { george, leela, mike, stan };
for( int i = 0; i < countof(robots); ++i ) {
rotation44(robots[i].pivot, -90,1,0,0);
}
if( flag("--matcaps") ) {
// patch models to use matcaps
model_set_texture(george, texture("matcaps/3B6E10_E3F2C3_88AC2E_99CE51-256px", 0)); // green
model_set_texture(leela, texture("matcaps/39433A_65866E_86BF8B_BFF8D8-256px", 0));
model_set_texture(mike, texture("matcaps/394641_B1A67E_75BEBE_7D7256-256px.png", 0));
model_set_texture(stan, texture("matcaps/test_steel", 0));
model_set_texture(girl, texture("matcaps/material3", 0));
model_set_texture(alien, texture("matcaps/material3", 0));
if( flag("--shaderball") )
model_set_texture(shaderball, texture("matcaps/normals", 0));
}
// camera
camera_t cam = camera();
// audio (both clips & streams)
audio_t SFX1 = audio_clip( "coin.wav" );
audio_t SFX2 = audio_clip( "pew.sfxr" );
audio_t BGM1 = audio_stream( "waterworld-map.fur"); // wrath_of_the_djinn.xm" );
audio_t BGM2 = audio_stream( "larry.mid" );
audio_t BGM3 = audio_stream( "monkey1.mid" ), BGM = BGM1;
audio_play(SFX1, 0);
audio_play(BGM, 0);
// demo loop
while (window_swap())
{
// input
if( input_down(KEY_ESC) ) break;
if( input_down(KEY_F5) ) window_reload();
if( input_down(KEY_W) && input_held(KEY_LCTRL) ) break;
if( input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen() ^ 1 );
if( input_down(KEY_X) ) window_screenshot(__FILE__ ".png");
if( input_down(KEY_Z) ) window_record(__FILE__ ".mp4");
// vec2 filtered_lpad = input_filter_deadzone(input2(GAMEPAD_LPAD), do_gamepad_deadzone + 1e-3);
// vec2 filtered_rpad = input_filter_deadzone(input2(GAMEPAD_RPAD), do_gamepad_deadzone + 1e-3);
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
// apply post-fxs from here
fx_begin();
// queue debug drawcalls
profile("Debugdraw") {
ddraw_grid(0);
ddraw_color(YELLOW);
ddraw_text(vec3(+1,+1,-1), 0.04f, va("(%f,%f,%f)", cam.position.x,cam.position.y,cam.position.z));
if(do_debugdraw) ddraw_demo(); // showcase many debugdraw shapes
ddraw_color(YELLOW);
ddraw_flush();
}
// draw skybox
profile("Skybox") {
skybox_render(&sky, cam.proj, cam.view);
}
profile("Skeletal update") if(!window_has_pause()) {
float delta = window_delta() * 30; // 30fps anim
// animate girl & alien
girl.curframe = model_animate(girl, girl.curframe + delta);
alien.curframe = model_animate(alien, alien.curframe + delta);
// animate robots
for(int i = 0; i < countof(robots); ++i) {
robots[i].curframe = model_animate(robots[i], robots[i].curframe + delta);
}
}
profile("Skeletal render") {
static vec3 p = {-10,0,-10}, r = {0,0,0}, s = {2,2,2};
gizmo(&p, &r, &s);
mat44 M; rotationq44(M, eulerq(r)); scale44(M, s.x,s.y,s.z); relocate44(M, p.x,p.y,p.z);
model_render(girl, cam.proj, cam.view, M, 0);
aabb box = model_aabb(girl, M);
ddraw_color(YELLOW);
ddraw_aabb(box.min, box.max);
}
profile("Skeletal render") {
static vec3 p = {+10,0,-10}, r = {0,-90,0}, s = {1,1,1};
//gizmo(&p, &r, &s);
mat44 M; rotationq44(M, eulerq(r)); scale44(M, s.x,s.y,s.z); relocate44(M, p.x,p.y,p.z);
model_render(alien, cam.proj, cam.view, M, 0);
aabb box = model_aabb(alien, M); // @fixme: neg Y
ddraw_color(YELLOW);
//ddraw_aabb(box.min, box.max);
}
profile("Skeletal render") for(int i = 0; i < countof(robots); ++i) {
float scale = 0.50;
mat44 M; copy44(M, robots[i].pivot); translate44(M, i*3,0,0); scale44(M, scale,scale,scale);
model_render(robots[i], cam.proj, cam.view, M, 0);
}
if(do_sponza) profile("Sponza") {
float scale = 1.00;
mat44 M; copy44(M, sponza.pivot); translate44(M, 0,0,0); scale44(M, scale,scale,scale);
model_render(sponza, cam.proj, cam.view, M, 0);
}
if(do_shaderball) profile("Shaderball") {
float scale = 1.00;
mat44 M; copy44(M, shaderball.pivot); translate44(M, 0,0,0); scale44(M, scale,scale,scale);
model_render(shaderball, cam.proj, cam.view, M, 0);
}
// post-fxs end here
fx_end();
// font demo
do_once font_scales(FONT_FACE1, 48, 24, 18, 12, 9, 6);
font_print(va(FONT_RIGHT FONT_BOTTOM FONT_H4 "%5.2f FPS", window_fps()));
// queue ui
if( ui_panel("App", 0)) {
if(ui_bool("Show debugdraw demo", &do_debugdraw)) {}
if(ui_separator()) {}
if(ui_slider("Gamepad deadzone", &do_gamepad_deadzone)) {}
if(ui_float2("Gamepad polarity", do_gamepad_polarity.v2)) {}
if(ui_float2("Gamepad sensitivity", do_gamepad_sensitivity.v2)) {}
if(ui_separator()) {}
if(ui_float2("Mouse polarity", do_mouse_polarity.v2)) {}
if(ui_float2("Mouse sensitivity", do_mouse_sensitivity.v2)) {}
if(ui_separator()) {}
if(ui_button("About...")) { do_about = 1; audio_play(SFX1, 0); }
if(ui_dialog("About", __FILE__ "\n" __DATE__ "\n" "Public Domain.", 0, &do_about)) {}
ui_panel_end();
}
if( ui_panel("Audio", 0)) {
static float bgm = 1, sfx = 1, master = 1;
if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm);
if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx);
if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master);
if( ui_label2_toolbar("BGM: Waterworld Map" /*Wrath of the Djinn"*/, ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = BGM1, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("BGM: Leisure Suit Larry", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = BGM2, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("BGM: Monkey Island", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = BGM3, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(SFX1, 0);
if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(SFX2, 0);
ui_panel_end();
}
ui_demo(1); // show all UI widgets in a tab
}
// data tests (json5)
const char json5[] =
" /* json5 */ // comment\n"
" abc: 42.67, def: true, integer:0x100 \n"
" huge: 2.2239333e5, \n"
" hello: 'world /*comment in string*/ //again', \n"
" children : { a: 1, b: 2, c: 3 },\n"
" array: [+1,2,-3,4,5], \n"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],";
if( json_push(json5) ) {
assert( json_float("/abc") == 42.67 );
assert( json_int("/def") == 1 );
assert( json_int("/integer") == 0x100 );
assert( json_float("/huge") > 2.22e5 );
assert( strlen(json_string("/hello")) == 35 );
assert( json_int("/children/a") == 1 );
assert( json_int("/children.b") == 2 );
assert( json_int("/children[c]") == 3 );
assert( json_int("/array[%d]", 2) == -3 );
assert( json_count("/invalids") == 8 );
assert( isnan(json_float("/invalids[0]")) );
assert( !json_find("/non_existing") );
assert( PRINTF("json5 tests OK\n") );
json_pop();
}
// data tests (xml)
const char *xml = vfs_read("test1.xml");
if( xml_push(xml) ) {
puts( xml );
puts( xml_string("/person/firstName/$") );
puts( xml_string("/person/lastName/$") );
puts( xml_string("/person/address/@type") );
xml_pop();
}
// network test (https)
array(char) webfile = download("https://www.google.com/");
printf("Network test: %d bytes downloaded from google.com\n", array_count(webfile));
// script test (lua)
script_run( "-- Bye.lua\nio.write(\"script test: Bye world!, from \", _VERSION, \"\\n\")" );
}

View File

@ -0,0 +1,29 @@
#include "fwk.h"
int main() {
// 75% sized, MSAAx2
window_create(25, WINDOW_SQUARE);
window_title(__FILE__);
unsigned program = shader_geom(vfs_read("shaders/geom-test/line.glsl"), vfs_read("shaders/geom-test/line-vs.glsl"), vfs_read("shaders/geom-test/line-fs.glsl"), "aPos", "FragColor", NULL );
float points[] = {
-0.5f, 0.5f, // top-left
0.5f, 0.5f, // top-right
0.5f, -0.5f, // bottom-right
-0.5f, -0.5f // bottom-left
};
mesh_t m = mesh();
mesh_update(&m, "p2", 0, 4, points, 0, 0, 0);
while ( window_swap() && !input_down(KEY_ESC) ){
if (input(KEY_F5)) window_reload();
shader_bind(program);
shader_float("t", (float)window_time());
mesh_render_prim(&m, GL_POINTS);
}
return 0;
}

View File

@ -0,0 +1,871 @@
#define FWK_IMPLEMENTATION
#include "joint/fwk.h"
/*
A basic node-based UI built with Nuklear.
Builds on the node editor example included in Nuklear v1.00, with the aim of
being used as a prototype for implementing a functioning node editor.
Features:
- Nodes of different types. Currently their implementations are #included in
the main file, but they could easily be turned into eg. a plugin system.
- Pins/pins of different types -- currently float values and colors.
- Adding and removing nodes.
- Linking nodes, with validation (one link per input, only link similar pins).
- Detaching and moving links.
- Evaluation of output values of connected nodes.
- Memory management based on fixed size arrays for links and node pointers
- Multiple node types
- Multiple pin types
- Linking between pins of the same type
- Detaching and reattaching links
- Getting value from linked node if pin is connected
Todo:
- Complete pin types.
- Allow dragging from output to input pin.
- Cut link by CTRL+clicking input pin.
- Cut link by drawing intersect line on a link.
- Group elemnts together with mouse, or LSHIFT+clicking.
- Drag groups.
- DEL elements.
- DEL groups.
- CTRL-C/CTRL-V/CTRL-X elements.
- CTRL-C/CTRL-V/CTRL-X groups.
- CTRL-Z,CTRL-Y.
- CTRL-N.
- CTRL-L,CTRL-S.
- CTRL-F.
- CTRL-Wheel Zooming.
- Allow to extend node types from Lua.
Extra todo:
- Execution Flow (see: nk_stroke_triangle, nk_fill_triangle)
- Complete missing nodes (see: nk_draw_image, nk_draw_text)
- Right-click could visualize node/board diagram as Lua script.
- Once that done, copy/pasting scripts should work within editor.
Sources:
- https://github.com/Immediate-Mode-UI/Nuklear/pull/561
- https://github.com/vurtun/nuklear/blob/master/demo/node_editor.c
*/
typedef enum pin_type_t {
type_flow,
type_int,type_float,
type_block,type_texture,type_image,
type_color,
/*
type_bool,
type_char, type_string,
type_int2, type_int3, type_int4,
type_float2, type_float3, type_float4,
type_array, type_map,
*/
type_total
} pin_type_t;
struct node_pin {
pin_type_t pin_type;
nk_bool is_connected;
struct node* connected_node;
int connected_pin;
};
struct node {
int ID;
char name[32];
struct nk_rect bounds;
int input_count;
int output_count;
struct node_pin *inputs;
struct node_pin *outputs;
struct {
float in_padding_x;
float in_padding_y;
float in_spacing_y;
float out_padding_x;
float out_padding_y;
float out_spacing_y;
} pin_spacing; /* Maybe this should be called "node_layout" and include the bounds? */
struct node *next; /* Z ordering only */
struct node *prev; /* Z ordering only */
void* (*eval_func)(struct node*, int oIndex);
void (*display_func)(struct nk_context*, struct node*);
};
struct node_link {
struct node* input_node;
int input_pin;
struct node* output_node;
int output_pin;
nk_bool is_active;
};
struct node_linking {
int active;
struct node *node;
int input_id;
int input_pin;
};
struct node_editor {
int initialized;
struct node *node_buf[32];
struct node_link links[64];
struct node *output_node;
struct node *begin;
struct node *end;
int node_count;
int link_count;
struct nk_rect bounds;
struct node *selected;
int show_grid;
struct nk_vec2 scrolling;
struct node_linking linking;
};
/* === PROTOTYPES === */
/* The node implementations need these two functions. */
/* These could/should go in a header file along with the node and node_pin structs and be #included in the node implementations */
struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, int in_count, int out_count);
void* node_editor_eval_connected(struct node *node, int input_pin_number);
/* ================== */
/* === NODE TYPE IMPLEMENTATIONS === */
#define NODE_DEFAULT_ROW_HEIGHT 25
// ----------------------------------------------------------------------------------------------------
// #include "node_output.h"
struct node_type_output {
struct node node;
struct nk_colorf input_val;
};
struct nk_colorf *node_output_get(struct node* node) {
struct node_type_output *output_node = (struct node_type_output*)node;
if (!node->inputs[0].is_connected) {
struct nk_colorf black = {0.0f, 0.0f, 0.0f, 0.0f};
output_node->input_val = black;
}
return &output_node->input_val;
}
static void node_output_display(struct nk_context *ctx, struct node *node) {
if (node->inputs[0].is_connected) {
struct node_type_output *output_node = (struct node_type_output*)node;
output_node->input_val = *(struct nk_colorf*)node_editor_eval_connected(node, 0);
nk_layout_row_dynamic(ctx, 60, 1);
nk_button_color(ctx, nk_rgba_cf(output_node->input_val));
}
}
struct node* node_output_create(struct node_editor *editor, struct nk_vec2 position) {
struct node_type_output *output_node = (struct node_type_output*)node_editor_add(editor, sizeof(struct node_type_output), "Output", nk_rect(position.x, position.y, 100, 100), 1, 0);
if (output_node){
output_node->node.inputs[0].pin_type = type_color;
output_node->node.display_func = node_output_display;
}
return (struct node*)output_node;
}
// ----------------------------------------------------------------------------------------------------
// #include "node_float.h"
struct node_type_float {
struct node node;
float output_val;
};
static float *node_float_eval(struct node* node, int oIndex) {
struct node_type_float *float_node = (struct node_type_float*)node;
NK_ASSERT(oIndex == 0);
return &float_node->output_val;
}
static void node_float_draw(struct nk_context *ctx, struct node *node) {
struct node_type_float *float_node = (struct node_type_float*)node;
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
float_node->output_val = nk_propertyf(ctx, "#Value:", 0.0f, float_node->output_val, 1.0f, 0.01f, 0.01f);
}
void node_float_create(struct node_editor *editor, struct nk_vec2 position) {
struct node_type_float *float_node = (struct node_type_float*)node_editor_add(editor, sizeof(struct node_type_float), "Float", nk_rect(position.x, position.y, 180, 75), 0, 1);
if (float_node)
{
float_node->output_val = 1.0f;
float_node->node.display_func = node_float_draw;
float_node->node.eval_func = (void*(*)(struct node*, int)) node_float_eval;
}
}
// ----------------------------------------------------------------------------------------------------
// #include "node_color.h"
struct node_type_color {
struct node node;
float input_val[4];
struct nk_colorf output_val;
};
static struct nk_colorf *node_color_eval(struct node* node, int oIndex)
{
struct node_type_color *color_node = (struct node_type_color*)node;
NK_ASSERT(oIndex == 0); /* only one output connector */
return &color_node->output_val;
}
static void node_color_draw(struct nk_context *ctx, struct node *node)
{
struct node_type_color *color_node = (struct node_type_color*)node;
float eval_result; /* Get the values from connected nodes into this so the inputs revert on disconnect */
const char* labels[4] = {"#R:","#G:","#B:","#A:"};
float color_val[4]; /* Because we can't just loop through the struct... */
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
nk_button_color(ctx, nk_rgba_cf(color_node->output_val));
for (int i = 0; i < 4; i++)
{
if (color_node->node.inputs[i].is_connected) {
eval_result = *(float*)node_editor_eval_connected(node, i);
eval_result = nk_propertyf(ctx, labels[i], eval_result, eval_result, eval_result, 0.01f, 0.01f);
color_val[i] = eval_result;
}
else {
color_node->input_val[i] = nk_propertyf(ctx, labels[i], 0.0f, color_node->input_val[i], 1.0f, 0.01f, 0.01f);
color_val[i] = color_node->input_val[i];
}
}
color_node->output_val.r = color_val[0];
color_node->output_val.g = color_val[1];
color_node->output_val.b = color_val[2];
color_node->output_val.a = color_val[3];
}
void node_color_create(struct node_editor *editor, struct nk_vec2 position)
{
struct node_type_color *color_node = (struct node_type_color*)node_editor_add(editor, sizeof(struct node_type_color), "Color", nk_rect(position.x, position.y, 180, 190), 4, 1);
if (color_node)
{
const struct nk_colorf black = {0.0f, 0.0f, 0.0f, 1.0f};
for (int i = 0; i < color_node->node.input_count; i++)
color_node->node.inputs[i].pin_type = type_float;
color_node->node.outputs[0].pin_type = type_color;
color_node->node.pin_spacing.in_padding_y += NODE_DEFAULT_ROW_HEIGHT;
color_node->input_val[0] = 0.0f;
color_node->input_val[1] = 0.0f;
color_node->input_val[2] = 0.0f;
color_node->input_val[3] = 1.0f;
color_node->output_val = black;
color_node->node.display_func = node_color_draw;
color_node->node.eval_func = (void*(*)(struct node*, int)) node_color_eval;
}
}
// ----------------------------------------------------------------------------------------------------
// #include "node_blend.h"
struct node_type_blend {
struct node node;
struct nk_colorf input_val[2];
struct nk_colorf output_val;
float blend_val;
};
static struct nk_colorf *node_blend_eval(struct node *node, int oIndex) {
struct node_type_blend* blend_node = (struct node_type_blend*)node;
return &blend_node->output_val;
}
static void node_blend_display(struct nk_context *ctx, struct node *node) {
struct node_type_blend *blend_node = (struct node_type_blend*)node;
const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f};
float blend_amnt;
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
for (int i = 0; i < 2; i++){
if(node->inputs[i].is_connected) {
blend_node->input_val[i] = *(struct nk_colorf*)node_editor_eval_connected(node, i);
}
else {
blend_node->input_val[i] = blank;
}
nk_button_color(ctx, nk_rgba_cf(blend_node->input_val[i]));
}
if (node->inputs[2].is_connected) {
blend_amnt = *(float*)node_editor_eval_connected(node, 2);
blend_amnt = nk_propertyf(ctx, "#Blend", blend_amnt, blend_amnt, blend_amnt, 0.01f, 0.01f);
}
else {
blend_node->blend_val = nk_propertyf(ctx, "#Blend", 0.0f, blend_node->blend_val, 1.0f, 0.01f, 0.01f);
blend_amnt = blend_node->blend_val;
}
if(node->inputs[0].is_connected && node->inputs[1].is_connected) {
blend_node->output_val.r = blend_node->input_val[0].r * (1.0f-blend_amnt) + blend_node->input_val[1].r * blend_amnt;
blend_node->output_val.g = blend_node->input_val[0].g * (1.0f-blend_amnt) + blend_node->input_val[1].g * blend_amnt;
blend_node->output_val.b = blend_node->input_val[0].b * (1.0f-blend_amnt) + blend_node->input_val[1].b * blend_amnt;
blend_node->output_val.a = blend_node->input_val[0].a * (1.0f-blend_amnt) + blend_node->input_val[1].a * blend_amnt;
}
else {
blend_node->output_val = blank;
}
}
void node_blend_create(struct node_editor *editor, struct nk_vec2 position) {
struct node_type_blend* blend_node = (struct node_type_blend*)node_editor_add(editor, sizeof(struct node_type_blend), "Blend", nk_rect(position.x, position.y, 180, 130), 3, 1);
if (blend_node) {
const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f};
for (int i = 0; i < (int)NK_LEN(blend_node->input_val); i++)
blend_node->node.inputs[i].pin_type = type_color;
blend_node->node.outputs[0].pin_type = type_color;
// blend_node->node.pin_spacing.in_padding_y = 42.0f;
// blend_node->node.pin_spacing.in_spacing_y = 29.0f;
for (int i = 0; i < (int)NK_LEN(blend_node->input_val); i++)
blend_node->input_val[i] = blank;
blend_node->output_val = blank;
blend_node->blend_val = 0.5f;
blend_node->node.display_func = node_blend_display;
blend_node->node.eval_func = (void*(*)(struct node*, int)) node_blend_eval;
}
}
/* ================================= */
#define NK_RGB3(r,g,b) {r,g,b,255}
#define BG_COLOR ((struct nk_color){0,0,0,80}) // nk_rgba(0,0,0,192)
static
struct editor_node_style {
int pin_type;
const char *shape;
struct nk_color color_idle;
struct nk_color color_hover;
} styles[] = {
// order matters:
{ type_flow, "triangle_right", NK_RGB3(200,200,200), NK_RGB3(255,255,255) }, // if .num_links == 0
{ type_int, "circle", NK_RGB3(33,227,175), NK_RGB3(135,239,195) },
{ type_float, "circle", NK_RGB3(156,253,65), NK_RGB3(144,225,137) },
{ type_block, "circle", NK_RGB3(6,165,239), NK_RGB3(137,196,247) },
{ type_texture, "circle", NK_RGB3(148,0,0), NK_RGB3(183,137,137) },
{ type_image, "circle", NK_RGB3(200,130,255), NK_RGB3(220,170,255) },
{ type_color, "circle", NK_RGB3(252,200,35), NK_RGB3(255,217,140) },
};
#define COLOR_FLOW_HI styles[type_flow].color_hover
#define COLOR_FLOW_LO styles[type_flow].color_idle
#define GRID_SIZE 32.0f
#define GRID_COLOR ((struct nk_color)NK_RGB3(60,60,80))
#define GRID_THICKNESS 1.0f
#define LINK_THICKNESS 1.0f
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
vec2 a = (POINT_A); \
vec2 b = (POINT_B); \
nk_stroke_line(canvas, a.x, a.y, b.x, b.y, LINK_THICKNESS, COLOR); \
} while(0)
#undef LINK_DRAW
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
vec2 a = (POINT_A); \
vec2 b = (POINT_B); \
nk_stroke_curve(canvas, a.x, a.y, a.x+50, a.y, b.x-50, b.y, b.x, b.y, LINK_THICKNESS, COLOR); \
} while(0)
#undef LINK_DRAW
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
vec2 a = (POINT_A); \
vec2 b = (POINT_B); \
float dist2 = len2( sub2( ptr2(&b.x), ptr2(&a.x) ) ); \
vec2 mid_a = mix2( ptr2(&a.x), ptr2(&b.x), 0.25 ); mid_a.y += dist2/2; \
vec2 mid_b = mix2( ptr2(&a.x), ptr2(&b.x), 0.75 ); mid_b.y += dist2/3; \
nk_stroke_curve(canvas, a.x, a.y, mid_a.x, mid_a.y, mid_b.x, mid_b.y, b.x, b.y, LINK_THICKNESS, COLOR); \
} while(0)
#define PIN_RADIUS 12
#define PIN_THICKNESS 1.0f
#define PIN_DRAW(PIN_ADDR,POINT,RADIUS) do { \
circle.x = (POINT).x - (RADIUS) / 2; \
circle.y = (POINT).y - (RADIUS) / 2; \
circle.w = circle.h = (RADIUS); \
struct nk_color color = node_get_type_color((PIN_ADDR).pin_type); \
if((PIN_ADDR).is_connected) \
nk_fill_circle(canvas, circle, color); \
else \
nk_stroke_circle(canvas, circle, PIN_THICKNESS, color); \
} while(0)
static struct nk_color node_get_type_color(unsigned pin_type) {
for( int i = 0; i < type_total; ++i )
if( styles[i].pin_type == pin_type )
return styles[i].color_idle;
return ((struct nk_color)NK_RGB3(255,0,255));
}
static void node_editor_push(struct node_editor *editor, struct node *node) {
if (!editor->begin) {
node->next = NULL;
node->prev = NULL;
editor->begin = node;
editor->end = node;
} else {
node->prev = editor->end;
if (editor->end)
editor->end->next = node;
node->next = NULL;
editor->end = node;
}
}
static void node_editor_pop(struct node_editor *editor, struct node *node) {
if (node->next)
node->next->prev = node->prev;
if (node->prev)
node->prev->next = node->next;
if (editor->end == node)
editor->end = node->prev;
if (editor->begin == node)
editor->begin = node->next;
node->next = NULL;
node->prev = NULL;
}
static struct node* node_editor_find_by_id(struct node_editor *editor, int ID) {
struct node *iter = editor->begin;
while (iter) {
if (iter->ID == ID)
return iter;
iter = iter->next;
}
return NULL;
}
static struct node_link* node_editor_find_link_by_output(struct node_editor *editor, struct node *output_node, int node_input_connector) {
for( int i = 0; i < editor->link_count; i++ ) {
if (editor->links[i].output_node == output_node &&
editor->links[i].output_pin == node_input_connector &&
editor->links[i].is_active == nk_true) {
return &editor->links[i];
}
}
return NULL;
}
static struct node_link* node_editor_find_link_by_input(struct node_editor *editor, struct node *input_node, int node_output_connector) {
for( int i = 0; i < editor->link_count; i++ ) {
if (editor->links[i].input_node == input_node &&
editor->links[i].input_pin == node_output_connector &&
editor->links[i].is_active == nk_true) {
return &editor->links[i];
}
}
return NULL;
}
static void node_editor_delete_link(struct node_link *link) {
link->is_active = nk_false;
link->input_node->outputs[link->input_pin].is_connected = nk_false;
link->output_node->inputs[link->output_pin].is_connected = nk_false;
}
struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, int in_count, int out_count) {
static int IDs = 0;
struct node *node = NULL;
if ((nk_size)editor->node_count < NK_LEN(editor->node_buf)) {
/* node_buf has unused pins */
node = MALLOC(nodeSize);
editor->node_buf[editor->node_count++] = node;
node->ID = IDs++;
} else {
/* check for freed up pins in node_buf */
for (int i = 0; i < editor->node_count; i++) {
if (editor->node_buf[i] == NULL) {
node = MALLOC(nodeSize);
editor->node_buf[i] = node;
node->ID = i;
break;
}
}
}
if (node == NULL) {
puts("Node creation failed");
return NULL;
}
node->bounds = bounds;
node->input_count = in_count;
node->output_count = out_count;
node->inputs = MALLOC(node->input_count * sizeof(struct node_pin));
node->outputs = MALLOC(node->output_count * sizeof(struct node_pin));
for (int i = 0; i < node->input_count; i++) {
node->inputs[i].is_connected = nk_false;
node->inputs[i].pin_type = type_float; /* default pin type */
}
for (int i = 0; i < node->output_count; i++) {
node->outputs[i].is_connected = nk_false;
node->outputs[i].pin_type = type_float; /* default pin type */
}
/* default pin spacing */
node->pin_spacing.in_padding_x = 2;
node->pin_spacing.in_padding_y = 32 + 25/2 + 6; // titlebar height + next half row + adjust
node->pin_spacing.in_spacing_y = 25; // row height+border
node->pin_spacing.out_padding_x = 3;
node->pin_spacing.out_padding_y = 32 + 25/2 + 6; // titlebar height + next half row + adjust
node->pin_spacing.out_spacing_y = 25; // row height+border
strcpy(node->name, name);
node_editor_push(editor, node);
return node;
}
void *node_editor_eval_connected(struct node* node, int input_pin_number) {
NK_ASSERT(node->inputs[input_pin_number].is_connected);
return node->inputs[input_pin_number].connected_node->eval_func(node->inputs[input_pin_number].connected_node, node->inputs[input_pin_number].connected_pin);
}
static void node_editor_link(struct node_editor *editor, struct node *in_node, int in_pin, struct node *out_node, int out_pin) {
/* Confusingly, in and out nodes/pins here refer to the inputs and outputs OF THE LINK ITSELF, not the nodes */
struct node_link *link = NULL;
if ((nk_size)editor->link_count < NK_LEN(editor->links)) {
link = &editor->links[editor->link_count++];
} else {
for (int i = 0; i < (int)NK_LEN(editor->links); i++)
{
if (editor->links[i].is_active == nk_false) {
link = &editor->links[i];
break;
}
}
}
if (link) {
out_node->inputs[out_pin].is_connected = nk_true;
in_node->outputs[in_pin].is_connected = nk_true;
out_node->inputs[out_pin].connected_node = in_node;
out_node->inputs[out_pin].connected_pin = in_pin;
link->input_node = in_node;
link->input_pin = in_pin;
link->output_node = out_node;
link->output_pin = out_pin;
link->is_active = nk_true;
} else {
puts("Too many links");
}
}
static void node_editor_init(struct node_editor *editor) {
if (editor->initialized) return;
struct nk_rect total_space = nk_window_get_content_region(ui_ctx);
struct nk_vec2 output_node_position = { total_space.w*2/3, total_space.h/3 };
struct nk_vec2 color_node_position = { total_space.w*1/4, total_space.h/3 };
memset(editor, 0, sizeof(*editor));
editor->output_node = node_output_create(editor, output_node_position);
node_color_create(editor, color_node_position);
editor->show_grid = nk_true;
editor->initialized = 1;
}
static int node_editor(struct node_editor *editor) {
int n = 0;
struct nk_rect total_space;
const struct nk_input *in = &ui_ctx->input;
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct node *updated = 0;
node_editor_init(editor);
{
/* allocate complete window space */
total_space = nk_window_get_content_region(ui_ctx);
nk_layout_space_begin(ui_ctx, NK_STATIC, total_space.h, editor->node_count);
{
struct node *it = editor->begin;
struct nk_rect size = nk_layout_space_bounds(ui_ctx);
struct nk_panel *nodePanel = 0;
if (editor->show_grid) {
/* display grid */
for (float x = (float)fmod(size.x - editor->scrolling.x, GRID_SIZE); x < size.w; x += GRID_SIZE)
nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, GRID_THICKNESS, GRID_COLOR);
for (float y = (float)fmod(size.y - editor->scrolling.y, GRID_SIZE); y < size.h; y += GRID_SIZE)
nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, GRID_THICKNESS, GRID_COLOR);
}
/* execute each node as a movable group */
/* loop through nodes */
while (it) {
/* Output node window should not have a close button */
nk_flags nodePanel_flags = NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE;
if (it != editor->output_node)
nodePanel_flags |= NK_WINDOW_CLOSABLE;
/* calculate scrolled node window position and size */
nk_layout_space_push(ui_ctx, nk_rect(it->bounds.x - editor->scrolling.x,
it->bounds.y - editor->scrolling.y, it->bounds.w, it->bounds.h));
/* execute node window */
char *name = va(" " ICON_MD_MENU " %s",it->name); //< @r-lyeh added some spacing+icon because of our UI customizations
struct nk_color bak = ui_ctx->style.window.fixed_background.data.color;
ui_ctx->style.window.fixed_background.data.color = BG_COLOR;
if (nk_group_begin(ui_ctx, name, nodePanel_flags))
{
/* always have last selected node on top */
nodePanel = nk_window_get_panel(ui_ctx);
if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nodePanel->bounds) &&
(!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT,
nk_layout_space_rect_to_screen(ui_ctx, nodePanel->bounds)))) &&
editor->end != it)
{
updated = it;
}
if ((nodePanel->flags & NK_WINDOW_HIDDEN)) /* Node close button has been clicked */
{
/* Delete node */
struct node_link *link_remove;
node_editor_pop(editor, it);
for (int n = 0; n < it->input_count; n++) {
if ((link_remove = node_editor_find_link_by_output(editor, it, n)))
{
node_editor_delete_link(link_remove);
}
}
for (int n = 0; n < it -> output_count; n++) {
while((link_remove = node_editor_find_link_by_input(editor, it, n)))
{
node_editor_delete_link(link_remove);
}
}
NK_ASSERT(editor->node_buf[it->ID] == it);
editor->node_buf[it->ID] = NULL;
FREE(it->inputs);
FREE(it->outputs);
FREE(it);
}
else {
/* ================= NODE CONTENT ===================== */
it->display_func(ui_ctx, it);
/* ==================================================== */
}
nk_group_end(ui_ctx);
}
ui_ctx->style.window.fixed_background.data.color = bak;
if (!(nodePanel->flags & NK_WINDOW_HIDDEN))
{
/* node pin and linking */
struct nk_rect bounds;
bounds = nk_layout_space_rect_to_local(ui_ctx, nodePanel->bounds);
bounds.x += editor->scrolling.x;
bounds.y += editor->scrolling.y;
it->bounds = bounds;
/* output pins */
for (int n = 0; n < it->output_count; ++n) {
struct nk_rect circle;
struct nk_vec2 pt = {nodePanel->bounds.x, nodePanel->bounds.y};
pt.x += nodePanel->bounds.w - PIN_RADIUS / 2 + it->pin_spacing.out_padding_x;
pt.y += it->pin_spacing.out_padding_y + it->pin_spacing.out_spacing_y * (n);
PIN_DRAW(it->outputs[n],pt,PIN_RADIUS);
/* start linking process */
/* set linking active */
if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) {
editor->linking.active = nk_true;
editor->linking.node = it;
editor->linking.input_id = it->ID;
editor->linking.input_pin = n;
}
/* draw link being dragged (from linked pin to mouse position) */
if (editor->linking.active && editor->linking.node == it &&
editor->linking.input_pin == n) {
LINK_DRAW(vec2(circle.x+3,circle.y+3),ptr2(&in->mouse.pos.x),COLOR_FLOW_HI);
}
}
/* input pins */
for (int n = 0; n < it->input_count; ++n) {
struct nk_rect circle;
struct nk_vec2 pt = {nodePanel->bounds.x, nodePanel->bounds.y};
pt.x += it->pin_spacing.in_padding_x;
pt.y += it->pin_spacing.in_padding_y + it->pin_spacing.in_spacing_y * (n);
PIN_DRAW(it->inputs[n],pt,PIN_RADIUS);
/* Detach link */
if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true) &&
editor->linking.active == nk_false &&
it->inputs[n].is_connected == nk_true) {
struct node_link *node_relink = node_editor_find_link_by_output(editor, it, n);
editor->linking.active = nk_true;
editor->linking.node = node_relink->input_node;
editor->linking.input_id = node_relink->input_node->ID;
editor->linking.input_pin = node_relink->input_pin;
node_editor_delete_link(node_relink);
}
/* Create link */
if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) &&
nk_input_is_mouse_hovering_rect(in, circle) &&
editor->linking.active &&
editor->linking.node != it &&
it->inputs[n].pin_type == editor->linking.node->outputs[editor->linking.input_pin].pin_type &&
it->inputs[n].is_connected != nk_true) {
editor->linking.active = nk_false;
node_editor_link(editor, editor->linking.node,
editor->linking.input_pin, it, n);
}
}
}
it = it->next;
}
/* reset (output) linking connection */
if (editor->linking.active && (!!input(KEY_LCTRL) || !!input(KEY_RCTRL) || nk_input_is_mouse_released(in, NK_BUTTON_LEFT))) {
editor->linking.active = nk_false;
editor->linking.node = NULL;
}
/* draw each static link */
for (int n = 0; n < editor->link_count; ++n) {
struct node_link *link = &editor->links[n];
if (link->is_active == nk_true){
struct node *ni = link->input_node;
struct node *no = link->output_node;
struct nk_vec2 l0 = nk_layout_space_to_screen(ui_ctx, nk_vec2(ni->bounds.x + ni->bounds.w + ni->pin_spacing.out_padding_x, 3.0f + ni->bounds.y + ni->pin_spacing.out_padding_y + ni->pin_spacing.out_spacing_y * (link->input_pin)));
struct nk_vec2 l1 = nk_layout_space_to_screen(ui_ctx, nk_vec2(no->bounds.x + no->pin_spacing.in_padding_x, 3.0f + no->bounds.y + no->pin_spacing.in_padding_y + no->pin_spacing.in_spacing_y * (link->output_pin)));
l0.x -= editor->scrolling.x;
l0.y -= editor->scrolling.y;
l1.x -= editor->scrolling.x;
l1.y -= editor->scrolling.y;
struct nk_color color = node_get_type_color(no->inputs[link->output_pin].pin_type);
LINK_DRAW(ptr2(&l0.x), ptr2(&l1.x), color);
}
}
if (updated) {
/* reshuffle nodes to have least recently selected node on top */
node_editor_pop(editor, updated);
node_editor_push(editor, updated);
}
/* node selection */
if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ui_ctx))) {
it = editor->begin;
editor->selected = NULL;
editor->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200);
while (it) {
struct nk_rect b = nk_layout_space_rect_to_screen(ui_ctx, it->bounds);
b.x -= editor->scrolling.x;
b.y -= editor->scrolling.y;
if (nk_input_is_mouse_hovering_rect(in, b))
editor->selected = it;
it = it->next;
}
}
/* contextual menu */
if (nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 220), nk_window_get_bounds(ui_ctx))) {
struct nk_vec2 wincoords = { in->mouse.pos.x-total_space.x-50, in->mouse.pos.y-total_space.y-32 };
#if 1
static char *filter = 0;
static int do_filter = 0;
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(ICON_MD_SEARCH ";");
if( choice == 1 ) do_filter = 1;
if( do_filter ) {
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &filter);
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
do_filter = 0;
}
} else {
if( filter ) filter[0] = '\0';
}
char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*";
#endif
#define ui_label_filtered(lbl) (strmatchi(lbl,filter_mask) && ui_label(lbl))
int close = 0;
if (ui_label_filtered("=Add Color node")) close=1,node_color_create(editor, wincoords);
if (ui_label_filtered("=Add Float node")) close=1,node_float_create(editor, wincoords);
if (ui_label_filtered("=Add Blend Node")) close=1,node_blend_create(editor, wincoords);
if (ui_label_filtered(editor->show_grid ? "=Hide Grid" : "=Show Grid"))
close=1,editor->show_grid = !editor->show_grid;
if(close) do_filter = 0, (filter ? filter[0] = '\0' : '\0'), nk_contextual_close(ui_ctx);
nk_contextual_end(ui_ctx);
}
}
nk_layout_space_end(ui_ctx);
/* window content scrolling */
if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ui_ctx)) &&
nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) {
editor->scrolling.x += in->mouse.delta.x;
editor->scrolling.y += in->mouse.delta.y;
}
}
return !nk_window_is_closed(ui_ctx, "NodeEdit");
}
int main() {
static int open = 1;
window_create(0.75, 0);
while(window_swap() && open) {
if( ui_window("Node editor",&open) ) {
static struct node_editor nodeEditor = {0};
node_editor(&nodeEditor);
ui_window_end();
}
}
}

View File

@ -0,0 +1,268 @@
// pathfind demo
// - rlyeh, public domain
//
// @todo: use 2d sprites instead, will shorten things
#include "fwk.h"
bool topdown_cam = 1;
void move_players();
int main() {
window_create(85, 0); // WINDOW_MSAA4);
window_title(__FILE__);
// window_fps_lock(60);
// game loop
while( window_swap() && !input(KEY_ESC) ) {
if( input(KEY_LALT) && input_down(KEY_Z) ) window_record(__FILE__ ".mp4");
move_players();
if( input_down(MOUSE_R) ) topdown_cam ^= 1;
}
}
struct player_t {
const char *name;
model_t mdl;
anim_t idle, run; // anim clips
float keys[4], scale; // up,down,left,right
vec2 inertia; // [forward,yaw]
vec3 pivot, speed; // [pitch,yaw,roll] [turn speed,forward speed,anim speed fps]
vec3 pos, dir, pad; // [position] [facing dir] [gamepad accumulator]
bool notified;
float brain[4]; // AI
};
// every chunk is a 2D grid [200x200 units], center at (W/2,H/2), bit:1 means traversable
array(unsigned) chunk = 0; // chunk partition in world
array(aabb) blockers = 0; // static blockers in world
vec3 from, to; bool recast; bool vis_recast;
enum { W = 200, Z = 200 };
void draw_scene() {
do_once array_resize(chunk, W*Z);
enum { NUM_BLOCKS = 200 };
do_once
for( int i = 0; i < NUM_BLOCKS; ++i) {
float ox = randi(-W/2,W/2);
float oz = randi(-Z/2,Z/2);
float ww = randi(1,10);
float dd = randi(1,10);
float hh = randi(3,10);
aabb b = { vec3(ox,0,oz), vec3(ox+ww,hh,oz+dd) };
static aabb spawn_zone = { {-10,-10,-10},{10,10,10} };
if( aabb_hit_aabb(b, spawn_zone) ) continue;
array_push(blockers, b);
for( int y = oz; y < (oz+dd); ++y )
for( int x = ox; x < (ox+ww); ++x )
chunk[ (x + W/2) + mini(y + Z/2, Z-1) * W ] |= 1;
}
ddraw_color(BLUE);
for( int i = 0; i < NUM_BLOCKS; ++i) {
ddraw_aabb(blockers[i].min, blockers[i].max);
}
// debug traversal visualization
if( input(KEY_F1) ) {
ddraw_color(GREEN);
for( int z = 0; z < Z; ++z )
for( int x = 0; x < W; ++x )
if( !chunk[x+z*W] ) ddraw_cube( vec3(x-W/2-0.5,0,z-Z/2-0.5), 1 );
ddraw_color(RED);
for( int z = 0; z < Z; ++z )
for( int x = 0; x < W; ++x )
if( chunk[x+z*W] ) ddraw_cube( vec3(x-W/2-0.5,0,z-Z/2-0.5), 1 );
}
static vec2i path[W*Z] = {0};
static int path_count = 0;
static uint64_t taken = 0;
if( vis_recast || input_down(KEY_SPACE) ) {
vis_recast = 0;
taken =- time_ns();
path_count = pathfind_astar(W, Z, chunk, vec2i(clampf(from.x+W/2,0,W-1),clampf(from.z+Z/2,0,Z-1)), vec2i(clampf(to.x+W/2,0,W-1),clampf(to.z+Z/2,0,Z-1)), path, countof(path));
taken += time_ns();
}
font_print(va(FONT_TOP FONT_RIGHT "%d (%5.2fms)", path_count, taken / 1e6));
// path visualization
if( path_count ) {
ddraw_color(YELLOW); // wizard of oz
vec2i* p = path;
for( int i = 0; i < path_count; ++i, ++p ) {
ddraw_point(vec3(p->x-W/2,0,p->y-Z/2));
}
}
}
void move_players() {
static struct player_t player[3];
do_once
player[0] = (struct player_t)
{ "PLAYER-1", model("kgirls01.fbx", 0), loop(0,60,0.25,0), loop(66,85,0.25,0), // idle anim [0..60], run anim [66..85]
{KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT}, 2, {0.90,0.80}, {-100}, {3, 0.30, 30}, {0}, {1} },
player[1] = (struct player_t)
{ "PLAYER-2", model("george.fbx", 0), loop(0,100,0.25,0), loop(372,396,0.25,0), // idle anim [0..100], run anim [372..396]
{KEY_I,KEY_K,KEY_J,KEY_L}, 1, {0.95,0.90}, {-90,-90}, {1.75, 0.25, 24}, {-5}, {1} },
player[2] = (struct player_t)
{ "PLAYER-3", model("alien.fbx", 0), loop(110,208,0.25,0), loop(360,380,0.25,0), // idle anim [110..208], run anim [360..380]
{KEY_W,KEY_S,KEY_A,KEY_D}, 0.85, {0.85,0.75}, {-90,-90}, {3.5, 0.35, 60}, {5}, {1} };
static camera_t cam; do_once cam = camera();
static skybox_t sky; do_once sky = skybox("cubemaps/stardust", 0);
skybox_render(&sky, cam.proj, cam.view);
for( int i = 0; i < countof(player); ++i )
ddraw_position_dir(player[i].pos, player[i].dir, 1.0f);
ddraw_ground(0);
draw_scene();
ddraw_flush();
// move and render players
for( int i = 0; i < countof(player); ++i ) {
struct player_t *p = &player[i];
// capture inputs
p->brain[0] = input(p->keys[0]);
p->brain[1] = input(p->keys[1]);
p->brain[2] = input(p->keys[2]);
p->brain[3] = input(p->keys[3]);
// setup waypoints for PLAYER-1
static array(vec3) points;
if( input_down(MOUSE_L) && !ui_hover() ) {
vec3 pt = editor_pick(input(MOUSE_X), input(MOUSE_Y));
hit *h = ray_hit_plane(ray(cam.position, pt), plane(vec3(0,0,0),vec3(0,1,0)));
if(h) {
if (array_count(points) == 0) recast = 1, vis_recast = 1;
array_push(points, h->p), from = player[0].pos, to = *array_back(points);
}
}
// ddraw waypoints
ddraw_color(YELLOW);
for( int i = 1; i < array_count(points); ++i) ddraw_line(points[i-1],points[i]);
for( int i = 0; i < array_count(points); ++i) ddraw_circle(points[i], vec3(0,1,0), 1); // prism(points[i], 1, 0, vec3(0,1,0), 4);
ddraw_color(RED);
for( int i = 0; i < array_count(points); ++i) ddraw_point(points[i]);
ddraw_color(WHITE);
// move thru waypoints (PLAYER-1 only)
if( i == 0 && array_count(points) ) {
static vec2i path[W*Z] = {0};
static int path_count = 0;
static int path_index = 0;
struct player_t *p = &player[i];
vec3 pt = points[0];
if( recast ) {
recast = 0;
path_count = pathfind_astar(W, Z, chunk, vec2i(clampf(p->pos.x+W/2,0,W-1),clampf(p->pos.z+Z/2,0,Z-1)), vec2i(clampf(pt.x+W/2,0,W-1),clampf(pt.z+Z/2,0,Z-1)), path, countof(path));
path_index = path_count-1;
}
if (path_count == 0) {
array_pop_front(points);
continue;
}
vec2i ph = path[path_index];
vec3 dst = vec3(ph.x-W/2,0,ph.y-Z/2);
vec3 vector1 = norm3(vec3(p->dir.x,0,p->dir.z));
vec3 vector2 = norm3(sub3(dst,p->pos));
float angle = atan2(vector2.z, vector2.x) - atan2(vector1.z, vector1.x);
angle *= 180 / C_PI;
// range [0, 360)
// if (angle < 0) { angle += 2 * 180; }
// range (-180, 180]
if (angle > 180) { angle -= 2 * 180; }
else if (angle <= -180) { angle += 2 * 180; }
float dist = len3(sub3(p->pos, dst));
if(dist < 1) {
path_index--;
if (path_index <= 0) {
// goal
array_pop_front(points);
recast = 1, vis_recast = 1;
}
}
else {
if( dist < 10 && abs(angle) > 10 ) {
// spin only
p->brain[ angle < 0 ? 2 : 3 ] = 1;
}
else {
// spin
p->brain[ angle < 0 ? 2 : 3 ] = 1;
// move forward
p->brain[ 0 ] = 1;
}
}
}
// accumulate movement
float yaw = p->brain[2] - p->brain[3];
float fwd = p->brain[0] - p->brain[1]; if(fwd<0) fwd = 0;
p->pad.x = p->pad.x * (p->inertia.y) + yaw * (1-p->inertia.y);
p->pad.y = p->pad.y * (p->inertia.x) + fwd * (1-p->inertia.x);
// rotate yaw dir, then apply into position
p->dir = rotatey3(p->dir, p->speed.x * p->pad.x);
p->pos = add3(p->pos, scale3(p->dir, p->speed.y * p->pad.y));
// animate clips and blend
anim_t *primary = fwd ? &p->run : &p->idle;
anim_t *secondary = fwd ? &p->idle : &p->run;
model_animate_blends(p->mdl, primary, secondary, window_delta() * p->speed.z);
// render model. transforms on top of initial pivot and scale
mat44 M; compose44(M, p->pos, eulerq(add3(p->pivot,vec3(atan2(p->dir.z,p->dir.x)*180/C_PI,0,0))),vec3(p->scale,p->scale,p->scale));
model_render(p->mdl, cam.proj, cam.view, M, 0);
// ui
if( yaw||fwd ) if( !p->notified ) p->notified = 1, ui_notify(0, va(ICON_MD_GAMEPAD " %s joined the game.", p->name));
ddraw_text(p->pos, 0.01, va("%s: %6.3f", fwd?"run":"idle", (fwd ? p->run : p->idle).timer ));
}
static int reset_topdown_cam = 1;
if( topdown_cam ) {
if( reset_topdown_cam ) reset_topdown_cam = 0, camera_teleport(&cam, vec3(0,80,0)), camera_lookat(&cam, vec3(cam.position.x,0,cam.position.z+1));
// gta1 cam style: top-down view, look-at player, smoothed values. optional rotation
if(0) { static vec3 smooth1; camera_lookat(&cam, smooth1 = mix3(add3(player[0].pos,player[0].dir),smooth1,0.95)); }
if(1) { static vec3 smooth2; camera_teleport(&cam, smooth2 = mix3(add3(player[0].pos,vec3(0.1,80,0.1)),smooth2,0.95)); }
camera_fov(&cam, cam.fov * 0.99 + 60 * 0.01);
} else {
reset_topdown_cam = 1;
// look at the players that are moving; center of their triangle otherwise.
float A = len3(player[0].pad); if(A<0.01) A=0;
float B = len3(player[1].pad); if(B<0.01) B=0;
float C = len3(player[2].pad); if(C<0.01) C=0;
float weight = A + B + C;
if( weight ) A /= weight, B /= weight, C /= weight; else A = B = C = 0.33333;
// smooth target before sending to camera
vec3 target = add3(add3(scale3(player[0].pos,A), scale3(player[1].pos,B)), scale3(player[2].pos,C));
static vec3 smooth1; camera_lookat(&cam, smooth1 = mix3(target,smooth1,!weight ? 0.98 : 0.95));
target = vec3(10,10,10);
static vec3 smooth2; camera_teleport(&cam, smooth2 = mix3(target,smooth2,!weight ? 0.98 : 0.95));
camera_fov(&cam, cam.fov * 0.99 + 45 * 0.01);
}
// ui
if( ui_panel("Controls", 0) ) {
ui_label2("[F1]", ICON_MD_KEYBOARD " Debug traversal visualization");
ui_label2("[Left mouse]", ICON_MD_MOUSE " Set Waypoint");
ui_label2("[Right mouse]", ICON_MD_MOUSE " Toggle camera");
ui_label2("Girl", ICON_MD_GAMEPAD " CURSOR keys");
ui_label2("Alien", ICON_MD_GAMEPAD " W,A,S,D keys");
ui_label2("Robot", ICON_MD_GAMEPAD " I,J,K,L keys");
ui_panel_end();
}
}

664
_fwk/demos/99-pbr.c 100644
View File

@ -0,0 +1,664 @@
// PBR model viewer. Based on Foxotron by @gargaj + cce/Peisik (UNLICENSE).
// - rlyeh, public domain.
//
// @todo: Middle mouse button to pan camera @todo
#include "fwk.h"
#if is(tcc) && !is(win32) // @todo: remove this & test on linux
int log2_64 (uint64_t value) {
const int tab64[64] = {
63, 0, 58, 1, 59, 47, 53, 2,
60, 39, 48, 27, 54, 33, 42, 3,
61, 51, 37, 40, 49, 18, 28, 20,
55, 30, 34, 11, 43, 14, 22, 4,
62, 57, 46, 52, 38, 26, 32, 41,
50, 36, 17, 19, 29, 10, 13, 21,
56, 45, 25, 31, 35, 16, 9, 12,
44, 24, 15, 8, 23, 7, 6, 5};
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value |= value >> 32;
return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
}
#define log2 log2_64
#endif
// -----------------------------------------------------------------------------
// textures
texture_t *LoadTextureRGBA8( const char *pathfile, unsigned flags ) {
int flags_hdr = strendi(pathfile, ".hdr") ? TEXTURE_FLOAT | TEXTURE_RGBA : 0;
texture_t t = texture(pathfile, flags | TEXTURE_LINEAR | TEXTURE_MIPMAPS | TEXTURE_REPEAT | flags_hdr);
if( t.id == texture_checker().id ) {
return NULL;
}
texture_t *tex = CALLOC(1, sizeof(texture_t));
*tex = t;
return tex;
}
// -----------------------------------------------------------------------------
// models
typedef struct Mesh {
GLuint vao, vbo, ibo;
int vert_stride;
void *vert_stream;
int num_verts, num_tris;
int material_idx;
bool transparent;
vec3 aabb_min, aabb_max;
} Mesh;
typedef struct Model {
array(Mesh) meshes;
array(pbr_material_t) materials;
unsigned shader;
} Model;
bool ModelLoad( Model *G, const char *_path );
void ModelDestroy( Model *G );
void ModelRebind( Model *G, unsigned shader );
void ModelRender( Model *G, const mat44 _worldRootMatrix );
void ModelDestroy( Model *G) {
for( int i = 0, end = array_count(G->materials); i < end; ++i ) {
pbr_material_destroy(&G->materials[i]);
}
array_free(G->materials);
for( int i = 0, end = array_count(G->meshes); i < end; ++i ) {
Mesh *it = &G->meshes[i];
glDeleteBuffers( 1, &it->ibo );
glDeleteBuffers( 1, &it->vbo );
glDeleteVertexArrays( 1, &it->vao );
}
array_free(G->meshes);
}
bool ModelLoad( Model *G, const char *_path ) {
ModelDestroy(G);
// -------------------------------------------------------------------------
Model g = {0};
*G = g;
model_t m = model(_path, 0);
int scn_num_meshes = m.num_meshes;
int scn_num_materials = array_count(m.materials);
// -------------------------------------------------------------------------
for( int i = 0; i < scn_num_materials; i++ ) {
const char *name = m.materials[i].name;
PRINTF("Loading material %d/%d: '%s'\n", i + 1, scn_num_materials, name);
pbr_material_t mt;
pbr_material(&mt, name);
array_push(G->materials, mt);
}
for( int i = 0; i < scn_num_meshes; i++ ) {
PRINTF("Loading mesh %d/%d\n", i + 1, scn_num_meshes);
int verts = m.num_verts;
int faces = m.num_triangles;
unsigned material_index = 0; // &m.iqm->meshes[i].material; // aiGetMeshMaterialIndex(scn_mesh[i]);
bool has_data = verts && faces;
if( !has_data ) {
continue;
}
PRINTF("Loading mesh v%d/f%d\n", verts, faces);
Mesh mesh = { 0 };
mesh.vao = m.vao;
mesh.vbo = m.vbo;
mesh.ibo = m.ibo;
mat44 id; id44(id);
mesh.aabb_min = model_aabb(m, id).min;
mesh.aabb_max = model_aabb(m, id).max;
// p3 n3 t3 b3 u2
mesh.vert_stride = m.stride;
mesh.vert_stream = m.verts;
mesh.num_verts = verts;
mesh.num_tris = faces;
mesh.material_idx = material_index;
// By importing materials before meshes we can investigate whether a mesh is transparent and flag it as such.
const pbr_material_t* mtl = G->materials ? &G->materials[mesh.material_idx] : NULL;
mesh.transparent = false;
if( mtl ) {
mesh.transparent |= mtl->albedo .texture ? mtl->albedo .texture->transparent : mtl->albedo .color.a < 1.0f;
mesh.transparent |= mtl->diffuse.texture ? mtl->diffuse.texture->transparent : mtl->diffuse.color.a < 1.0f;
}
array_push(G->meshes, mesh);
}
#if 0
G->mGlobalAmbient = vec4( 0.3,0.3,0.3,0.3 );
int scn_num_lights = 0;
for( int i = 0; i < scn_num_lights; i++ ) {
PRINTF("Loading light %d/%d\n", i + 1, scn_num_lights);
vec4 *color = aiGetLightColor(scn_light[i]);
char *type = aiGetLightType(scn_light[i]);
if( 0 == strcmp(type, "AMBIENT") ) {
memcpy( &G->mGlobalAmbient, &color->r, sizeof( float ) * 4 );
} else {
// @todo
}
}
#endif
return true;
}
void ModelRender( Model *G, const mat44 _worldRootMatrix ) {
unsigned _shader = G->shader;
shader_bind( _shader );
shader_vec4("global_ambient", vec4(1,1,1,1)); // unused
// loop thrice: first opaque, then transparent backface, then transparent frontface
for(int j = 0; j < 3; ++j) {
bool bTransparentPass = j > 0;
if(bTransparentPass) {
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glCullFace( j == 1 ? GL_FRONT : GL_BACK ); // glDepthMask( GL_FALSE);
}
mat44 mat_world; copy44(mat_world, _worldRootMatrix); // @fixme mMatrices[ node.mID ] * _worldRootMatrix
shader_mat44( "mat_world", mat_world );
for( int i = 0, end = array_count(G->meshes); i < end; i++ ) {
const Mesh *mesh = &G->meshes[ i ];
// Postpone rendering transparent meshes
if(mesh->transparent != bTransparentPass)
continue;
const pbr_material_t *material = &G->materials[ mesh->material_idx ];
shader_colormap( "map_diffuse", material->diffuse );
shader_colormap( "map_normals", material->normals );
shader_colormap( "map_specular", material->specular );
shader_colormap( "map_albedo", material->albedo );
shader_colormap( "map_roughness", material->roughness );
shader_colormap( "map_metallic", material->metallic );
shader_colormap( "map_ao", material->ao );
shader_colormap( "map_ambient", material->ambient );
shader_colormap( "map_emissive", material->emissive );
shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only
shader_vec2( "resolution", vec2(window_width(),window_height()));
glActiveTexture(GL_TEXTURE0); // be nice to Mesa before rendering
glBindVertexArray( mesh->vao );
glDrawElements( GL_TRIANGLES, mesh->num_tris * 3, GL_UNSIGNED_INT, NULL );
}
if(bTransparentPass) {
glDisable( GL_BLEND );
// glDepthMask( GL_TRUE );
}
}
//glBindVertexArray( 0 );
//glUseProgram( 0 );
}
static
void G_SetupVertexArray( unsigned _shader, const char *name, int stride, int num_floats, int *offset, int opt_location ) {
int location = opt_location >= 0 ? opt_location : glGetAttribLocation( _shader, name );
if( location >= 0 ) {
glVertexAttribPointer( location, num_floats, GL_FLOAT, GL_FALSE, stride, (GLvoid *)(uintptr_t)(*offset) );
glEnableVertexAttribArray( location );
}
*offset += num_floats * sizeof( GLfloat );
}
void ModelRebind( Model *G, unsigned _shader ) {
shader_bind(_shader);
if(_shader == G->shader) return;
G->shader = _shader;
for( int i = 0, end = array_count(G->meshes); i < end; i++ ) {
const Mesh *mesh = &G->meshes[ i ];
glBindVertexArray( mesh->vao );
glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ibo );
glDisableVertexAttribArray( 0 );
glDisableVertexAttribArray( 1 );
glDisableVertexAttribArray( 2 );
glDisableVertexAttribArray( 3 );
glDisableVertexAttribArray( 4 );
glDisableVertexAttribArray( 5 );
glDisableVertexAttribArray( 6 );
glDisableVertexAttribArray( 7 );
int offset = 0, stride = mesh->vert_stride;
G_SetupVertexArray( _shader, "in_pos", stride, 3, &offset, -1/*0*/ );
G_SetupVertexArray( _shader, "in_texcoord", stride, 2, &offset, -1/*1*/ );
G_SetupVertexArray( _shader, "in_normal", stride, 3, &offset, -1/*2*/ );
G_SetupVertexArray( _shader, "in_tangent", stride, 4, &offset, -1/*3*/ );
//glBindVertexArray( 0 );
}
}
// -----------------------------------------------------------------------------
// skyboxes
// Extracts single key value from an HDRLabs IBL file. Returns an empty string on error.
static const char *ibl_readkey( const char* pathfile, const char* key ) {
char *data = vfs_read(pathfile);
if( data ) {
const char *found = strstr(data, va("%s=", key));
if( found ) return found + strlen(key) + 1;
found = strstr(data, va("%s =", key));
if( found ) return found + strlen(key) + 2;
}
return "";
}
typedef struct Skybox {
vec3 sunColor; float sunYaw, sunPitch; // ibl settings
texture_t *reflection; // reflection map (hdr)
texture_t *env; // irradiance map (env)
} Skybox;
Skybox g_skybox = { {1,1,1} };
void SkyboxDestroy( Skybox *s ) {
if( s->reflection ) texture_destroy( s->reflection );
if( s->env ) texture_destroy( s->env );
*s = (Skybox){0};
}
bool SkyboxLoad( Skybox *s, const char **slots ) { // hdr,env,ibl
SkyboxDestroy( s );
const char* reflectionPath = slots[0];
const char* envPath = slots[1];
const char* iblPath = slots[2];
// unsigned invalid = texture_checker().id;
// Reflection map
if( reflectionPath ) {
if( (s->reflection = LoadTextureRGBA8( reflectionPath, TEXTURE_SRGB )) != NULL ) {
glBindTexture( GL_TEXTURE_2D, s->reflection->id );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
// Irradiance map
if( envPath ) {
if( (s->env = LoadTextureRGBA8( envPath, TEXTURE_SRGB )) != NULL ) {
glBindTexture( GL_TEXTURE_2D, s->env->id );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
// Sun color & direction from .ibl file
s->sunColor = vec3(1,1,1);
s->sunYaw = 0, s->sunPitch = 0;
if( iblPath ) {
vec3 sc; if( 3 == sscanf(ibl_readkey(iblPath, "SUNcolor"), "%f,%f,%f", &sc.x, &sc.y, &sc.z) ) {
s->sunColor = scale3(sc, 1/255.f);
}
vec2 uv = vec2(atof(ibl_readkey(iblPath, "SUNu")), atof(ibl_readkey(iblPath, "SUNv")));
if( len2(uv) > 0 ) {
s->sunYaw = C_PI * (-2. * uv.x + 0.5f);
s->sunPitch = (0.5f - uv.y) * C_PI;
}
}
return s->reflection && s->env;
}
// -----------------------------------------------------------------------------
// main
const char *shader_names[] = {"Physically Based", "Basic SpecGloss" };
const char *shaders[][2] = { // name, vs, fs
{ "Shaders/pbr.vs", "Shaders/pbr.fs" },
{ "Shaders/basic_specgloss.vs", "Shaders/basic_specgloss.fs" }
};
const char *skyboxes[][3] = { // reflection, env, metadata
{"hdr/Tokyo_BigSight_1k.hdr","hdr/Tokyo_BigSight_Env.hdr","hdr/Tokyo_BigSight.ibl"},
{"hdr/GCanyon_C_YumaPoint_1k.hdr","hdr/GCanyon_C_YumaPoint_Env.hdr","hdr/GCanyon_C_YumaPoint.ibl"},
{"hdr/Factory_Catwalk_1k.hdr","hdr/Factory_Catwalk_Env.hdr","hdr/Factory_Catwalk.ibl"},
{"hdr/MonValley_G_DirtRoad_1k.hdr","hdr/MonValley_G_DirtRoad_Env.hdr","hdr/MonValley_G_DirtRoad.ibl"},
{"hdr/Shiodome_Stairs_1k.hdr","hdr/Shiodome_Stairs_Env.hdr","hdr/Shiodome_Stairs.ibl"},
};
Model gModel;
unsigned gShader = ~0u;
unsigned gShaderConfig = ~0u;
bool LoadShaderConfig( int slot ) { // name,vs,fs
unsigned newShader = shader( vfs_read(shaders[slot][0]), vfs_read(shaders[slot][1]), NULL, NULL, NULL );
if( newShader == ~0u ) return false;
shader_destroy( gShader );
gShaderConfig = slot;
gShader = newShader;
return true;
}
void camera_fit(camera_t *cam) {
vec3 target = scale3( add3( gModel.meshes[0].aabb_min, gModel.meshes[0].aabb_max ), 0.5f);
float distance = len3( sub3( gModel.meshes[0].aabb_max, gModel.meshes[0].aabb_min ) ) * 0.85f;
cam->position = add3(target, scale3(norm3(sub3(cam->position,target)), distance));
camera_lookat(cam, vec3(0,0,0));
}
int main() {
window_create( 75, WINDOW_MSAA2 );
window_title(__FILE__);
// load all fx files in all subdirs
fx_load("fx**.fs");
LoadShaderConfig( 0 );
brdf_lut();
// -------------------------------------------------------------------------
// Mainloop
float model_yaw = 0, model_pitch = 0;
float lightYaw = 0.0f;
float lightPitch = 0.0f;
vec4 skyBackgroundColor = vec4(0.01,0.01,0.02,1); // vec4(1,0,0,1);
float skyExposure = 1.0; // plain 'exposure' instead? this is camera related
float skyBlur = 0.00; // 0.00
float skyOpacity = 0.99; // 0.99
bool do_wireframe = false;
bool do_xzySpace = true; // xzySpace or xyzSpace
bool do_flipY = false;
const mat44 xzyMatrix = {
1, 0, 0, 0,
0, 0, 1, 0,
0,+1, 0, 0,
0, 0, 0, 1 };
camera_t cam = camera(); cam.speed = 0.1;
int firstskyboxes = 0; // 0: tokyo_bigsight
SkyboxLoad( &g_skybox, &skyboxes[firstskyboxes][0] );
lightYaw = g_skybox.sunYaw;
lightPitch = g_skybox.sunPitch;
unsigned skysphereShader = shader( vfs_read("Skyboxes/skysphere.vs"), vfs_read("Skyboxes/skysphere.fs"), NULL, NULL, NULL );
Model skysphere = { 0 }; ModelLoad(&skysphere, "Skyboxes/skysphere.fbx"); ModelRebind(&skysphere, skysphereShader);
if( ModelLoad( &gModel, option("--model", "damagedhelmet.gltf") ) ) {
ModelRebind( &gModel, gShader );
}
cam.position = vec3(+1,0,+1);
camera_fit(&cam);
static mat44 worldRootXYZ; do_once id44(worldRootXYZ); // mat44( 1.0f );
while( window_swap() && !input(KEY_ESC) ) {
if( input(KEY_F5) ) window_reload();
if( input_down( KEY_F ) ) camera_fit(&cam);
// ---------------------------------------------------------------------
static int fps_mode;
if(input_down(KEY_TAB)) { fps_mode ^= 1; camera_fit(&cam); }
if(fps_mode) {
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
window_cursor( !active );
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
} else {
// orbit camera
window_cursor( true );
bool active = !ui_active() && !ui_hover() && !gizmo_active();
vec2 inc_mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active * input(MOUSE_L));
float inc_distance = -0.2f * active * input_diff(MOUSE_W);
camera_orbit(&cam, inc_mouse.x, inc_mouse.y, inc_distance);
// rotate model
model_yaw -= input_diff(MOUSE_X) * 0.2f * active * input(MOUSE_R);
model_pitch += input_diff(MOUSE_Y) * 0.2f * active * input(MOUSE_R);
}
// ---------------------------------------------------------------------
glClearColor( skyBackgroundColor.r, skyBackgroundColor.g, skyBackgroundColor.b, skyBackgroundColor.a );
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
// ---------------------------------------------------------------------
// Mesh state
fx_begin();
profile("PBR Model (bindings)") {
ModelRebind( &gModel, gShader );
shader_mat44( "mat_projection", cam.proj );
//cameraPosition = scale3(cameraPosition, gCameraDistance);
shader_vec3( "camera_position", cam.position );
vec3 lightDirection = vec3( 0, 0, 1 );
lightDirection = rotatex3( lightDirection, deg(lightPitch) );
lightDirection = rotatey3( lightDirection, deg(lightYaw) );
vec3 fillLightDirection = vec3( 0, 0, 1 );
fillLightDirection = rotatex3( fillLightDirection, deg(lightPitch - 0.4f) );
fillLightDirection = rotatey3( fillLightDirection, deg(lightYaw + 0.8f) );
shader_vec3( "lights[0].direction", lightDirection );
shader_vec3( "lights[0].color", g_skybox.sunColor );
shader_vec3( "lights[1].direction", fillLightDirection );
shader_vec3( "lights[1].color", vec3( 0.5f, 0.5f, 0.5f ) );
shader_vec3( "lights[2].direction", neg3(fillLightDirection) );
shader_vec3( "lights[2].color", vec3( 0.25f, 0.25f, 0.25f ) );
shader_float( "skysphere_rotation", lightYaw - g_skybox.sunYaw );
mat44 viewMatrix, inv_viewMatrix;
copy44(viewMatrix, cam.view);
invert44( inv_viewMatrix, viewMatrix);
shader_mat44( "mat_view", viewMatrix );
shader_mat44( "mat_view_inverse", inv_viewMatrix );
shader_bool( "has_tex_skysphere", g_skybox.reflection != NULL );
shader_bool( "has_tex_skyenv", g_skybox.env != NULL );
if( g_skybox.reflection ) {
float mipCount = floor( log2( g_skybox.reflection->h ) );
shader_texture( "tex_skysphere", *g_skybox.reflection );
shader_float( "skysphere_mip_count", mipCount );
}
if( g_skybox.env ) {
shader_texture( "tex_skyenv", *g_skybox.env );
}
shader_texture( "tex_brdf_lut", brdf_lut() );
shader_float( "exposure", skyExposure );
shader_uint( "frame_count", (unsigned)window_frame() );
}
// ---------------------------------------------------------------------
// Mesh render
mat44 M;
copy44( M, do_xzySpace ? xzyMatrix : worldRootXYZ );
if( do_flipY ) scale44( M, 1,-1,1 );
rotate44( M, model_yaw, 0,0,1 );
rotate44( M, model_pitch, 1,0,0 );
profile("PBR Model (render)") {
ModelRender( &gModel, M );
}
profile("PBR Model (wireframe)") {
if( do_wireframe ) {
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glDepthFunc( GL_LEQUAL );
shader_float("exposure", 100.0f );
ModelRender( &gModel, M );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glDepthFunc( GL_LESS );
}
}
// ---------------------------------------------------------------------
// Skysphere render
profile("PBR Skybox") {
ModelRebind(&skysphere, skysphereShader );
mat44 projview; multiply44x2(projview, cam.proj, cam.view);
shader_mat44( "mat_mvp", projview );
shader_bool( "has_tex_skysphere", g_skybox.reflection != NULL );
shader_bool( "has_tex_skyenv", g_skybox.env != NULL );
if( g_skybox.reflection ) {
const float mipCount = floor( log2( g_skybox.reflection->h ) );
shader_texture( "tex_skysphere", *g_skybox.reflection );
shader_float( "skysphere_mip_count", mipCount );
}
if( g_skybox.env ) {
shader_texture( "tex_skyenv", *g_skybox.env );
}
shader_vec4( "background_color", skyBackgroundColor );
shader_float( "skysphere_blur", skyBlur );
shader_float( "skysphere_opacity", skyOpacity );
shader_float( "skysphere_rotation", lightYaw - g_skybox.sunYaw );
shader_float( "exposure", skyExposure );
shader_uint( "frame_count", (unsigned)window_frame() );
glDepthFunc( GL_LEQUAL );
ModelRender(&skysphere, worldRootXYZ );
glDepthFunc( GL_LESS );
}
fx_end();
// ---------------------------------------------------------------------
// UI
if( ui_panel( "Viewer", 0 ) ) {
ui_bool( "Wireframe", &do_wireframe );
ui_separator();
if( ui_radio("Shader config:", shader_names, countof(shader_names), &gShaderConfig) ) {
LoadShaderConfig( gShaderConfig );
ModelRebind(&gModel, gShader );
}
ui_separator();
for( int i = 0; i < countof(skyboxes); i++ ) {
const char *filename = skyboxes[i][0];
bool selected = !strcmp(g_skybox.reflection->filename, file_name(filename));
if( ui_bool( filename, &selected ) ) {
SkyboxLoad( &g_skybox, &skyboxes[i][0] );
lightYaw = g_skybox.sunYaw;
lightPitch = g_skybox.sunPitch;
}
}
ui_separator();
ui_float( "Sky exposure", &skyExposure); skyExposure = clampf(skyExposure, 0.1f, 4.0f );
ui_float( "Sky blur", &skyBlur); skyBlur = clampf(skyBlur, 0.0f, 1.0f );
ui_float( "Sky opacity", &skyOpacity); skyOpacity = clampf(skyOpacity, 0.0f, 1.0f );
ui_color4f( "Sky background", (float *) &skyBackgroundColor.x );
ui_separator();
ui_float( "SunLight Yaw", &lightYaw );
ui_float( "SunLight Pitch", &lightPitch );
ui_panel_end();
}
if( ui_panel( "Model", 0 ) ) {
ui_label(va("Material count: %d", array_count(gModel.materials)));
ui_label(va("Mesh count: %d", array_count(gModel.meshes)));
int triCount = 0; for( int i = 0, end = array_count(gModel.meshes); i < end; ++i ) triCount += gModel.meshes[i].num_tris;
ui_label(va("Triangle count: %d", triCount));
ui_separator();
bool xyzSpace = !do_xzySpace;
if( ui_bool( "XYZ space", &xyzSpace ) ) {
do_xzySpace = !do_xzySpace;
}
ui_bool( "XZY space", &do_xzySpace );
ui_bool( "invert Y", &do_flipY );
ui_separator();
for( int i = 0, end = array_count(gModel.materials); i < end; ++i ) {
pbr_material_t *it = &gModel.materials[i];
ui_label(va("Name: %s", it->name));
ui_float( "Specular shininess", &it->specular_shininess );
ui_separator(); if(ui_colormap( "Albedo", &it->albedo )) colormap(&it->albedo , app_loadfile(), 1);
ui_separator(); if(ui_colormap( "Ambient", &it->ambient )) colormap(&it->ambient , app_loadfile(), 0);
ui_separator(); if(ui_colormap( "AO", &it->ao )) colormap(&it->ao , app_loadfile(), 0);
ui_separator(); if(ui_colormap( "Diffuse", &it->diffuse )) colormap(&it->diffuse , app_loadfile(), 1);
ui_separator(); if(ui_colormap( "Emissive", &it->emissive )) colormap(&it->emissive , app_loadfile(), 1);
ui_separator(); if(ui_colormap( "Metallic", &it->metallic )) colormap(&it->metallic , app_loadfile(), 0);
ui_separator(); if(ui_colormap( "Normal", &it->normals )) colormap(&it->normals , app_loadfile(), 0);
ui_separator(); if(ui_colormap( "Roughness", &it->roughness )) colormap(&it->roughness, app_loadfile(), 0);
ui_separator(); if(ui_colormap( "Specular", &it->specular )) colormap(&it->specular , app_loadfile(), 0);
}
ui_panel_end();
}
if( ui_panel("Help", 0)) {
if( fps_mode ) {
ui_label("TAB: switch to Orbit camera mode");
ui_label("WASD,QEC: move camera");
ui_label("Drag + Mouse Button: camera freelook");
} else {
ui_label("TAB: switch to FPS camera mode");
ui_label("Drag + Left Mouse Button: orbit camera");
ui_label("Drag + Right Mouse Button: rotate model");
ui_label("Mouse wheel: camera distance");
}
ui_label("F: center view");
ui_panel_end();
}
}
}

View File

@ -0,0 +1,568 @@
// spine json loader (wip)
// - rlyeh, public domain
//
// [ref] http://es.esotericsoftware.com/spine-json-format
//
// notable misses:
// - mesh deforms
// - cubic beziers
// - shears
// - bounding boxes
#include "fwk.h"
#define spine spine2
#define spine_render spine_render2
#define spine_ui spine_ui2
#define spine_animate spine_animate2
#define spine_skin spine_skin2
enum { _64 = 64 }; // max bones
typedef struct spine_bone_t {
char *name, *parent;
struct spine_bone_t *parent_bone;
float z; // draw order usually matches bone-id. ie, zindex == bone_id .. root(0) < chest (mid) < finger(top)
float x, y, deg; // base
float x2, y2, deg2; // accum / temporaries during bone transform time
float x3, y3, deg3; // values from timeline
unsigned rect_id;
unsigned atlas_id;
} spine_bone_t;
typedef struct spine_slot_t {
char *name, *bone, *attach;
} spine_slot_t;
typedef struct spine_rect_t {
char *name;
float x,y,w,h,sx,sy,deg;
} spine_rect_t;
typedef struct spine_skin_t {
char *name;
array(spine_rect_t) rects;
} spine_skin_t;
typedef struct spine_animkey_t { // offline; only during loading
float time, curve[4]; // time is mandatory, curve is optional
union {
char *name; // type: attachment (mode-1)
struct { float deg; }; // type: rotate (mode-2)
struct { float x,y; }; // type: translate (mode-3)
};
} spine_animkey_t;
#if 0
typedef struct spine_pose_t { // runtime; only during playing
unsigned frame;
array(vec4) xform; // entry per bone. translation(x,y),rotation(z),attachment-id(w)
} spine_pose_t;
#endif
typedef struct spine_anim_t {
char *name;
union {
#if 0
struct {
unsigned frames;
array(spine_pose_t) poses;
};
#endif
struct {
array(spine_animkey_t) attach_keys[_64];
array(spine_animkey_t) rotate_keys[_64];
array(spine_animkey_t) translate_keys[_64];
};
};
} spine_anim_t;
typedef struct spine_atlas_t {
char *name;
float x,y,w,h,deg;
} spine_atlas_t;
typedef struct spine_t {
char *name;
texture_t texture;
unsigned skin;
array(spine_bone_t) bones;
array(spine_slot_t) slots;
array(spine_skin_t) skins;
array(spine_anim_t) anims;
array(spine_atlas_t) atlas;
// anim controller
unsigned inuse;
float time, maxtime;
} spine_t;
// ---
void spine_convert_animkeys_to_animpose(spine_anim_t *input) {
spine_anim_t copy = *input; // @todo
// @leak: attach/rot/tra keys
}
int find_bone_id(spine_t *s, const char *bone_name) {
for( unsigned i = 0, end = array_count(s->bones); i < end; ++i )
if( !strcmp(s->bones[i].name, bone_name)) return i;
return -1;
}
spine_bone_t *find_bone(spine_t *s, const char *bone_name) {
int bone_id = find_bone_id(s, bone_name);
return bone_id >= 0 ? &s->bones[bone_id] : NULL;
}
void spine_skin(spine_t *p, unsigned skin) {
if( !p->texture.id ) return;
if( skin >= array_count(p->skins) ) return;
p->skin = skin;
char *skin_name = va("%s/", p->skins[skin].name);
int header = strlen(skin_name);
for( int i = 0; i < array_count(p->atlas); ++i) {
if(!strbeg(p->atlas[i].name, skin_name)) continue;
int bone_id = find_bone_id(p, p->atlas[i].name+header );
if( bone_id < 0 ) continue;
p->bones[bone_id].atlas_id = i;
}
for( int i = 0; i < array_count(p->skins[p->skin].rects); ++i) {
int bone_id = find_bone_id(p, p->skins[p->skin].rects[i].name );
if( bone_id < 0 ) continue;
p->bones[bone_id].rect_id = i;
}
}
spine_t spine(const char *file_json, const char *file_atlas, unsigned flags) {
spine_t z = {0}, t = z;
char *atlas = vfs_read(file_atlas);
if(!atlas || !atlas[0]) return z;
// goblins.png
// size: 1024, 128
// filter: Linear, Linear
// pma: true
// dagger
// bounds: 2, 18, 26, 108
// goblin/eyes-closed
// bounds: 2, 4, 34, 12
spine_atlas_t *sa = 0;
const char *last_id = 0;
const char *texture_name = 0;
const char *texture_filter = 0;
const char *texture_format = 0;
const char *texture_repeat = 0;
float texture_width = 0, texture_height = 0, temp;
for each_substring(atlas, "\r\n", it) {
it += strspn(it, " \t\f\v");
/**/ if( strbeg(it, "pma:" ) || strbeg(it, "index:") ) {} // ignored
else if( strbeg(it, "size:" ) ) sscanf(it+5, "%f,%f", &texture_width, &texture_height);
else if( strbeg(it, "rotate:" ) ) { float tmp; tmp=sa->w,sa->w=sa->h,sa->h=tmp; sa->deg = 90; } // assert(val==90)
else if( strbeg(it, "repeat:" ) ) texture_repeat = it+7; // temp string
else if( strbeg(it, "filter:" ) ) texture_filter = it+7; // temp string
else if( strbeg(it, "format:" ) ) texture_format = it+7; // temp string
else if( strbeg(it, "bounds:" ) ) {
sscanf(it+7, "%f,%f,%f,%f", &sa->x, &sa->y, &sa->w, &sa->h);
}
else if( !texture_name ) texture_name = va("%s", it);
else {
array_push(t.atlas, ((spine_atlas_t){0}) );
sa = &t.atlas[array_count(t.atlas) - 1];
sa->name = STRDUP(it);
}
}
for( int i = 0; i < array_count(t.atlas); ++i ) {
sa = &t.atlas[i];
sa->x /= texture_width, sa->y /= texture_height;
sa->w /= texture_width, sa->h /= texture_height;
}
if(!texture_name) return z;
t.texture = texture(texture_name, 0); // @todo: add texture flags here
json_push(vfs_read(file_json)); // @fixme: json_push_from_file() ?
array_resize(t.bones, json_count("/bones"));
array_reserve(t.slots, json_count("/slots"));
array_resize(t.skins, json_count("/skins"));
array_resize(t.anims, json_count("/animations"));
for( int i = 0, end = json_count("/bones"); i < end; ++i ) {
spine_bone_t v = {0};
v.name = STRDUP(json_string("/bones[%d]/name", i));
v.parent = STRDUP(json_string("/bones[%d]/parent", i));
v.x = json_float("/bones[%d]/x", i);
v.y = json_float("/bones[%d]/y", i);
v.z = i;
v.deg = json_float("/bones[%d]/rotation", i);
t.bones[i] = v;
for( int j = i-1; j > 0; --j ) {
if( strcmp(t.bones[j].name,v.parent) ) continue;
t.bones[i].parent_bone = &t.bones[j];
break;
}
}
for( int i = 0, end = json_count("/slots"); i < end; ++i ) {
spine_slot_t v = {0};
v.name = STRDUP(json_string("/slots[%d]/name", i));
v.bone = STRDUP(json_string("/slots[%d]/bone", i));
v.attach = STRDUP(json_string("/slots[%d]/attachment", i));
array_push(t.slots, v);
// slots define draw-order. so, update draw-order/zindex in bone
spine_bone_t *b = find_bone(&t, v.name);
if( b ) b->z = i;
}
for( int i = 0, end = json_count("/skins"); i < end; ++i ) {
spine_skin_t v = {0};
v.name = STRDUP(json_string("/skins[%d]/name", i));
for( int j = 0, jend = json_count("/skins[%d]/attachments",i); j < jend; ++j ) // /skins/default/
for( int k = 0, kend = json_count("/skins[%d]/attachments[%d]",i,j); k < kend; ++k ) { // /skins/default/left hand item/
spine_rect_t r = {0};
r.name = STRDUP(json_key("/skins[%d]/attachments[%d][%d]",i,j,k)); // stringf("%s-%s-%s", json_key("/skins[%d]",i), json_key("/skins[%d][%d]",i,j), json_key("/skins[%d][%d][%d]",i,j,k));
r.x = json_float("/skins[%d]/attachments[%d][%d]/x",i,j,k);
r.y = json_float("/skins[%d]/attachments[%d][%d]/y",i,j,k);
r.sx= json_float("/skins[%d]/attachments[%d][%d]/scaleX",i,j,k); r.sx += !r.sx;
r.sy= json_float("/skins[%d]/attachments[%d][%d]/scaleY",i,j,k); r.sy += !r.sy;
r.w = json_float("/skins[%d]/attachments[%d][%d]/width",i,j,k);
r.h = json_float("/skins[%d]/attachments[%d][%d]/height",i,j,k);
r.deg = json_float("/skins[%d]/attachments[%d][%d]/rotation",i,j,k);
array_push(v.rects, r);
}
t.skins[i] = v;
}
#if 1
// simplify:
// merge /skins/default into existing /skins/*, then delete /skins/default
if( array_count(t.skins) > 1 ) {
for( int i = 1; i < array_count(t.skins); ++i ) {
for( int j = 0; j < array_count(t.skins[0].rects); ++j ) {
array_push(t.skins[i].rects, t.skins[0].rects[j]);
}
}
// @leak @fixme: free(t.skins[0])
for( int i = 0; i < array_count(t.skins)-1; ++i ) {
t.skins[i] = t.skins[i+1];
}
array_pop(t.skins);
}
#endif
for( int i = 0, end = json_count("/animations"); i < end; ++i ) {
int id;
const char *name;
spine_anim_t v = {0};
v.name = STRDUP(json_key("/animations[%d]", i));
// slots / attachments
for( int j = 0, jend = json_count("/animations[%d]/slots",i); j < jend; ++j )
for( int k = 0, kend = json_count("/animations[%d]/slots[%d]",i,j); k < kend; ++k ) // ids
{
int bone_id = find_bone_id(&t, json_key("/animations[%d]/bones[%d]",i,j));
if( bone_id < 0 ) continue;
for( int l = 0, lend = json_count("/animations[%d]/slots[%d][%d]",i,j,k); l < lend; ++l ) { // channels (rot,tra,attach)
spine_animkey_t key = {0};
key.name = STRDUP(json_string("/animations[%d]/slots[%d][%d][%d]/name",i,j,k,l));
key.time = json_float("/animations[%d]/slots[%d][%d][%d]/time",i,j,k,l);
if( json_count("/animations[%d]/slots[%d][%d][%d]/curve",i,j,k,l) == 4 ) {
key.curve[0] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[0]",i,j,k,l);
key.curve[1] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[1]",i,j,k,l);
key.curve[2] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[2]",i,j,k,l);
key.curve[3] = json_float("/animations[%d]/slots[%d][%d][%d]/curve[3]",i,j,k,l);
}
// @todo: convert name to id
// for(id = 0; t.bones[id].name && strcmp(t.bones[id].name,key.name); ++id)
// printf("%s vs %s\n", key.name, t.bones[id].name);
array_push(v.attach_keys[bone_id], key);
}
}
// bones
for( int j = 0, jend = json_count("/animations[%d]/bones",i); j < jend; ++j ) // slots or bones
for( int k = 0, kend = json_count("/animations[%d]/bones[%d]",i,j); k < kend; ++k ) { // bone ids
int bone_id = find_bone_id(&t, json_key("/animations[%d]/bones[%d]",i,j));
if( bone_id < 0 ) continue;
// parse bones
for( int l = 0, lend = json_count("/animations[%d]/bones[%d][%d]",i,j,k); l < lend; ++l ) { // channels (rot,tra,attach)
const char *channel = json_key("/animations[%d]/bones[%d][%d]",i,j,k);
int track = !strcmp(channel, "rotate") ? 1 : !strcmp(channel, "translate") ? 2 : 0;
if( !track ) continue;
spine_animkey_t key = {0};
key.time = json_float("/animations[%d]/bones[%d][%d][%d]/time",i,j,k,l);
if( json_count("/animations[%d]/bones[%d][%d][%d]/curve",i,j,k,l) == 4 ) {
key.curve[0] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[0]",i,j,k,l);
key.curve[1] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[1]",i,j,k,l);
key.curve[2] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[2]",i,j,k,l);
key.curve[3] = json_float("/animations[%d]/bones[%d][%d][%d]/curve[3]",i,j,k,l);
}
if( track == 1 )
key.deg = json_float("/animations[%d]/bones[%d][%d][%d]/value",i,j,k,l), // "/angle"
array_push(v.rotate_keys[bone_id], key);
else
key.x = json_float("/animations[%d]/bones[%d][%d][%d]/x",i,j,k,l),
key.y = json_float("/animations[%d]/bones[%d][%d][%d]/y",i,j,k,l),
array_push(v.translate_keys[bone_id], key);
}
}
t.anims[i] = v;
}
json_pop();
spine_skin(&t, 0);
return t;
}
void spine_render(spine_t *p, vec3 offset, unsigned flags) {
if( !p->texture.id ) return;
if( !flags ) return;
ddraw_push_2d();
if( flags & 2 ) ddraw_line(vec3(0,0,0), vec3(window_width(),window_height(),0));
if( flags & 2 ) ddraw_line(vec3(window_width(),0,0), vec3(0,window_height(),0));
for( int i = 1; i < array_count(p->bones); ++i ) {
spine_bone_t *self = &p->bones[i];
static array(spine_bone_t*) chain = 0; array_resize(chain, 0);
for( spine_bone_t *next = self; next ; next = next->parent_bone ) {
array_push(chain, next);
}
vec3 target = {0}, prev = {0};
for( int j = 1, end = array_count(chain); j < end; ++j ) { // traverse from root(skipped) -> `i` bone direction
int j_opposite = (end - 1) - j;
spine_bone_t *b = chain[j_opposite]; // bone
spine_bone_t *pb = chain[j_opposite+1]; // parent bone
prev = target;
const float deg2rad = C_PI / 180;
b->x2 = b->x3 + pb->x2 + b->x * cos( -pb->deg2 * deg2rad ) - b->y * sin( -pb->deg2 * deg2rad );
b->y2 = -b->y3 + pb->y2 - b->y * cos( pb->deg2 * deg2rad ) + b->x * sin( pb->deg2 * deg2rad );
b->deg2 = -b->deg3 + pb->deg2 - b->deg;
target = vec3(b->x2,b->y2,b->deg2);
}
float deg = target.z, deg_prev = prev.z;
target.z = 0; prev.z = 0;
target = add3(target, offset);
prev = add3(prev, offset);
if( flags & 2 ) {
ddraw_point( target );
ddraw_text( target, -0.25f, self->name );
ddraw_line( target, prev ); // from bone to parent
}
if( flags & 1 ) {
vec4 rect = ptr4(&p->atlas[self->atlas_id].x);
float zindex = self->z;
float offsx = 0; // -(rect.w * p->texture.w); // -p->atlas[self->atlas_id].w - (self->rect_id ? p->skins[p->skin].rects[self->rect_id].w/2 : 0);
float offsy = 0; // /*-(rect.z * p->texture.h)*2*/ -p->atlas[self->atlas_id].h - (self->rect_id ? p->skins[p->skin].rects[self->rect_id].h/2 : 0);
float deg_rect = self->rect_id ? p->skins[p->skin].rects[self->rect_id].deg : 0;
float tilt = p->atlas[self->atlas_id].deg + self->deg2 - deg_rect; // + self->deg2 + deg_rect + p->atlas[self->atlas_id].deg
unsigned tint = ~0u;
sprite_rect(p->texture, rect, zindex, add3(vec3(target.x,target.y,1),vec3(offsx,offsy,0)), tilt, tint);
}
}
ddraw_pop_2d();
ddraw_flush();
}
void spine_animate(spine_t *p, float *time, float *maxtime, float delta) {
if( !p->texture.id ) return;
if( delta > 1/120.f ) delta = 1/120.f;
if( *time >= *maxtime ) *time = 0; else *time += delta;
// reset root // needed?
p->bones[0].x2 = 0;
p->bones[0].y2 = 0;
p->bones[0].deg2 = 0;
p->bones[0].x3 = 0;
p->bones[0].y3 = 0;
p->bones[0].deg3 = 0;
for( int i = 0, end = array_count(p->bones); i < end; ++i) {
// @todo: attach channel
// @todo: per channel: if curve == linear || curve == stepped || array_count(curve) == 4 {...}
for each_array_ptr(p->anims[p->inuse].rotate_keys[i], spine_animkey_t, r) {
double r0 = r->time;
*maxtime = maxf( *maxtime, r0 );
if( absf(*time - r0) < delta ) {
p->bones[i].deg3 = r->deg;
}
}
for each_array_ptr(p->anims[p->inuse].translate_keys[i], spine_animkey_t, r) {
double r0 = r->time;
*maxtime = maxf( *maxtime, r0 );
if( absf(*time - r0) < delta ) {
p->bones[i].x3 = r->x;
p->bones[i].y3 = r->y;
}
}
}
}
void spine_ui(spine_t *p) {
if( ui_collapse(va("Anims: %d", array_count(p->anims)), va("%p-a", p))) {
for each_array_ptr(p->anims, spine_anim_t, q) {
if(ui_slider2("", &p->time, va("%.2f/%.0f %.2f%%", p->time, p->maxtime, p->time * 100.f))) {
spine_animate(p, &p->time, &p->maxtime, 0);
}
int choice = ui_label2_toolbar(q->name, ICON_MD_PAUSE_CIRCLE " " ICON_MD_PLAY_CIRCLE);
if( choice == 1 ) window_pause( 0 ); // play
if( choice == 2 ) window_pause( 1 ); // pause
for( int i = 0; i < _64; ++i ) {
ui_separator();
ui_label(va("Bone %d: Attachment keys", i));
for each_array_ptr(q->attach_keys[i], spine_animkey_t, r) {
ui_label(va("%.2f [%.2f %.2f %.2f %.2f] %s", r->time, r->curve[0], r->curve[1], r->curve[2], r->curve[3], r->name));
}
ui_label(va("Bone %d: Rotate keys", i));
for each_array_ptr(q->rotate_keys[i], spine_animkey_t, r) {
ui_label(va("%.2f [%.2f %.2f %.2f %.2f] %.2f deg", r->time, r->curve[0], r->curve[1], r->curve[2], r->curve[3], r->deg));
}
ui_label(va("Bone %d: Translate keys", i));
for each_array_ptr(q->translate_keys[i], spine_animkey_t, r) {
ui_label(va("%.2f [%.2f %.2f %.2f %.2f] (%.2f,%.2f)", r->time, r->curve[0], r->curve[1], r->curve[2], r->curve[3], r->x, r->y));
}
}
}
ui_collapse_end();
}
if( ui_collapse(va("Bones: %d", array_count(p->bones)), va("%p-b", p))) {
for each_array_ptr(p->bones, spine_bone_t, q)
if( ui_collapse(q->name, va("%p-b2", q)) ) {
ui_label2("Parent:", q->parent);
ui_label2("X:", va("%.2f", q->x));
ui_label2("Y:", va("%.2f", q->y));
ui_label2("Rotation:", va("%.2f", q->deg));
ui_collapse_end();
}
ui_collapse_end();
}
if( ui_collapse(va("Slots: %d", array_count(p->slots)), va("%p-s", p))) {
for each_array_ptr(p->slots, spine_slot_t, q)
if( ui_collapse(q->name, va("%p-s2", q)) ) {
ui_label2("Bone:", q->bone);
ui_label2("Attachment:", q->attach);
ui_collapse_end();
}
ui_collapse_end();
}
if( ui_collapse(va("Skins: %d", array_count(p->skins)), va("%p-k", p))) {
for each_array_ptr(p->skins, spine_skin_t, q)
if( ui_collapse(q->name, va("%p-k2", q)) ) {
for each_array_ptr(q->rects, spine_rect_t, r)
if( ui_collapse(r->name, va("%p-k3", r)) ) {
ui_label2("X:", va("%.2f", r->x));
ui_label2("Y:", va("%.2f", r->y));
ui_label2("Scale X:", va("%.2f", r->sx));
ui_label2("Scale Y:", va("%.2f", r->sy));
ui_label2("Width:", va("%.2f", r->w));
ui_label2("Height:", va("%.2f", r->h));
ui_label2("Rotation:", va("%.2f", r->deg));
ui_collapse_end();
spine_bone_t *b = find_bone(p, r->name);
if( b ) {
static float tilt = 0;
if( input(KEY_LCTRL) ) tilt += 60*1/60.f; else tilt = 0;
spine_atlas_t *r = p->atlas + b->atlas_id;
sprite_flush();
camera_get_active()->position = vec3(0,0,2);
vec4 rect = ptr4(&r->x); float zindex = 0; vec3 xy_zoom = vec3(0,0,0); unsigned tint = ~0u;
sprite_rect(p->texture,
// rect: vec4(r->x*1.0/p->texture.w,r->y*1.0/p->texture.h,(r->x+r->w)*1.0/p->texture.w,(r->y+r->h)*1.0/p->texture.h),
ptr4(&r->x), // atlas
0, vec3(0,0,0), r->deg + tilt, tint);
sprite_flush();
camera_get_active()->position = vec3(+window_width()/3,window_height()/2.25,2);
}
}
ui_collapse_end();
}
ui_collapse_end();
}
if( ui_int("Use skin", &p->skin) ) {
p->skin = clampf(p->skin, 0, array_count(p->skins) - 1);
spine_skin(p, p->skin);
}
if( p->texture.id ) ui_texture(0, p->texture);
}
int main() {
window_create(0.75, 0);
camera_t cam = camera();
cam.position = vec3(0,0,1);
camera_enable(&cam);
spine_t s = spine("goblins.json", "goblins.atlas", 0);
while( window_swap() ) {
camera_get_active()->position.x = window_width()/2;
camera_get_active()->position.y = window_height()/2;
static bool do_skin = 1, do_skel = 1;
spine_animate(&s, &s.time, &s.maxtime, !window_has_pause() * window_delta());
spine_render(&s, vec3(window_width()/2, window_height()/2, 0), do_skin );
sprite_flush();
glClear(GL_DEPTH_BUFFER_BIT);
spine_render(&s, vec3(window_width()/2, window_height()/2, 0), (do_skel*2));
if( ui_panel("Spine", 0) ) {
if(ui_button("Load")) {
s = spine("goblins.json", "goblins.atlas", 0);
}
spine_ui(&s);
if(ui_bool("Draw Skin", &do_skin));
if(ui_bool("Draw Skeleton", &do_skel));
ui_panel_end();
}
}
}

View File

@ -0,0 +1,74 @@
// render sponza
#include "fwk.h"
int main() {
window_create(80, WINDOW_MSAA8);
window_title(__FILE__);
window_fps_unlock();
// load all fx files
fx_load("fx**.fs");
// load skybox
skybox_t sky = skybox(flag("--mie") ? 0 : "hdr/Tokyo_BigSight_1k.hdr", 0); // --mie for rayleigh/mie scattering
// load static scene
model_t sponza;
sponza = model("sponza.obj", 0); // MODEL_NO_TEXTURES);
translation44(sponza.pivot, 0,-1,0);
rotate44(sponza.pivot, -90,1,0,0);
scale44(sponza.pivot, 10,10,10);
// camera
camera_t cam = camera();
double sky_update_until = 0.0;
// demo loop
while (window_swap())
{
// input
if( input_down(KEY_ESC) ) break;
if( input_down(KEY_F5) ) window_reload();
if( input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen() ^ 1 );
if( input_down(KEY_X) ) window_screenshot(__FILE__ ".png");
if( input_down(KEY_Z) ) window_record(__FILE__ ".mp4");
// initialize SH coefficients from rayleigh skybox
if (flag("--mie") && sky_update_until <= window_time()) {
shader_bind(sky.program);
shader_vec3("uSunPos", vec3(0, (cosf((float)window_time()*0.25)*0.3)+0.2, -1));
skybox_mie_calc_sh(&sky, 2.0f);
sky_update_until = window_time() + 0.02;
}
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
// draw skybox
profile("Skybox") {
skybox_render(&sky, cam.proj, cam.view);
}
// apply post-fxs from here
fx_begin();
float scale = 1.00;
mat44 M; copy44(M, sponza.pivot); translate44(M, 0,0,0); scale44(M, scale,scale,scale);
shader_bind(sponza.program);
shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh);
model_render(sponza, cam.proj, cam.view, M, 0);
// post-fxs end here
fx_end(0);
}
}

View File

@ -0,0 +1,90 @@
#include "fwk.h"
int TEMP_GPU = 1;
#define TEX_WIDTH 1024
void temp_calc(vec4 *pixels){
// flood it
for (int y = 1; y < TEX_WIDTH-1; y++){
for (int x = 1; x < TEX_WIDTH-1; x++){
int idx = (y * TEX_WIDTH) + x;
vec4 *m = &pixels[idx];
vec4 sum = vec4(0,0,0,0);
for (int cy = -1; cy <= 1; ++cy) {
for (int cx = -1; cx <= 1; ++cx) {
sum = add4(sum, pixels[((y+cy)*TEX_WIDTH)+(x+cx)]);
}
}
*m = scale4(sum,1/9.0f);
}
}
}
int main() {
window_create(50, WINDOW_SQUARE|WINDOW_VSYNC_DISABLED);
window_title(__FILE__);
window_fps_unlock();
texture_t tex;
vec4 *img = REALLOC(0, TEX_WIDTH*TEX_WIDTH*sizeof(vec4));
for (int i=0; i <TEX_WIDTH*TEX_WIDTH; i++){
img[i] = vec4(0.3,0.3,0.3,1);
}
tex = texture_create(TEX_WIDTH, TEX_WIDTH, 4, img, TEXTURE_LINEAR|TEXTURE_FLOAT);
unsigned comp = compute(vfs_read("shaders/temperature.glsl"));
shader_bind(comp);
shader_image(tex, 0, 0, 0, BUFFER_READ_WRITE);
while ( window_swap() && !input_down(KEY_ESC) ){
if (input_down(KEY_F5)) window_reload();
if (input_down(KEY_F8)) TEMP_GPU ^= 1;
if (TEMP_GPU) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex.id);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, img);
}
for (int i=0; i <TEX_WIDTH*64; i++){
img[i] = vec4(0,0,1,1);
img[(TEX_WIDTH - 64) * TEX_WIDTH+i] = vec4(1,0,0,1);
}
int enabled = !ui_active() && !ui_hover();
vec4 mouse = enabled ? vec4(input(MOUSE_X),input(MOUSE_Y),input(MOUSE_L),input(MOUSE_R)) : vec4(0,0,0,0); // x,y,l,r
int strong = input(KEY_LSHIFT)?5:1;
int x = (int)clampf(floorf(TEX_WIDTH/(float)window_width() * mouse.x), 1, TEX_WIDTH-2);
int y = (int)clampf(floorf(TEX_WIDTH/(float)window_height() * mouse.y), 1, TEX_WIDTH-2);
if (mouse.z || mouse.w){
for (int cy = -5*strong; cy <= 5*strong; ++cy) {
for (int cx = -5*strong; cx <= 5*strong; ++cx) {
int px = (int)clampf(cx+x, 1, TEX_WIDTH-2);
int py = (int)clampf(cy+y, 1, TEX_WIDTH-2);
img[(py*TEX_WIDTH)+px] = vec4(mouse.z,0,mouse.w,1);
}
}
}
if (TEMP_GPU) {
glBindTexture(GL_TEXTURE_2D, tex.id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, TEX_WIDTH, TEX_WIDTH, 0, GL_RGBA, GL_FLOAT, img);
shader_bind(comp);
compute_dispatch(TEX_WIDTH/16, TEX_WIDTH/16, 1);
write_barrier_image();
} else {
temp_calc(img);
texture_update(&tex, TEX_WIDTH, TEX_WIDTH, 4, img, TEXTURE_LINEAR|TEXTURE_FLOAT);
}
fullscreen_quad_rgb(tex, 2.2);
ddraw_text2d(vec2(0,0), va("mode: %s\nimage res: %d\ninputs: %.01f %.01f %.01f %.01f %s", TEMP_GPU?"GPU compute":"CPU", TEX_WIDTH, mouse.x, mouse.y, mouse.z, mouse.w, strong>1?"lshift":""));
ddraw_text2d(vec2(0,window_height() - 20), va("delta: %.2f ms", window_delta()*1000));
}
return 0;
}

View File

@ -0,0 +1,15 @@
#include "fwk.h" // Minimal C sample
__thread int foo=0;
int worker(void *ud) {
printf("thread:%d\n", foo);
return 0;
}
int main() {
foo=1;
printf("main:%d\n", foo);
thread_destroy(thread(worker, NULL));
return 0;
}

View File

@ -0,0 +1,11 @@
#!/bin/bash 2>nul || goto :windows
sh ../MAKE.bat demos
exit
:windows
pushd ..
call MAKE.bat demos %*
popd

BIN
_fwk/demos/lua 100644

Binary file not shown.

View File

@ -0,0 +1,153 @@
#version 330 core
struct Light
{
vec3 direction;
vec3 color;
};
struct ColorMap
{
bool has_tex;
vec4 color;
};
uniform ColorMap map_albedo; uniform sampler2D map_albedo_tex;
uniform ColorMap map_diffuse; uniform sampler2D map_diffuse_tex;
uniform ColorMap map_specular; uniform sampler2D map_specular_tex;
uniform ColorMap map_normals; uniform sampler2D map_normals_tex;
uniform ColorMap map_roughness; uniform sampler2D map_roughness_tex;
uniform ColorMap map_metallic; uniform sampler2D map_metallic_tex;
uniform ColorMap map_ao; uniform sampler2D map_ao_tex;
uniform ColorMap map_ambient; uniform sampler2D map_ambient_tex;
uniform ColorMap map_emissive; uniform sampler2D map_emissive_tex;
#define sample_colormap(ColorMap_, uv_) \
(ColorMap_.has_tex ? texture( ColorMap_##_tex, uv_ ) : ColorMap_.color)
in vec3 out_normal;
in vec3 out_tangent;
in vec3 out_binormal;
in vec2 out_texcoord;
in vec3 out_worldpos;
in vec3 out_to_camera;
uniform float specular_shininess;
uniform float skysphere_rotation;
uniform float skysphere_mip_count;
uniform float exposure;
uniform vec3 camera_position;
uniform Light lights[3];
uniform vec4 global_ambient;
uniform bool has_tex_skysphere;
uniform bool has_tex_skyenv;
uniform sampler2D tex_skysphere;
uniform sampler2D tex_skyenv;
out vec4 frag_color;
const float PI = 3.1415926536;
vec2 sphere_to_polar( vec3 normal )
{
normal = normalize( normal );
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, acos( normal.y ) / PI );
}
vec3 sample_irradiance_fast( vec3 normal )
{
// Sample the irradiance map if it exists, otherwise fall back to blurred reflection map.
if ( has_tex_skyenv )
{
vec2 polar = sphere_to_polar( normal );
// HACK: Sample a smaller mip here to avoid high frequency color variations on detailed normal
// mapped areas.
float miplevel = 5.5; // tweaked for a 360x180 irradiance texture
return textureLod( tex_skyenv, polar, miplevel ).rgb * exposure;
}
else
{
vec2 polar = sphere_to_polar( normal );
return textureLod( tex_skysphere, polar, 0.80 * skysphere_mip_count ).rgb * exposure;
}
}
float calculate_specular( vec3 normal, vec3 light_direction )
{
vec3 V = normalize( out_to_camera );
vec3 L = -normalize( light_direction );
vec3 H = normalize( V + L );
float rdotv = clamp( dot( normal, H ), 0.0, 1.0 );
float total_specular = pow( rdotv, specular_shininess );
return total_specular;
}
void main(void)
{
vec4 specular;
if( map_specular.has_tex ) {
specular = sample_colormap( map_specular, out_texcoord );
} else {
float roughness = 1.0;
float metallic = 0.0;
if( map_metallic.has_tex && map_roughness.has_tex ) {
metallic = sample_colormap( map_metallic, out_texcoord ).x;
roughness = sample_colormap( map_roughness, out_texcoord ).x;
}
else if( map_roughness.has_tex ) {
//< @r-lyeh, metalness B, roughness G, (@todo: self-shadowing occlusion R; for now, any of R/B are metallic)
metallic = sample_colormap( map_roughness, out_texcoord ).b + sample_colormap( map_roughness, out_texcoord ).r;
roughness = sample_colormap( map_roughness, out_texcoord ).g;
}
float gloss = metallic * (1.0 - roughness);
specular = vec4(gloss);
}
vec4 baseColor_alpha;
if ( map_albedo.has_tex )
baseColor_alpha = sample_colormap( map_albedo, out_texcoord );
else
baseColor_alpha = sample_colormap( map_diffuse, out_texcoord );
vec3 diffuse = baseColor_alpha.xyz;
float alpha = baseColor_alpha.w;
vec3 ambient = sample_colormap( map_ambient, out_texcoord ).xyz;
vec3 normals = normalize(texture( map_normals_tex, out_texcoord ).xyz * vec3(2.0) - vec3(1.0));
vec3 normal = out_normal;
if ( map_normals.has_tex )
{
// Mikkelsen's tangent space normal map decoding. See http://mikktspace.com/ for rationale.
vec3 bi = cross( out_normal, out_tangent );
vec3 nmap = normals.xyz;
normal = nmap.x * out_tangent + nmap.y * bi + nmap.z * out_normal;
}
normal = normalize( normal );
vec3 irradiance = sample_irradiance_fast( normal );
ambient *= irradiance;
vec3 color = ambient * global_ambient.rgb;
for ( int i = 0; i < lights.length(); i++ )
{
float ndotl = clamp( dot( normal, -normalize( lights[ i ].direction ) ), 0.0, 1.0 );
vec3 specular = specular.rgb * calculate_specular( normal, lights[ i ].direction ) * specular.a;
color += (diffuse + specular) * ndotl * lights[ i ].color;
}
color += sample_colormap( map_emissive, out_texcoord ).rgb;
frag_color = vec4( pow( color * exposure, vec3(1. / 2.2) ), alpha );
}

View File

@ -0,0 +1,54 @@
#version 330 core
layout(location = 0) in vec3 in_pos;
layout(location = 1) in vec2 in_texcoord;
layout(location = 2) in vec3 in_normal;
layout(location = 3) in vec4 in_tangent; // vec3 + bi sign
//in vec3 in_binormal;
out vec3 out_normal;
out vec3 out_tangent;
out vec3 out_binormal;
out vec2 out_texcoord;
out vec3 out_worldpos;
out vec3 out_viewpos;
out vec3 out_to_camera;
uniform mat4x4 mat_projection;
uniform mat4x4 mat_view;
uniform mat4x4 mat_view_inverse;
uniform mat4x4 mat_world;
void main()
{
vec4 o = vec4( in_pos.x, in_pos.y, in_pos.z, 1.0 );
o = mat_world * o;
out_worldpos = o.xyz;
o = mat_view * o;
out_viewpos = o.xyz;
vec3 to_camera = normalize( -o.xyz );
out_to_camera = mat3( mat_view_inverse ) * to_camera;
o = mat_projection * o;
gl_Position = o;
#if 0
// compute tangent T and bitangent B
vec3 Q1 = dFdx(in_pos);
vec3 Q2 = dFdy(in_pos);
vec2 st1 = dFdx(in_texcoord);
vec2 st2 = dFdy(in_texcoord);
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
vec3 B = normalize(-Q1*st2.s + Q2*st1.s);
vec3 in_binormal = B;
#else
vec3 in_binormal = cross(in_normal, in_tangent.xyz) * in_tangent.w;
#endif
out_normal = normalize( mat3( mat_world ) * in_normal );
out_tangent = normalize( mat3( mat_world ) * in_tangent.xyz );
out_binormal = normalize( mat3( mat_world ) * in_binormal );
out_texcoord = in_texcoord;
}

View File

@ -0,0 +1,112 @@
// uniform mat4 view = mat4(1.0);
uniform vec3 lightPos = vec3(1.0);
uniform float doTexture = 1.;
#ifdef VSMCUBE
uniform samplerCube shadowMap; // VSMCUBE
#else
uniform sampler2D shadowMap; // !VSMCUBE
#endif
struct light {
vec3 position; // world-space
vec4 diffuse;
vec4 specular;
float constantAttenuation, linearAttenuation, quadraticAttenuation;
};
light light0 = light(
lightPos,
vec4(1,1,1,1), // diffuse
vec4(1,1,1,1), // specular
1.0, 0.0, 0.0 // attenuation (const, linear, quad)
);
// From http://fabiensanglard.net/shadowmappingVSM/index.php
#ifdef VSMCUBE
float chebyshevUpperBound(float distance, vec3 dir) {
distance = distance/20 ;
vec2 moments = texture(shadowMap, dir).rg;
#else
float chebyshevUpperBound(float distance, vec4 scPostW) {
vec2 moments = texture(shadowMap,scPostW.xy).rg;
#endif
// Surface is fully lit. as the current fragment is before the light occluder
if (distance <= moments.x)
return 1.0;
// The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check
// How likely this pixel is to be lit (p_max)
float variance = moments.y - (moments.x*moments.x);
//variance = max(variance, 0.000002);
variance = max(variance, 0.00002);
float d = distance - moments.x;
float p_max = variance / (variance + d*d);
return p_max;
}
vec4 shadowmap(in vec4 vpeye, in vec4 vneye, in vec2 uv, in vec4 sc) {
#ifndef VSMCUBE
return vec4(1.);
#else
vec3 fragment = vec3(vpeye);
vec3 normal = vec3(normalize(vneye));
vec3 viewDir = normalize(-fragment);
// Lighting
// Convert to eye-space
vec3 light = vec3(view * vec4(light0.position, 1.0));
#ifdef VSMCUBE
// Vectors
vec3 fragmentToLight = light - fragment;
vec3 fragmentToLightDir = normalize(fragmentToLight);
// Shadows
vec4 fragmentToLight_world = inverse(view) * vec4(fragmentToLightDir, 0.0);
float shadowFactor = chebyshevUpperBound(length(fragmentToLight), -fragmentToLight_world.xyz);
#else
// Shadows
vec4 scPostW = sc / sc.w;
scPostW = scPostW * 0.5 + 0.5;
float shadowFactor = 1.0; // Not in shadow
bool outsideShadowMap = sc.w <= 0.0f || (scPostW.x < 0 || scPostW.y < 0) || (scPostW.x >= 1 || scPostW.y >= 1);
if (!outsideShadowMap) {
shadowFactor = chebyshevUpperBound(scPostW.z, scPostW);
}
#endif
vec4 diffColor = vec4(1,1,1,1);
#ifdef VSMCUBE
if(doTexture != 0) diffColor = vec4(vec3(texture(shadowMap, -fragmentToLight_world.xyz).r), 1.0);
#else
if(doTexture != 0) diffColor = vec4(vec3(texture(shadowMap, vec2(uv.x, 1.0 - uv.y)).r), 1.0);
#endif
#if 1
vec3 positionToLight = light - fragment;
vec3 lightDir = normalize(positionToLight);
// Angle between fragment-normal and incoming light
float cosAngIncidence = dot(lightDir, normal);
cosAngIncidence = clamp(cosAngIncidence, 0, 1);
float attenuation = 1.0f;
attenuation = 1.0 / (light0.constantAttenuation + light0.linearAttenuation * length(positionToLight) + light0.quadraticAttenuation * pow(length(positionToLight),2));
vec4 diffuse = diffColor * light0.diffuse * cosAngIncidence * attenuation;
vec4 total_lighting;
total_lighting += vec4(0.1, 0.1, 0.1, 1.0) * diffColor; // Ambient
total_lighting += diffuse * shadowFactor; // Diffuse
#else
vec4 total_lighting = diffColor;
#endif
return vec4(clamp(vec3(total_lighting), 0., 1.), 1.0);
#endif
}

View File

@ -0,0 +1,9 @@
// uniform mat4 view = mat4(1.0);
uniform vec3 lightPos = vec3(1.0);
uniform float doTexture = 0.;
uniform sampler2D shadowMap;
vec4 shadowmap(in vec4 vpeye, in vec4 vneye, in vec2 Texcoord, in vec4 sc) {
return vec4(1.);
};

View File

@ -0,0 +1,57 @@
uniform sampler2D u_texture;
in vec2 vTexCoord;
in vec4 vColor;
out vec4 fragColor;
// [src] https://www.shadertoy.com/view/MllBWf CC1.0
vec4 texture_AA(sampler2D tx, vec2 uv) {
vec2 res = vec2(textureSize(tx, 0));
uv = uv*res + 0.5;
// tweak fractionnal value of the texture coordinate
vec2 fl = floor(uv);
vec2 fr = fract(uv);
vec2 aa = fwidth(uv)*0.75;
fr = smoothstep( vec2(0.5)-aa, vec2(0.5)+aa, fr);
// return value
uv = (fl+fr-0.5) / res;
return texture(tx, uv);
}
// [src] https://www.shadertoy.com/view/MllBWf CC1.0
vec4 texture_AA2( sampler2D tex, vec2 uv) {
vec2 res = vec2(textureSize(tex,0));
uv = uv*res;
vec2 seam = floor(uv+0.5);
uv = seam + clamp( (uv-seam)/fwidth(uv), -0.5, 0.5);
return texture(tex, uv/res);
}
// [src] https://www.shadertoy.com/view/ltBfRD
vec4 texture_AA3(sampler2D tex, vec2 uv) {
vec2 res = vec2(textureSize(tex,0));
float width = 2.0;
uv = uv * res;
// ---
vec2 uv_floor = floor(uv + 0.5);
vec2 uv_fract = fract(uv + 0.5);
vec2 uv_aa = fwidth(uv) * width * 0.5;
uv_fract = smoothstep(
vec2(0.5) - uv_aa,
vec2(0.5) + uv_aa,
uv_fract
);
uv = (uv_floor + uv_fract - 0.5) / res;
return texture(tex, uv);
}
void main() {
vec4 texColor = texture_AA2(u_texture, vTexCoord);
if(texColor.a < 0.9) discard;
fragColor = vColor * texColor;
}

View File

@ -0,0 +1,16 @@
#define texture2D texture
#define texture2DLod textureLod
#define FRAGCOLOR fragColor
#define texcoord uv
#define TEXCOORD uv
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform float iWidth, iHeight, iTime, iFrame, iMousex, iMousey;
uniform float iChannelRes0x, iChannelRes0y;
uniform float iChannelRes1x, iChannelRes1y;
vec2 iResolution = vec2(iWidth, iHeight);
vec2 iMouse = vec2(iMousex, iMousey);
vec2 iChannelResolution[2] = vec2[2]( vec2(iChannelRes0x, iChannelRes0y),vec2(iChannelRes1x, iChannelRes1y) );
float iGlobalTime = iTime;
in vec2 texcoord;
out vec4 fragColor;

View File

@ -0,0 +1,13 @@
uniform sampler2D texture0; /*unit0*/
uniform float u_inv_gamma;
in vec2 uv;
out vec4 fragcolor;
void main() {
vec4 texel = texture( texture0, uv );
fragcolor = texel;
fragcolor.rgb = pow( fragcolor.rgb, vec3( u_inv_gamma ) ); // defaults: 1.0/2.2 gamma
}

View File

@ -0,0 +1,39 @@
uniform sampler2D u_texture_y; /*unit0*/
uniform sampler2D u_texture_cb; /*unit1*/
uniform sampler2D u_texture_cr; /*unit2*/
uniform float u_gamma;
in vec2 uv;
out vec4 fragcolor;
void main() {
float y = texture(u_texture_y, uv).r;
float cb = texture(u_texture_cb, uv).r;
float cr = texture(u_texture_cr, uv).r;
const mat4 to_rgb = mat4(
1.0000, 1.0000, 1.0000, 0.0000,
0.0000, -0.3441, 1.7720, 0.0000,
1.4020, -0.7141, 0.0000, 0.0000,
-0.7010, 0.5291, -0.8860, 1.0000
);
vec4 texel = to_rgb * vec4(y, cb, cr, 1.0);
/* same as:
vec3 yCbCr = vec3(y,cb-0.5,cr-0.5);
vec4 texel = vec4( dot( vec3( 1.0, 0.0, 1.402 ), yCbCr ),
dot( vec3( 1.0 , -0.34414 , -0.71414 ), yCbCr ),
dot( vec3( 1.0, 1.772, 0.0 ), yCbCr ), 1.0);
*/
// gamma correction
texel.rgb = pow(texel.rgb, vec3(1.0 / u_gamma));
// saturation (algorithm from Chapter 16 of OpenGL Shading Language)
if(false) { float saturation = 2.0; const vec3 W = vec3(0.2125, 0.7154, 0.0721);
vec3 intensity = vec3(dot(texel.rgb, W));
texel.rgb = mix(intensity, texel.rgb, saturation); }
fragcolor = vec4(texel.rgb, 1.0);
}

View File

@ -0,0 +1,165 @@
uniform mat4 model, view;
uniform sampler2D u_texture2d;
uniform vec3 u_coefficients_sh[9];
uniform bool u_textured = true;
uniform bool u_lit = false;
uniform bool u_matcaps = false;
uniform vec4 u_diffuse = vec4(1.0,1.0,1.0,1.0);
in vec3 v_position;
in vec3 v_position_ws;
#ifdef RIM
uniform mat4 M; // RIM
uniform vec3 u_rimcolor = vec3(0.2,0.2,0.2);
uniform vec3 u_rimrange = vec3(0.11,0.98,0.5);
uniform vec3 u_rimpivot = vec3(0,0,0);
uniform bool u_rimambient = true;
#endif
in vec3 v_normal, v_normal_ws;
in vec2 v_texcoord;
in vec4 v_color;
out vec4 fragcolor;
{{include-shadowmap}}
in vec4 vpeye;
in vec4 vneye;
in vec4 sc;
vec4 shadowing() {
return shadowmap(vpeye, vneye, v_texcoord, sc);
}
uniform vec3 u_cam_pos;
uniform vec3 u_cam_dir;
uniform int u_num_lights;
struct light_t {
int type;
vec3 diffuse;
vec3 specular;
vec3 ambient;
vec3 pos;
vec3 dir;
float power;
float innerCone;
float outerCone;
// falloff
float constant;
float linear;
float quadratic;
};
#define MAX_LIGHTS 16
const int LIGHT_DIRECTIONAL = 0;
const int LIGHT_POINT = 1;
const int LIGHT_SPOT = 2;
uniform light_t u_lights[MAX_LIGHTS];
#ifdef SHADING_PHONG
vec3 shading_phong(light_t l) {
vec3 lightDir;
float attenuation = 1.0;
if (l.type == LIGHT_DIRECTIONAL) {
lightDir = normalize(-l.dir);
} else if (l.type == LIGHT_POINT || l.type == LIGHT_SPOT) {
vec3 toLight = l.pos - v_position_ws;
lightDir = normalize(toLight);
float distance = length(toLight);
attenuation = 1.0 / (l.constant + l.linear * distance + l.quadratic * (distance * distance));
if (l.type == LIGHT_SPOT) {
float angle = dot(l.dir, -lightDir);
if (angle > l.outerCone) {
float intensity = (angle-l.outerCone)/(l.innerCone-l.outerCone);
attenuation *= clamp(intensity, 0.0, 1.0);
} else {
attenuation = 0.0;
}
}
}
// fast-rejection for faraway vertices
if (attenuation <= 0.01) {
return vec3(0,0,0);
}
vec3 n = normalize(v_normal_ws);
float diffuse = max(dot(n, lightDir), 0.0);
vec3 halfVec = normalize(lightDir + u_cam_dir);
float specular = pow(max(dot(n, halfVec), 0.0), l.power);
return (attenuation*l.ambient + diffuse*attenuation*l.diffuse + specular*attenuation*l.specular);
}
#endif
vec3 lighting() {
vec3 lit = vec3(0,0,0);
#ifndef SHADING_NONE
for (int i=0; i<u_num_lights; i++) {
#ifdef SHADING_PHONG
lit += shading_phong(u_lights[i]);
#endif
}
#endif
return lit;
}
void main() {
vec3 n = normalize(v_normal_ws);
vec4 lit = vec4(1.0, 1.0, 1.0, 1.0);
// SH lighting
{
vec3 SHLightResult[9];
SHLightResult[0] = 0.282095f * u_coefficients_sh[0];
SHLightResult[1] = -0.488603f * u_coefficients_sh[1] * n.y;
SHLightResult[2] = 0.488603f * u_coefficients_sh[2] * n.z;
SHLightResult[3] = -0.488603f * u_coefficients_sh[3] * n.x;
SHLightResult[4] = 1.092548f * u_coefficients_sh[4] * n.x * n.y;
SHLightResult[5] = -1.092548f * u_coefficients_sh[5] * n.y * n.z;
SHLightResult[6] = 0.315392f * u_coefficients_sh[6] * (3.0f * n.z * n.z - 1.0f);
SHLightResult[7] = -1.092548f * u_coefficients_sh[7] * n.x * n.z;
SHLightResult[8] = 0.546274f * u_coefficients_sh[8] * (n.x * n.x - n.y * n.y);
vec3 result = vec3(0.0);
for (int i = 0; i < 9; ++i)
result += SHLightResult[i];
if( (result.x*result.x+result.y*result.y+result.z*result.z) > 0.0 ) lit = vec4(result, 1.0);
}
// analytical lights (phong shading)
lit += vec4(lighting(), 0.0);
// base
vec4 diffuse;
if(u_matcaps) {
vec2 muv = vec2(view * vec4(v_normal_ws, 0))*0.5+vec2(0.5,0.5); // normal (model space) to view space
diffuse = texture(u_texture2d, vec2(muv.x, 1.0-muv.y));
} else if(u_textured) {
diffuse = texture(u_texture2d, v_texcoord);
} else {
diffuse = u_diffuse; // * v_color;
}
// lighting mix
fragcolor = diffuse * lit * shadowing();
// rimlight
#ifdef RIM
{vec3 n = normalize(mat3(M) * v_normal); // convert normal to view space
vec3 p = (M * vec4(v_position,1.0)).xyz; // convert position to view space
vec3 v = vec3(0,-1,0);
if (!u_rimambient) {
v = normalize(u_rimpivot-p);
}
float rim = 1.0 - max(dot(v,n), 0.0);
vec3 col = u_rimcolor*(pow(smoothstep(1.0-u_rimrange.x,u_rimrange.y,rim), u_rimrange.z));
fragcolor += vec4(col, 1.0);}
#endif
}

View File

@ -0,0 +1,16 @@
uniform sampler2D fsDiffTex;
uniform sampler2D fsNormalTex;
uniform sampler2D fsPositionTex;
uniform mat4 MVP;
in vec3 v_normal;
in vec2 v_texcoord;
out vec4 fragColor;
void main() {
vec4 diff = texture(fsDiffTex, v_texcoord).rgba;
vec3 n = normalize(mat3(MVP) * v_normal); // transform normal to eye space
fragColor = diff;// * vec4(v_normal.xyz, 1);
}

View File

@ -0,0 +1,10 @@
uniform samplerCube u_cubemap;
in vec3 v_direction;
out vec4 fragcolor;
void main() {
fragcolor = vec4(texture(u_cubemap, v_direction).rgb, 1.0);
}

View File

@ -0,0 +1,154 @@
uniform vec3 uSunPos;
uniform vec3 uRayOrigin;
uniform float uSunIntensity;
uniform float uPlanetRadius;
uniform float uAtmosphereRadius;
uniform vec3 uRayleighScattering;
uniform float uMieScattering;
uniform float uRayleighScaleHeight;
uniform float uMieScaleHeight;
uniform float uMiePreferredDirection;
in vec3 v_direction;
out vec4 fragcolor;
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g);
void main() {
vec3 color = atmosphere(
normalize(v_direction), // normalized ray direction
uRayOrigin, // ray origin
uSunPos, // position of the sun
uSunIntensity, // intensity of the sun
uPlanetRadius, // radius of the planet in meters
uAtmosphereRadius, // radius of the atmosphere in meters
uRayleighScattering, // Rayleigh scattering coefficient
uMieScattering, // Mie scattering coefficient
uRayleighScaleHeight, // Rayleigh scale height
uMieScaleHeight, // Mie scale height
uMiePreferredDirection // Mie preferred scattering direction
);
// Apply exposure.
color = 1.0 - exp(-1.0 * color);
fragcolor = vec4(color, 1);
}
// [src] https://github.com/wwwtyro/glsl-atmosphere by wwwtyro (Unlicensed)
// For more information, please refer to <http://unlicense.org>
#define PI 3.141592
#define iSteps 16
#define jSteps 8
vec2 rsi(vec3 r0, vec3 rd, float sr) {
// ray-sphere intersection that assumes
// the sphere is centered at the origin.
// No intersection when result.x > result.y
float a = dot(rd, rd);
float b = 2.0 * dot(rd, r0);
float c = dot(r0, r0) - (sr * sr);
float d = (b*b) - 4.0*a*c;
if (d < 0.0) return vec2(1e5,-1e5);
return vec2(
(-b - sqrt(d))/(2.0*a),
(-b + sqrt(d))/(2.0*a)
);
}
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) {
// Normalize the sun and view directions.
pSun = normalize(pSun);
r = normalize(r);
// Calculate the step size of the primary ray.
vec2 p = rsi(r0, r, rAtmos);
if (p.x > p.y) return vec3(0,0,0);
p.y = min(p.y, rsi(r0, r, rPlanet).x);
float iStepSize = (p.y - p.x) / float(iSteps);
// Initialize the primary ray time.
float iTime = 0.0;
// Initialize accumulators for Rayleigh and Mie scattering.
vec3 totalRlh = vec3(0,0,0);
vec3 totalMie = vec3(0,0,0);
// Initialize optical depth accumulators for the primary ray.
float iOdRlh = 0.0;
float iOdMie = 0.0;
// Calculate the Rayleigh and Mie phases.
float mu = dot(r, pSun);
float mumu = mu * mu;
float gg = g * g;
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg));
// Sample the primary ray.
for (int i = 0; i < iSteps; i++) {
// Calculate the primary ray sample position.
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
// Calculate the height of the sample.
float iHeight = length(iPos) - rPlanet;
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
float odStepMie = exp(-iHeight / shMie) * iStepSize;
// Accumulate optical depth.
iOdRlh += odStepRlh;
iOdMie += odStepMie;
// Calculate the step size of the secondary ray.
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
// Initialize the secondary ray time.
float jTime = 0.0;
// Initialize optical depth accumulators for the secondary ray.
float jOdRlh = 0.0;
float jOdMie = 0.0;
// Sample the secondary ray.
for (int j = 0; j < jSteps; j++) {
// Calculate the secondary ray sample position.
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
// Calculate the height of the sample.
float jHeight = length(jPos) - rPlanet;
// Accumulate the optical depth.
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
jOdMie += exp(-jHeight / shMie) * jStepSize;
// Increment the secondary ray time.
jTime += jStepSize;
}
// Calculate attenuation.
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
// Accumulate scattering.
totalRlh += odStepRlh * attn;
totalMie += odStepMie * attn;
// Increment the primary ray time.
iTime += iStepSize;
}
// Calculate and return the final color.
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
}

View File

@ -0,0 +1,4 @@
void mainImage( out vec4 fragColor, in vec2 fragCoord );
void main() {
mainImage(fragColor, texcoord.xy * iResolution);
}

View File

@ -0,0 +1,17 @@
uniform sampler2D textureSource;
uniform vec2 ScaleU;
in vec2 Texcoord;
out vec4 outColor;
void main() {
vec4 color = vec4(0.0);
color += texture( textureSource, Texcoord.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
color += texture( textureSource, Texcoord.st + vec2( -2.0*ScaleU.x, -2.0*ScaleU.y ) )*0.09375;
color += texture( textureSource, Texcoord.st + vec2( -1.0*ScaleU.x, -1.0*ScaleU.y ) )*0.234375;
color += texture( textureSource, Texcoord.st + vec2( 0.0 , 0.0) )*0.3125;
color += texture( textureSource, Texcoord.st + vec2( 1.0*ScaleU.x, 1.0*ScaleU.y ) )*0.234375;
color += texture( textureSource, Texcoord.st + vec2( 2.0*ScaleU.x, 2.0*ScaleU.y ) )*0.09375;
color += texture( textureSource, Texcoord.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
outColor = vec4(color.xyz, 1.0);
}

View File

@ -0,0 +1,19 @@
in vec4 v_position;
out vec4 outColor;
void main() {
#ifdef VSMCUBE
float depth = length( vec3(v_position) ) / 20;
#else
float depth = v_position.z / v_position.w;
depth = depth * 0.5 + 0.5;
#endif
float moment1 = depth;
float moment2 = depth * depth;
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy);
outColor = vec4( moment1, moment2, 0.0, 0.0);
}

View File

@ -0,0 +1,24 @@
#version 130
#define texture2D texture
uniform float iGlobalTime; // shader playback time (in seconds)
uniform float iGlobalDelta; // ??
uniform float iGlobalFrame; // ??
uniform float iSampleRate; // ??
uniform float iTime; // ??
uniform int iFrame; // ??
uniform float iChannelTime[4]; // channel playback time (in seconds)
uniform vec2 iResolution; // viewport resolution (in pixels)
uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
uniform vec3 iOffset; // ?? (0,0,0)
uniform vec4 iMouse; // mouse pixel coords. xy: hover, zw: LMB click)
uniform vec4 iDate; // (year, month, day, time in seconds)
uniform sampler2D iChannel0; // input channel 0 /*sampler%s*/
uniform sampler2D iChannel1; // input channel 1
uniform sampler2D iChannel2; // input channel 2
uniform sampler2D iChannel3; // input channel 3
in vec2 texCoord;
out vec4 fragColor;
void mainImage( out vec4 fragColor, in vec2 fragCoord );
void main() {
mainImage(fragColor, texCoord.xy);
}

View File

@ -0,0 +1,485 @@
#version 400
// original PBR shader by @seece (Public Domain). link: https://github.com/Gargaj/Foxotron/pull/12
//#define textureQueryLod(t,c) vec2(0.0,0.0) // #version 400 required
uniform vec2 resolution = vec2(640.0,480.0); // debug options below use this (USE_MAP_DEBUGGING, USE_AMBIENT_DEBUGGING)
#define USE_BRUTEFORCE_IRRADIANCE false // Samples irradiance from tex_skysphere when enabled.
#define USE_WRAPAROUND_SPECULAR true // Makes silhouettes more reflective to avoid black pixels.
#define USE_SPECULAR_AO_ATTENUATION true // Dampens IBL specular ambient with AO if enabled.
#define USE_NORMAL_VARIATION_TO_ROUGHNESS true // Increases roughness if normal map has variation and was minified.
#define USE_MAP_DEBUGGING false // Shows all ColorMaps as horizontal bars
#define USE_AMBIENT_DEBUGGING false // Splits the screen in two and shows image-based specular (left), full shading (middle), diffuse shading (right).
#define BOOST_LIGHTING 2.00f // Multiplies analytic light's color with this constant because otherwise they look really pathetic.
#define BOOST_SPECULAR 1.50f
#define BOOST_NOISE 2.50f
struct Light
{
vec3 direction;
vec3 color;
};
struct ColorMap
{
bool has_tex;
vec4 color;
};
uniform ColorMap map_albedo; uniform sampler2D map_albedo_tex;
uniform ColorMap map_diffuse; uniform sampler2D map_diffuse_tex;
uniform ColorMap map_specular; uniform sampler2D map_specular_tex; // not used
uniform ColorMap map_normals; uniform sampler2D map_normals_tex;
uniform ColorMap map_roughness; uniform sampler2D map_roughness_tex;
uniform ColorMap map_metallic; uniform sampler2D map_metallic_tex;
uniform ColorMap map_ao; uniform sampler2D map_ao_tex;
uniform ColorMap map_ambient; uniform sampler2D map_ambient_tex;
uniform ColorMap map_emissive; uniform sampler2D map_emissive_tex;
#define sample_colormap(ColorMap_, uv_) \
(ColorMap_.has_tex ? texture( ColorMap_##_tex, uv_ ) : ColorMap_.color)
in vec3 out_normal;
in vec3 out_tangent;
in vec3 out_binormal;
in vec2 out_texcoord;
in vec3 out_worldpos;
in vec3 out_to_camera;
uniform float skysphere_rotation;
uniform float skysphere_mip_count;
uniform float exposure;
uniform uint frame_count;
uniform float specular_shininess;
uniform vec3 camera_position;
uniform Light lights[3];
uniform sampler2D tex_skysphere;
uniform sampler2D tex_skyenv;
uniform sampler2D tex_brdf_lut;
uniform bool has_tex_skysphere;
uniform bool has_tex_skyenv;
out vec4 frag_color;
const float PI = 3.1415926536;
// MurMurHash 3 finalizer. Implementation is in public domain.
uint hash( uint h )
{
h ^= h >> 16;
h *= 0x85ebca6bU;
h ^= h >> 13;
h *= 0xc2b2ae35U;
h ^= h >> 16;
return h;
}
// Random function using the idea of StackOverflow user "Spatial" https://stackoverflow.com/a/17479300
// Creates random 23 bits and puts them into the fraction bits of an 32-bit float.
float random( uvec3 h )
{
uint m = hash(h.x ^ hash( h.y ) ^ hash( h.z ));
return uintBitsToFloat( ( m & 0x007FFFFFu ) | 0x3f800000u ) - 1.;
}
float random( vec3 v )
{
return random(floatBitsToUint( v ));
}
vec3 fresnel_schlick( vec3 H, vec3 V, vec3 F0 )
{
float cosTheta = clamp( dot( H, V ), 0., 1. );
return F0 + ( vec3( 1.0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
}
// A Fresnel term that dampens rough specular reflections.
// https://seblagarde.wordpress.com/2011/08/17/hello-world/
vec3 fresnel_schlick_roughness( vec3 H, vec3 V, vec3 F0, float roughness )
{
float cosTheta = clamp( dot( H, V ), 0., 1. );
return F0 + ( max( vec3( 1.0 - roughness ), F0 ) - F0 ) * pow( 1. - cosTheta, 5.0 );
}
float distribution_ggx( vec3 N, vec3 H, float roughness )
{
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max( 0., dot( N, H ) );
float factor = NdotH * NdotH * ( a2 - 1. ) + 1.;
return a2 / ( PI * factor * factor );
}
float geometry_schlick_ggx( vec3 N, vec3 V, float k )
{
float NdotV = max( 0., dot( N, V ) );
return NdotV / (NdotV * ( 1. - k ) + k );
}
float geometry_smith( vec3 N, vec3 V, vec3 L, float roughness )
{
#if 1 // original
float r = roughness + 1.;
float k = (r * r) / 8.;
#elif 0 // vries
float a = roughness;
float k = (a * a) / 2.0;
#elif 0 // vries improved?
float a = roughness * roughness;
float k = a / 2.0;
#endif
return geometry_schlick_ggx( N, V, k ) * geometry_schlick_ggx( N, L, k );
}
vec2 sphere_to_polar( vec3 normal )
{
normal = normalize( normal );
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, acos( normal.y ) / PI );
}
// Our vertically GL_CLAMPed textures seem to blend towards black when sampling the half-pixel edge.
// Not sure if it has a border, or this if is a driver bug, but can repro on multiple nvidia cards.
// Knowing the texture height we can limit sampling to the centers of the top and bottom pixel rows.
vec2 sphere_to_polar_clamp_y( vec3 normal, float texture_height )
{
normal = normalize( normal );
return vec2( ( atan( normal.z, normal.x ) + skysphere_rotation ) / PI / 2.0 + 0.5, clamp(acos( normal.y ) / PI, 0.5 / texture_height, 1.0 - 0.5 / texture_height) );
}
vec3 sample_sky( vec3 normal )
{
vec2 polar = sphere_to_polar( normal );
return texture( tex_skysphere, polar ).rgb * exposure;
}
// Takes samples around the hemisphere, converts them to radiances via weighting and
// returns a normalized sum.
vec3 sample_irradiance_slow( vec3 normal, vec3 vertex_tangent )
{
float delta = 0.10;
vec3 up = abs( normal.y ) < 0.999 ? vec3( 0., 1., 0. ) : vec3( 0., 0., 1. );
vec3 tangent_x = normalize( cross( up, normal ) );
vec3 tangent_y = cross( normal, tangent_x );
int numIrradianceSamples = 0;
vec3 irradiance = vec3(0.);
for ( float phi = 0.; phi < 2. * PI ; phi += delta )
{
for ( float theta = 0.; theta < 0.5 * PI; theta += delta )
{
vec3 tangent_space = vec3(
sin( theta ) * cos( phi ),
sin( theta ) * sin( phi ),
cos( theta ) );
vec3 world_space = tangent_space.x * tangent_x + tangent_space.y + tangent_y + tangent_space.z * normal;
vec3 color = sample_sky( world_space );
irradiance += color * cos( theta ) * sin( theta );
numIrradianceSamples++;
}
}
irradiance = PI * irradiance / float( numIrradianceSamples );
return irradiance;
}
vec3 sample_irradiance_fast( vec3 normal, vec3 vertex_tangent )
{
// Sample the irradiance map if it exists, otherwise fall back to blurred reflection map.
if ( has_tex_skyenv )
{
vec2 polar = sphere_to_polar_clamp_y( normal, 180.0 );
return textureLod( tex_skyenv, polar, 0.0 ).rgb * exposure;
}
else
{
vec2 polar = sphere_to_polar( normal );
return textureLod( tex_skysphere, polar, 0.80 * skysphere_mip_count ).rgb * exposure;
}
}
vec3 specular_ibl( vec3 V, vec3 N, float roughness, vec3 fresnel )
{
// What we'd like to do here is take a LOT of skybox samples around the reflection
// vector R according to the BRDF lobe.
//
// Unfortunately it's not possible in real time so we use the following UE4 style approximations:
// 1. Integrate incoming light and BRDF separately ("split sum approximation")
// 2. Assume V = R = N so that we can just blur the skybox and sample that.
// 3. Bake the BRDF integral into a lookup texture so that it can be computed in constant time.
//
// Here we also simplify approximation #2 by using bilinear mipmaps with a magic formula instead
// of properly convolving it with a GGX lobe.
//
// For details, see Brian Karis, "Real Shading in Unreal Engine 4", 2013.
vec3 R = 2. * dot( V, N ) * N - V;
vec2 polar = sphere_to_polar( R );
// Map roughness from range [0, 1] into a mip LOD [0, skysphere_mip_count].
// The magic numbers were chosen empirically.
float mip = 0.9 * skysphere_mip_count * pow(roughness, 0.25 * BOOST_SPECULAR);
vec3 prefiltered = textureLod( tex_skysphere, polar, mip ).rgb * exposure;
float NdotV = dot( N, V );
// dot( N, V ) seems to produce negative values so we can try to stretch it a bit behind the silhouette
// to avoid black pixels.
if (USE_WRAPAROUND_SPECULAR)
{
NdotV = NdotV * 0.9 + 0.1;
}
NdotV = min(0.99, max(0.01, NdotV));
// A precomputed lookup table contains a scale and a bias term for specular intensity (called "fresnel" here).
// See equation (8) in Karis' course notes mentioned above.
vec2 envBRDF = texture( tex_brdf_lut, vec2(NdotV, 1.0-roughness) ).xy; // (NdotV,1-roughtness) for green top-left (NdotV,roughness) for green bottom-left
vec3 specular = prefiltered * (fresnel * envBRDF.x + vec3(envBRDF.y));
return specular;
}
void main(void)
{
vec3 baseColor = vec3( 0.5, 0.5, 0.5 );
float roughness = 1.0;
float metallic = 0.0;
float ao = 1.0;
float alpha = 1.0;
vec4 baseColor_alpha;
if ( map_albedo.has_tex )
baseColor_alpha = sample_colormap( map_albedo, out_texcoord );
else
baseColor_alpha = sample_colormap( map_diffuse, out_texcoord );
baseColor = baseColor_alpha.xyz;
alpha = baseColor_alpha.w;
if( map_metallic.has_tex && map_roughness.has_tex ) {
metallic = sample_colormap( map_metallic, out_texcoord ).x;
roughness = sample_colormap( map_roughness, out_texcoord ).x;
}
else if( map_roughness.has_tex ) {
//< @r-lyeh, metalness B, roughness G, (@todo: self-shadowing occlusion R; for now, any of R/B are metallic)
metallic = sample_colormap( map_roughness, out_texcoord ).b + sample_colormap( map_roughness, out_texcoord ).r;
roughness = sample_colormap( map_roughness, out_texcoord ).g;
}
if ( map_ao.has_tex )
ao = sample_colormap( map_ao, out_texcoord ).x;
else if ( map_ambient.has_tex )
ao = sample_colormap( map_ambient, out_texcoord ).x;
vec3 emissive = sample_colormap( map_emissive, out_texcoord ).rgb;
vec3 normalmap = texture( map_normals_tex, out_texcoord ).xyz * vec3(2.0) - vec3(1.0);
float normalmap_mip = textureQueryLod( map_normals_tex, out_texcoord ).x;
float normalmap_length = length(normalmap);
normalmap /= normalmap_length;
vec3 normal = out_normal;
if ( map_normals.has_tex )
{
// Mikkelsen's tangent space normal map decoding. See http://mikktspace.com/ for rationale.
vec3 bi = cross( out_normal, out_tangent );
vec3 nmap = normalmap.xyz;
normal = nmap.x * out_tangent + nmap.y * bi + nmap.z * out_normal;
}
normal = normalize( normal );
if( USE_MAP_DEBUGGING && !USE_AMBIENT_DEBUGGING )
{
vec3 c = vec3(1., 0., 0.);
float x = gl_FragCoord.x / resolution.x;
float y = gl_FragCoord.y / resolution.y;
if ( y < (7.0/7.0) ) c = vec3(.5) + .5*out_normal;
if ( y < (6.0/7.0) ) c = vec3(.5) + .5*normalmap;
if ( y < (5.0/7.0) ) c = vec3(ao);
if ( y < (4.0/7.0) ) c = vec3(emissive);
if ( y < (3.0/7.0) ) c = vec3(metallic);
if ( y < (2.0/7.0) ) c = vec3(roughness);
if ( y < (1.0/7.0) ) c = baseColor;
frag_color = vec4(c, 1.);
return;
}
if (USE_NORMAL_VARIATION_TO_ROUGHNESS)
{
// Try to reduce specular aliasing by increasing roughness when minified normal maps have high variation.
float variation = 1. - pow( normalmap_length, 8. );
float minification = clamp( normalmap_mip - 2., 0., 1. );
roughness = mix( roughness, 1.0, variation * minification );
}
vec3 N = normal;
vec3 V = normalize( out_to_camera );
vec3 Lo = vec3(0.);
vec3 F0 = vec3(0.04);
F0 = mix( F0, baseColor, metallic );
bool use_ibl = has_tex_skysphere;
// Add contributions from analytic lights only if we don't have a skybox.
{
int num_lights = use_ibl ? 1 : lights.length();
for ( int i = 0; i < num_lights; i++ )
{
vec3 radiance = lights[ i ].color * BOOST_LIGHTING;
vec3 L = -normalize( lights[ i ].direction );
vec3 H = normalize( V + L );
vec3 F = fresnel_schlick( H, V, F0 );
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
// Premultiplied alpha applied to the diffuse component only
kD *= alpha;
float D = distribution_ggx( N, H, roughness );
float G = geometry_smith( N, V, L, roughness );
vec3 num = D * F * G;
float denom = 4. * max( 0., dot( N, V ) ) * max( 0., dot( N, L ) );
vec3 specular = kS * (num / max( 0.001, denom ));
float NdotL = max( 0., dot( N, L ) );
Lo += ( kD * ( baseColor / PI ) + specular ) * radiance * NdotL;
}
}
vec3 ambient = sample_colormap( map_ambient, out_texcoord ).xyz;
vec3 diffuse_ambient;
vec3 specular_ambient;
if ( use_ibl )
{
// Image based lighting.
// Based on https://learnopengl.com/PBR/IBL/Diffuse-irradiance
vec3 irradiance = vec3(0.);
if ( USE_BRUTEFORCE_IRRADIANCE )
{
irradiance = sample_irradiance_slow( normal, out_tangent );
}
else
{
irradiance = sample_irradiance_fast( normal, out_tangent );
}
// Compute the Fresnel term for a perfect mirror reflection with L = R.
// In this case the halfway vector H = N.
//
// We use a modified Fresnel function that dampens specular reflections of very
// rough surfaces to avoid too bright pixels at grazing angles.
vec3 F = fresnel_schlick_roughness( N, V, F0, roughness );
vec3 kS = F;
// Subtract the amount of reflected light (specular) to get the energy left for
// absorbed (diffuse) light.
vec3 kD = vec3(1.) - kS;
// Metallic surfaces have only a specular reflection.
kD *= 1.0 - metallic;
// Premultiplied alpha applied to the diffuse component only
kD *= alpha;
// Modulate the incoming lighting with the diffuse color: some wavelengths get absorbed.
diffuse_ambient = irradiance * baseColor;
// Ambient light also has a specular part.
specular_ambient = specular_ibl( V, normal, roughness, F );
// Ambient occlusion tells us the fraction of sky light that reaches this point.
if (USE_SPECULAR_AO_ATTENUATION)
{
ambient = ao * (kD * diffuse_ambient + specular_ambient);
}
else
{
// We don't attenuate specular_ambient ambient here with AO which might cause flickering in dark cavities.
ambient = ao * (kD * diffuse_ambient) + specular_ambient;
}
}
vec3 color = (ambient + Lo) + emissive;
if ( USE_AMBIENT_DEBUGGING )
{
float y = gl_FragCoord.y / resolution.y;
if( USE_MAP_DEBUGGING && y > 0.5 )
{
if ( (y-0.5) < (7.0/7.0/2.0) ) color = vec3(.5) + .5*out_normal;
if ( (y-0.5) < (6.0/7.0/2.0) ) color = vec3(.5) + .5*normalmap;
if ( (y-0.5) < (5.0/7.0/2.0) ) color = vec3(ao);
if ( (y-0.5) < (4.0/7.0/2.0) ) color = vec3(emissive);
if ( (y-0.5) < (3.0/7.0/2.0) ) color = vec3(metallic);
if ( (y-0.5) < (2.0/7.0/2.0) ) color = vec3(roughness);
if ( (y-0.5) < (1.0/7.0/2.0) ) color = baseColor;
} else {
float x = gl_FragCoord.x / resolution.x;
if ( x < 0.33 )
color = specular_ambient;
else if( x > 0.66 )
color = diffuse_ambient;
}
}
#if 0 // original
// basic tonemap and gamma correction
color = color / ( vec3(1.) + color );
color = pow( color, vec3(1. / 2.2) );
#elif 0
// filmic tonemapper
vec3 linearColor = color;
vec3 x = max(vec3(0.0), linearColor - 0.004);
color = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
// gamma correction
// color = pow( color, vec3(1. / 2.2) );
#elif 1
// aces film (CC0, src: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/)
vec3 x = color;
float a = 2.51f;
float b = 0.03f;
float c = 2.43f;
float d = 0.59f;
float e = 0.14f;
color = clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0);
// gamma correction
color = pow( color, vec3(1. / 2.2) );
#endif
// dither with noise.
// float dither = random( uvec3( floatBitsToUint( gl_FragCoord.xy ), frame_count ) );
// color += BOOST_NOISE * vec3( (-1.0/256.) + (2./256.) * dither );
// Technically this alpha may be too transparent, if there is a lot of reflected light we wouldn't
// see the background, maybe we can approximate it well enough by adding a fresnel term
frag_color = vec4( color, alpha );
}

View File

@ -0,0 +1,56 @@
#version 330 core
layout(location = 0) in vec3 in_pos;
layout(location = 1) in vec2 in_texcoord;
layout(location = 2) in vec3 in_normal;
layout(location = 3) in vec4 in_tangent; // vec3 + bi sign
//in vec3 in_binormal;
out vec3 out_normal;
out vec3 out_tangent;
out vec3 out_binormal;
out vec2 out_texcoord;
out vec3 out_worldpos;
out vec3 out_viewpos;
out vec3 out_to_camera;
uniform mat4x4 mat_projection;
uniform mat4x4 mat_view;
uniform mat4x4 mat_view_inverse;
uniform mat4x4 mat_world;
void main()
{
vec4 o = vec4( in_pos.x, in_pos.y, in_pos.z, 1.0 );
o = mat_world * o;
out_worldpos = o.xyz;
o = mat_view * o;
out_viewpos = o.xyz;
vec3 to_camera = normalize( -o.xyz );
out_to_camera = mat3( mat_view_inverse ) * to_camera;
o = mat_projection * o;
gl_Position = o;
#if 0
// compute tangent T and bitangent B
vec3 Q1 = dFdx(in_pos);
vec3 Q2 = dFdy(in_pos);
vec2 st1 = dFdx(in_texcoord);
vec2 st2 = dFdy(in_texcoord);
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
vec3 B = normalize(-Q1*st2.s + Q2*st1.s);
vec3 in_binormal = B;
#else
vec3 in_binormal = cross(in_normal, in_tangent.xyz) * in_tangent.w;
#endif
out_normal = normalize( mat3( mat_world ) * in_normal );
out_tangent = normalize( mat3( mat_world ) * in_tangent.xyz );
out_binormal = normalize( mat3( mat_world ) * in_binormal );
out_texcoord = in_texcoord;
}

View File

@ -0,0 +1,7 @@
out vec2 texcoord;
void main() {
texcoord = vec2( (gl_VertexID << 1) & 2, gl_VertexID & 2 );
gl_Position = vec4( texCoord * 2.0 - 1.0, 0.0, 1.0 );
}

View File

@ -0,0 +1,9 @@
out vec2 uv;
void main() {
float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);
float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);
gl_Position = vec4(-1.0 + x*2.0, 0.0+(-1.0+y*2.0), 0.0, 1.0); // normal(0+),flipped(0-)
uv = vec2(x, y); // normal(y),flipped(1.0-y)
}

View File

@ -0,0 +1,9 @@
out vec2 uv;
void main() {
float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);
float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);
gl_Position = vec4(-1.0 + x*2.0, 0.0-(-1.0+y*2.0), 0.0, 1.0); // normal(0+),flipped(0-)
uv = vec2(x, y); // normal(y),flipped(1.0-y)
}

View File

@ -0,0 +1,124 @@
#ifndef MAX_BONES
#define MAX_BONES 110
#endif
uniform mat3x4 vsBoneMatrix[MAX_BONES];
uniform bool SKINNED = false;
uniform mat4 M; // RIM
uniform mat4 VP;
#if 0
// Fetch blend channels from all attached blend deformers.
for (size_t di = 0; di < mesh->blend_deformers.count; di++) {
ufbx_blend_deformer *deformer = mesh->blend_deformers.data[di];
for (size_t ci = 0; ci < deformer->channels.count; ci++) {
ufbx_blend_channel *chan = deformer->channels.data[ci];
if (chan->keyframes.count == 0) continue;
if (num_blend_shapes < MAX_BLEND_SHAPES) {
blend_channels[num_blend_shapes] = chan;
vmesh->blend_channel_indices[num_blend_shapes] = (int32_t)chan->typed_id;
num_blend_shapes++;
}
}
}
if (num_blend_shapes > 0) {
vmesh->blend_shape_image = pack_blend_channels_to_image(mesh, blend_channels, num_blend_shapes);
vmesh->num_blend_shapes = num_blend_shapes;
}
ubo.f_num_blend_shapes = (float)mesh->num_blend_shapes;
for (size_t i = 0; i < mesh->num_blend_shapes; i++) {
ubo.blend_weights[i] = view->scene.blend_channels[mesh->blend_channel_indices[i]].weight;
}
sg_image blend_shapes = mesh->num_blend_shapes > 0 ? mesh->blend_shape_image : view->empty_blend_shape_image;
#endif
// for blendshapes
#ifndef MAX_BLENDSHAPES
#define MAX_BLENDSHAPES 16
#endif
uniform vec4 blend_weights[MAX_BLENDSHAPES]; // @todo: implement me
uniform float f_num_blend_shapes; // @todo: implement me
uniform sampler2DArray blend_shapes; // @todo: implement me
in vec3 att_position; // @todo: reorder ass2iqe to emit p3 n3 u2 t3 b3 c4B i4 w4 instead
in vec2 att_texcoord;
in vec3 att_normal;
in vec4 att_tangent; // vec3 + bi sign
in mat4 att_instanced_matrix; // for instanced rendering
in vec4 att_indexes; // @fixme: gles might use ivec4 instead?
in vec4 att_weights; // @todo: downgrade from float to byte
in float att_vertexindex; // for blendshapes
in vec4 att_color;
in vec3 att_bitangent; // @todo: remove? also, ass2iqe might output this
out vec4 v_color;
out vec3 v_position, v_position_ws;
out vec3 v_normal, v_normal_ws;
out vec2 v_texcoord;
// shadow
uniform mat4 model, view;
uniform mat4 cameraToShadowProjector;
out vec4 vneye;
out vec4 vpeye;
out vec4 sc;
void do_shadow() {
vneye = view * model * vec4(att_normal, 0.0f);
vpeye = view * model * vec4(att_position, 1.0);
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f);
}
// blendshapes
vec3 evaluate_blend_shape(int vertex_index) {
ivec2 coord = ivec2(vertex_index & (2048 - 1), vertex_index >> 11);
int num_blend_shapes = int(f_num_blend_shapes);
vec3 offset = vec3(0.0);
for (int i = 0; i < num_blend_shapes; i++) {
vec4 packedw = blend_weights[i >> 2];
float weight = packedw[i & 3];
offset += weight * texelFetch(blend_shapes, ivec3(coord, i), 0).xyz;
}
return offset;
}
void main() {
vec3 objPos;
if(!SKINNED) {
objPos = att_position;
v_normal = att_normal;
} else {
mat3x4 m = vsBoneMatrix[int(att_indexes.x)] * att_weights.x;
m += vsBoneMatrix[int(att_indexes.y)] * att_weights.y;
m += vsBoneMatrix[int(att_indexes.z)] * att_weights.z;
m += vsBoneMatrix[int(att_indexes.w)] * att_weights.w;
objPos = vec4(att_position, 1.0) * m;
// blendshapes
// objPos += evaluate_blend_shape(int(att_vertexindex));
v_normal = vec4(att_normal, 0.0) * m;
//@todo: tangents
}
// vec3 tangent = att_tangent.xyz;
// vec3 bitangent = cross(att_normal, att_tangent.xyz) * att_tangent.w;
v_normal_ws = normalize(vec3(model * vec4(v_normal, 0.))); // normal to world/model space
v_normal = normalize(v_normal);
v_position_ws = (att_instanced_matrix * vec4( objPos, 1.0 )).xyz;
v_position = att_position;
v_texcoord = att_texcoord;
v_color = att_color;
gl_Position = VP * att_instanced_matrix * vec4( objPos, 1.0 );
do_shadow();
}

View File

@ -0,0 +1,15 @@
uniform mat4 u_mvp;
in vec3 att_Position;
in vec2 att_TexCoord;
in vec4 att_Color;
out vec2 vTexCoord;
out vec4 vColor;
void main() {
vColor = att_Color;
vTexCoord = att_TexCoord;
gl_Position = u_mvp * vec4(att_Position, 1.0);
}

View File

@ -0,0 +1,37 @@
uniform mat4 u_mvp;
in vec3 att_position;
in vec3 att_normal;
in vec2 att_texcoord;
in vec4 att_color;
out vec4 v_color;
out vec3 v_position;
out vec3 v_position_ws;
out vec3 v_normal;
out vec3 v_normal_ws;
out vec2 v_texcoord;
// shadow
uniform mat4 model, view, proj;
uniform mat4 cameraToShadowProjector; // !VSMCUBE
out vec4 vneye;
out vec4 vpeye;
out vec4 sc; // !VSMCUBE
void do_shadow() {
vneye = view * model * vec4(att_normal, 0.0f);
vpeye = view * model * vec4(att_position, 1.0);
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f);
}
void main() {
gl_Position = u_mvp * vec4(att_position, 1.0);
v_position = att_position;
v_position_ws = vec3(model * vec4(att_position, 1.));
v_normal = normalize(att_normal);
v_normal_ws = normalize(vec3(model * vec4(att_normal, 0.))); // normal world/model space
v_texcoord = att_texcoord;
v_color = att_color;
do_shadow();
}

View File

@ -0,0 +1,12 @@
uniform mat4 u_mvp;
in vec3 att_position;
out vec3 v_direction;
void main() {
vec4 position = u_mvp * vec4(att_position, 0.0);
gl_Position = position.xyww;
v_direction = att_position;
}

View File

@ -0,0 +1,10 @@
#version 130
uniform vec2 iResolution; // viewport resolution (in pixels)
out vec2 texCoord;
void main() {
texCoord = vec2( (gl_VertexID << 1) & 2, gl_VertexID & 2 );
gl_Position = vec4( texCoord * 2.0 - 1.0, 0.0, 1.0 );
texCoord = texCoord * iResolution;
}

View File

@ -0,0 +1,11 @@
#version 130
uniform vec2 iResolution; // viewport resolution (in pixels)
out vec2 texCoord;
void main() {
texCoord = vec2( (gl_VertexID << 1) & 2, gl_VertexID & 2 );
gl_Position = vec4( texCoord * 2.0 - 1.0, 0.0, 1.0 );
texCoord = texCoord * iResolution;
texCoord.y = iResolution.y - texCoord.y; // flip Y
}

View File

@ -0,0 +1,8 @@
in vec3 position;
in vec2 texcoord;
out vec2 Texcoord;
void main() {
gl_Position = vec4(position, 1.0);
Texcoord = texcoord;
}

View File

@ -0,0 +1,15 @@
uniform mat4 model;
uniform mat4 cameraToShadowView;
uniform mat4 cameraToShadowProjector;
in vec3 position;
out vec4 v_position;
void main() {
gl_Position = cameraToShadowProjector * model * vec4(position, 1.0);
#ifdef VSMCUBE
v_position = cameraToShadowView * model * vec4(position, 1.0);
#else
v_position = gl_Position;
#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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,490 @@
/* A mathematical expression evaluator.
* It uses a recursive descent parser internally.
* Author: Werner Stoop
* This is free and unencumbered software released into the public domain.
* http://unlicense.org/
*/
#include <assert.h>
#include <ctype.h>
#include <math.h> /* remember to compile with -lm */
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
/* Special tokens used by the lexer function lex()
* they've been chosen as non-printable characters
* so that printable characters can be used for other
* purposes
*/
#define TOK_END 0 /* end of text */
#define TOK_INI 1 /* Initial state */
#define TOK_ID 2 /* identifier */
#define TOK_NUM 3 /* number */
/* Types of errors */
// 0 /* "no error" */
#define ERR_MEMORY 1 /* "out of memory" */
#define ERR_LEXER 2 /* "unknown token" */
#define ERR_LONGID 3 /* "identifier too long" */
#define ERR_VALUE 4 /* "value expected" */
#define ERR_BRACKET 5 /* "missing ')'" */
#define ERR_FUNC 6 /* "unknown function" */
#define ERR_ARGS 7 /* "wrong number of arguments" */
#define ERR_CONST 8 /* "unknown constant" */
/* Other definitions */
#define MAX_ID_LEN 11 /* Max length of an identifier */
#define OPERATORS "+-*/%(),^" /* Valid operators */
#define EVAL_PI 3.141592654
#define EVAL_E 2.718281828
#define EVAL_DEG (EVAL_PI/180)
/* Internal structure for the parser/evaluator */
struct eval {
jmp_buf j; /* For error handling */
const char *p; /* Position in the text being parsed */
double *st; /* Stack */
int st_size; /* Stack size */
int sp; /* Stack pointer */
/* The current and next tokens identified by the lexer */
struct {
int type; /* Type of the token */
double n_val; /* Numeric value of the previous lexed token */
char s_val[MAX_ID_LEN]; /* String (identifier) value of the previous lexed token */
} token[2];
int cur_tok; /* Current token, either 0 or 1 (see the comments of lex()) */
};
/* Prototypes */
static double pop(struct eval *ev);
static void push(struct eval *ev, double d);
static int lex(struct eval *ev);
/* Prototypes for the recursive descent parser */
static void expr(struct eval *ev);
static void add_expr(struct eval *ev);
static void mul_expr(struct eval *ev);
static void pow_expr(struct eval *ev);
static void uni_expr(struct eval *ev);
static void bra_expr(struct eval *ev);
static void id_expr(struct eval *ev);
static void num_expr(struct eval *ev);
/*
* Evaluates a mathemeatical expression
*/
double eval(const char *exp/*, int *ep*/) {
int _ep, *ep = &_ep;
struct eval ev;
double ans = 0.0;
assert(ep != NULL);
/* Allocate a stack */
ev.st_size = 10;
ev.st = CALLOC(ev.st_size, sizeof *ev.st);
if(!ev.st)
{
*ep = ERR_MEMORY;
return NAN; //0.0;
}
ev.sp = 0;
/* Manage errors */
*ep = setjmp(ev.j);
if(*ep != 0)
{
FREE(ev.st);
return NAN; //0.0;
}
/* Initialize the lexer */
ev.token[0].type = TOK_INI;
ev.token[0].s_val[0] = '\0';
ev.token[1].type = TOK_INI;
ev.token[1].s_val[0] = '\0';
ev.cur_tok = 0;
/* Initialize the parser */
ev.p = exp;
/* lex once to initialize the lexer */
if(lex(&ev) != TOK_END)
{
expr(&ev);
ans = pop(&ev);
}
FREE(ev.st);
return ans;
}
/*
* Pushes a value onto the stack, increases the stack size if necessary
*/
static void push(struct eval *ev, double d) {
if(ev->sp == ev->st_size) {
/* Resize the stack by 1.5 */
double *old = ev->st;
int new_size = ev->st_size + (ev->st_size >> 1);
ev->st = REALLOC(ev->st, new_size);
if(!ev->st) {
ev->st = old;
longjmp(ev->j, ERR_MEMORY);
}
ev->st_size = new_size;
}
ev->st[ev->sp++] = d;
}
// Pops a value from the top of the stack
static double pop(struct eval *ev) {
assert(ev->sp > 0);
return ev->st[--ev->sp];
}
// stricmp() is common, but not standard, so I provide my own
static int istrcmp(const char *p, const char *q) {
for(; tolower(p[0]) == tolower(q[0]) && p[0]; p++, q++);
return tolower(p[0]) - tolower(q[0]);
}
/*
* Lexical analyzer function
*
* In order to implement LL(1), struct eval has an array of two token structures,
* and its cur_tok member is used to point to the _current_ token, while the other
* element contains the _next_ token. This implements a 2 element ring buffer where
* the lexer always writes to the _next_ token so that the recursive descent parser can
* _peek_ at the next token.
*/
static int lex(struct eval *ev) {
int next_tok;
start:
/* Cycle the tokens */
next_tok = ev->cur_tok;
ev->cur_tok = ev->cur_tok?0:1;
while(isspace(ev->p[0])) ev->p++;
if(!ev->p[0]) {
/* End of the expression */
ev->token[next_tok].type = TOK_END;
goto end;
}
else if(isdigit(ev->p[0]) || ev->p[0] == '.') {
/* Number */
char *endp;
ev->token[next_tok].type = TOK_NUM;
ev->token[next_tok].n_val = strtod(ev->p, &endp);
ev->p = endp;
goto end;
}
else if(isalpha(ev->p[0])) {
/* Identifier */
int i;
for(i = 0; isalnum(ev->p[0]) && i < MAX_ID_LEN - 1; i++, ev->p++)
ev->token[next_tok].s_val[i] = ev->p[0];
if(isalpha(ev->p[0])) longjmp(ev->j, ERR_LONGID);
ev->token[next_tok].s_val[i] = '\0';
ev->token[next_tok].type = TOK_ID;
goto end;
}
else if(strchr(OPERATORS, ev->p[0])) {
/* Operator */
ev->token[next_tok].type = ev->p[0];
ev->p++;
goto end;
}
else /* Unknown token */
longjmp(ev->j, ERR_LEXER);
end:
/* If this was the first call, cycle the tokens again */
if(ev->token[ev->cur_tok].type == TOK_INI)
goto start;
return ev->token[ev->cur_tok].type;
}
#define EVAL_TYPE(e) (e->token[e->cur_tok].type)
#define EVAL_ERROR(c) longjmp(ev->j, (c))
// num_expr ::= NUMBER
static void num_expr(struct eval *ev) {
if(EVAL_TYPE(ev) != TOK_NUM)
EVAL_ERROR(ERR_VALUE);
push(ev, ev->token[ev->cur_tok].n_val);
lex(ev);
}
// expr ::= add_expr
static void expr(struct eval *ev) {
add_expr(ev);
}
// add_expr ::= mul_expr [('+'|'-') mul_expr]
static void add_expr(struct eval *ev) {
int t;
mul_expr(ev);
while((t =EVAL_TYPE(ev)) == '+' || t == '-') {
double a,b;
lex(ev);
mul_expr(ev);
b = pop(ev);
a = pop(ev);
if(t == '+')
push(ev, a + b);
else
push(ev, a - b);
}
}
// mul_expr ::= pow_expr [('*'|'/'|'%') pow_expr]
static void mul_expr(struct eval *ev) {
int t;
pow_expr(ev);
while((t = EVAL_TYPE(ev)) == '*' || t == '/' || t == '%') {
double a,b;
lex(ev);
pow_expr(ev);
b = pop(ev);
a = pop(ev);
if(t == '*')
push(ev, a * b);
else if(t == '/')
push(ev, a / b);
else
push(ev, fmod(a, b));
}
}
// pow_expr ::= uni_expr ['^' pow_expr]
static void pow_expr(struct eval *ev) {
/* Note that exponentiation is right associative:
2^3^4 is 2^(3^4), not (2^3)^4 */
uni_expr(ev);
if(EVAL_TYPE(ev) == '^') {
double a,b;
lex(ev);
pow_expr(ev);
b = pop(ev);
a = pop(ev);
push(ev, pow(a,b));
}
}
// uni_expr ::= ['+'|'-'] bra_expr
static void uni_expr(struct eval *ev) {
int t = '+';
if(EVAL_TYPE(ev) == '-' || EVAL_TYPE(ev) == '+') {
t = EVAL_TYPE(ev);
lex(ev);
}
bra_expr(ev);
if(t == '-') {
double a = pop(ev);
push(ev, -a);
}
}
// bra_expr ::= '(' add_expr ')' | id_expr
static void bra_expr(struct eval *ev) {
if(EVAL_TYPE(ev) == '(') {
lex(ev);
add_expr(ev);
if(EVAL_TYPE(ev) != ')')
EVAL_ERROR(ERR_BRACKET);
lex(ev);
}
else
id_expr(ev);
}
// id_expr ::= ID '(' add_expr [',' add_expr]* ')' | ID | num_expr
static void id_expr(struct eval *ev) {
int nargs = 0;
char id[MAX_ID_LEN];
if(EVAL_TYPE(ev) != TOK_ID) {
num_expr(ev);
} else {
strcpy(id, ev->token[ev->cur_tok].s_val);
lex(ev);
if(EVAL_TYPE(ev) != '(') {
/**/ if(!istrcmp(id, "true")) push(ev, 1.0);
else if(!istrcmp(id, "false")) push(ev, 0.0);
else if(!istrcmp(id, "on")) push(ev, 1.0);
else if(!istrcmp(id, "off")) push(ev, 0.0);
// pi - 3.141592654
else if(!istrcmp(id, "pi"))
push(ev, EVAL_PI);
// e - base of natural logarithms, 2.718281828
else if(!istrcmp(id, "e"))
push(ev, EVAL_E);
// deg - deg2rad, allows to degree conversion `sin(90*deg) = 1`
else if(!istrcmp(id, "deg"))
push(ev, EVAL_DEG);
else
EVAL_ERROR(ERR_CONST);
} else {
lex(ev);
while(EVAL_TYPE(ev) != ')') {
add_expr(ev);
nargs++;
if(EVAL_TYPE(ev) == ')') break;
if(EVAL_TYPE(ev) != ',')
EVAL_ERROR(ERR_BRACKET);
lex(ev);
}
lex(ev);
// abs(x) - absolute value of x
if(!istrcmp(id, "abs")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, fabs(pop(ev)));
}
// ceil(x) - smallest integer greater than x
else if(!istrcmp(id, "ceil")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, ceil(pop(ev)));
}
// floor(x) - largest integer smaller than x
else if(!istrcmp(id, "floor")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, floor(pop(ev)));
}
// sin(x) - sine of x, in radians
else if(!istrcmp(id, "sin")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, sin(pop(ev)));
}
// asin(x) - arcsine of x, in radians
else if(!istrcmp(id, "asin")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, asin(pop(ev)));
}
// cos(x) - cosine of x, in radians
else if(!istrcmp(id, "cos")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, cos(pop(ev)));
}
// acos(x) - arccosine of x, in radians
else if(!istrcmp(id, "acos")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, acos(pop(ev)));
}
// tan(x) - tangent of x, in radians
else if(!istrcmp(id, "tan")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, tan(pop(ev)));
}
// atan(x) - arctangent of x, in radians
else if(!istrcmp(id, "atan")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, atan(pop(ev)));
}
// atan(y,x) - arctangent of y/x, in radians.
else if(!istrcmp(id, "atan2")) {
double a, b;
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
b = pop(ev);
a = pop(ev);
push(ev, atan2(a,b));
}
// sinh(x) - hyperbolic sine of x, in radians
else if(!istrcmp(id, "sinh")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, sinh(pop(ev)));
}
// cosh(x) - hyperbolic cosine of x, in radians
else if(!istrcmp(id, "cosh")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, cosh(pop(ev)));
}
// tanh(x) - hyperbolic tangent of x, in radians
else if(!istrcmp(id, "tanh")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, tanh(pop(ev)));
}
// log(x) - natural logarithm of x
else if(!istrcmp(id, "log")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, log(pop(ev)));
}
// log10(x) - logarithm of x, base-10
else if(!istrcmp(id, "log10")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, log10(pop(ev)));
}
// exp(x) - computes e^x
else if(!istrcmp(id, "exp")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, exp(pop(ev)));
}
// sqrt(x) - square root of x
else if(!istrcmp(id, "sqrt")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, sqrt(pop(ev)));
}
// rad(x) - converts x from degrees to radians
else if(!istrcmp(id, "rad")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, pop(ev)*EVAL_PI/180);
}
// deg(x) - converts x from radians to degrees
else if(!istrcmp(id, "deg")) {
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
push(ev, pop(ev)*180/EVAL_PI);
}
// pow(x,y) - computes x^y
else if(!istrcmp(id, "pow")) {
double a, b;
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
b = pop(ev);
a = pop(ev);
push(ev, pow(a,b));
}
// hypot(x,y) - computes sqrt(x*x + y*y)
else if(!istrcmp(id, "hypot")) {
double a, b;
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
b = pop(ev);
a = pop(ev);
push(ev, sqrt(a*a + b*b));
}
else
EVAL_ERROR(ERR_FUNC);
}
}
}
//
#ifdef EVALDEMO
#include <stdio.h>
int main() {
assert( eval("1+1") == 2 ); // common path
assert( eval("1+") != eval("1+") ); // check that errors return NAN
assert(~puts("Ok") );
}
#endif

View File

@ -0,0 +1,607 @@
// GJK distance algorithm. original code by @vurtun and @randygaul, public domain.
// [src] https://gist.github.com/vurtun/29727217c269a2fbf4c0ed9a1d11cb40
// - rlyeh, public domain.
/*
GilbertJohnsonKeerthi (GJK) 3D distance algorithm
The GilbertJohnsonKeerthi (GJK) distance algorithm is a method of determining
the minimum distance between two convex sets. The algorithm's stability, speed
which operates in near-constant time, and small storage footprint make it
popular for realtime collision detection.
Unlike many other distance algorithms, it has no requirments on geometry data
to be stored in any specific format, but instead relies solely on a support
function to iteratively generate closer simplices to the correct answer using
the Minkowski sum (CSO) of two convex shapes.
GJK algorithms are used incrementally. In this mode, the final simplex from a
previous solution is used as the initial guess in the next iteration. If the
positions in the new frame are close to those in the old frame, the algorithm
will converge in one or two iterations.
*/
#ifndef GJK_H
#define GJK_H
#define GJK_MAX_ITERATIONS 20
typedef struct gjk_support {
int aid, bid;
vec3 a;
vec3 b;
} gjk_support;
typedef struct gjk_vertex {
vec3 a;
vec3 b;
vec3 p;
int aid, bid;
} gjk_vertex;
typedef struct gjk_simplex {
int max_iter, iter;
int hit, cnt;
gjk_vertex v[4];
float bc[4], D;
} gjk_simplex;
typedef struct gjk_result {
int hit;
vec3 p0;
vec3 p1;
float distance_squared;
int iterations;
} gjk_result;
int gjk(gjk_simplex *s, const gjk_support *sup, vec3 *dv);
gjk_result gjk_analyze(const gjk_simplex *s);
gjk_result gjk_quad(float a_radius, float b_radius);
#endif
#ifdef GJK_C
//#pragma once
#include <math.h>
#include <assert.h>
#define GJK_FLT_MAX FLT_MAX // 3.40282347E+38F
#define GJK_EPSILON FLT_EPSILON // 1.19209290E-07F
float gjk_inv_sqrt(float n) {
union {unsigned u; float f;} conv; conv.f = n;
conv.u = 0x5f375A84 - (conv.u >> 1);
conv.f = conv.f * (1.5f - (n * 0.5f * conv.f * conv.f));
return conv.f;
}
int gjk(gjk_simplex *s, const gjk_support *sup, vec3 *dv) {
assert(s);
assert(dv);
assert(sup);
if (!s || !sup || !dv) return 0;
if (s->max_iter > 0 && s->iter >= s->max_iter)
return 0;
/* I.) Initialize */
if (s->cnt == 0) {
s->D = GJK_FLT_MAX;
s->max_iter = !s->max_iter ? GJK_MAX_ITERATIONS: s->max_iter;
}
/* II.) Check for duplications */
for (int i = 0; i < s->cnt; ++i) {
if (sup->aid != s->v[i].aid) continue;
if (sup->bid != s->v[i].bid) continue;
return 0;
}
/* III.) Add vertex into simplex */
gjk_vertex *vert = &s->v[s->cnt];
vert->a = sup->a;
vert->b = sup->b;
vert->p = *dv;
vert->aid = sup->aid;
vert->bid = sup->bid;
s->bc[s->cnt++] = 1.0f;
/* IV.) Find closest simplex point */
switch (s->cnt) {
case 1: break;
case 2: {
/* -------------------- Line ----------------------- */
vec3 a = s->v[0].p;
vec3 b = s->v[1].p;
/* compute barycentric coordinates */
vec3 ab = sub3(a, b);
vec3 ba = sub3(b, a);
float u = dot3(b, ba);
float v = dot3(a, ab);
if (v <= 0.0f) {
/* region A */
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
if (u <= 0.0f) {
/* region B */
s->v[0] = s->v[1];
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
/* region AB */
s->bc[0] = u;
s->bc[1] = v;
s->cnt = 2;
} break;
case 3: {
/* -------------------- Triangle ----------------------- */
vec3 a = s->v[0].p;
vec3 b = s->v[1].p;
vec3 c = s->v[2].p;
vec3 ab = sub3(a, b);
vec3 ba = sub3(b, a);
vec3 bc = sub3(b, c);
vec3 cb = sub3(c, b);
vec3 ca = sub3(c, a);
vec3 ac = sub3(a, c);
/* compute barycentric coordinates */
float u_ab = dot3(b, ba);
float v_ab = dot3(a, ab);
float u_bc = dot3(c, cb);
float v_bc = dot3(b, bc);
float u_ca = dot3(a, ac);
float v_ca = dot3(c, ca);
if (v_ab <= 0.0f && u_ca <= 0.0f) {
/* region A */
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
if (u_ab <= 0.0f && v_bc <= 0.0f) {
/* region B */
s->v[0] = s->v[1];
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
if (u_bc <= 0.0f && v_ca <= 0.0f) {
/* region C */
s->v[0] = s->v[2];
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
/* calculate fractional area */
vec3 n; n = cross3(ba, ca);
vec3 n1; n1 = cross3(b, c);
vec3 n2; n2 = cross3(c, a);
vec3 n3; n3 = cross3(a, b);
float u_abc = dot3(n1, n);
float v_abc = dot3(n2, n);
float w_abc = dot3(n3, n);
if (u_ab > 0.0f && v_ab > 0.0f && w_abc <= 0.0f) {
/* region AB */
s->bc[0] = u_ab;
s->bc[1] = v_ab;
s->cnt = 2;
break;
}
if (u_bc > 0.0f && v_bc > 0.0f && u_abc <= 0.0f) {
/* region BC */
s->v[0] = s->v[1];
s->v[1] = s->v[2];
s->bc[0] = u_bc;
s->bc[1] = v_bc;
s->cnt = 2;
break;
}
if (u_ca > 0.0f && v_ca > 0.0f && v_abc <= 0.0f) {
/* region CA */
s->v[1] = s->v[0];
s->v[0] = s->v[2];
s->bc[0] = u_ca;
s->bc[1] = v_ca;
s->cnt = 2;
break;
}
/* region ABC */
assert(u_abc > 0.0f && v_abc > 0.0f && w_abc > 0.0f);
s->bc[0] = u_abc;
s->bc[1] = v_abc;
s->bc[2] = w_abc;
s->cnt = 3;
} break;
case 4: {
/* -------------------- Tetrahedron ----------------------- */
vec3 a = s->v[0].p;
vec3 b = s->v[1].p;
vec3 c = s->v[2].p;
vec3 d = s->v[3].p;
vec3 ab = sub3(a, b);
vec3 ba = sub3(b, a);
vec3 bc = sub3(b, c);
vec3 cb = sub3(c, b);
vec3 ca = sub3(c, a);
vec3 ac = sub3(a, c);
vec3 db = sub3(d, b);
vec3 bd = sub3(b, d);
vec3 dc = sub3(d, c);
vec3 cd = sub3(c, d);
vec3 da = sub3(d, a);
vec3 ad = sub3(a, d);
/* compute barycentric coordinates */
float u_ab = dot3(b, ba);
float v_ab = dot3(a, ab);
float u_bc = dot3(c, cb);
float v_bc = dot3(b, bc);
float u_ca = dot3(a, ac);
float v_ca = dot3(c, ca);
float u_bd = dot3(d, db);
float v_bd = dot3(b, bd);
float u_dc = dot3(c, cd);
float v_dc = dot3(d, dc);
float u_ad = dot3(d, da);
float v_ad = dot3(a, ad);
/* check verticies for closest point */
if (v_ab <= 0.0f && u_ca <= 0.0f && v_ad <= 0.0f) {
/* region A */
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
if (u_ab <= 0.0f && v_bc <= 0.0f && v_bd <= 0.0f) {
/* region B */
s->v[0] = s->v[1];
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
if (u_bc <= 0.0f && v_ca <= 0.0f && u_dc <= 0.0f) {
/* region C */
s->v[0] = s->v[2];
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
if (u_bd <= 0.0f && v_dc <= 0.0f && u_ad <= 0.0f) {
/* region D */
s->v[0] = s->v[3];
s->bc[0] = 1.0f;
s->cnt = 1;
break;
}
/* calculate fractional area */
vec3 n; n = cross3(da, ba);
vec3 n1; n1 = cross3(d, b);
vec3 n2; n2 = cross3(b, a);
vec3 n3; n3 = cross3(a, d);
float u_adb = dot3(n1, n);
float v_adb = dot3(n2, n);
float w_adb = dot3(n3, n);
n = cross3(ca, da);
n1 = cross3(c, d);
n2 = cross3(d, a);
n3 = cross3(a, c);
float u_acd = dot3(n1, n);
float v_acd = dot3(n2, n);
float w_acd = dot3(n3, n);
n = cross3(bc, dc);
n1 = cross3(b, d);
n2 = cross3(d, c);
n3 = cross3(c, b);
float u_cbd = dot3(n1, n);
float v_cbd = dot3(n2, n);
float w_cbd = dot3(n3, n);
n = cross3(ba, ca);
n1 = cross3(b, c);
n2 = cross3(c, a);
n3 = cross3(a, b);
float u_abc = dot3(n1, n);
float v_abc = dot3(n2, n);
float w_abc = dot3(n3, n);
/* check edges for closest point */
if (w_abc <= 0.0f && v_adb <= 0.0f && u_ab > 0.0f && v_ab > 0.0f) {
/* region AB */
s->bc[0] = u_ab;
s->bc[1] = v_ab;
s->cnt = 2;
break;
}
if (u_abc <= 0.0f && w_cbd <= 0.0f && u_bc > 0.0f && v_bc > 0.0f) {
/* region BC */
s->v[0] = s->v[1];
s->v[1] = s->v[2];
s->bc[0] = u_bc;
s->bc[1] = v_bc;
s->cnt = 2;
break;
}
if (v_abc <= 0.0f && w_acd <= 0.0f && u_ca > 0.0f && v_ca > 0.0f) {
/* region CA */
s->v[1] = s->v[0];
s->v[0] = s->v[2];
s->bc[0] = u_ca;
s->bc[1] = v_ca;
s->cnt = 2;
break;
}
if (v_cbd <= 0.0f && u_acd <= 0.0f && u_dc > 0.0f && v_dc > 0.0f) {
/* region DC */
s->v[0] = s->v[3];
s->v[1] = s->v[2];
s->bc[0] = u_dc;
s->bc[1] = v_dc;
s->cnt = 2;
break;
}
if (v_acd <= 0.0f && w_adb <= 0.0f && u_ad > 0.0f && v_ad > 0.0f) {
/* region AD */
s->v[1] = s->v[3];
s->bc[0] = u_ad;
s->bc[1] = v_ad;
s->cnt = 2;
break;
}
if (u_cbd <= 0.0f && u_adb <= 0.0f && u_bd > 0.0f && v_bd > 0.0f) {
/* region BD */
s->v[0] = s->v[1];
s->v[1] = s->v[3];
s->bc[0] = u_bd;
s->bc[1] = v_bd;
s->cnt = 2;
break;
}
/* calculate fractional volume (volume can be negative!) */
float denom = dot3(cross3(cb, ab), db); // box3(cb, ab, db)
float volume = (denom == 0) ? 1.0f: 1.0f/denom;
float u_abcd = dot3(cross3(c, d), b) * volume; // box3(c, d, b)
float v_abcd = dot3(cross3(c, a), d) * volume; // box3(c, a, d)
float w_abcd = dot3(cross3(d, a), b) * volume; // box3(d, a, b)
float x_abcd = dot3(cross3(b, a), c) * volume; // box3(b, a, c)
/* check faces for closest point */
if (x_abcd <= 0.0f && u_abc > 0.0f && v_abc > 0.0f && w_abc > 0.0f) {
/* region ABC */
s->bc[0] = u_abc;
s->bc[1] = v_abc;
s->bc[2] = w_abc;
s->cnt = 3;
break;
}
if (u_abcd <= 0.0f && u_cbd > 0.0f && v_cbd > 0.0f && w_cbd > 0.0f) {
/* region CBD */
s->v[0] = s->v[2];
s->v[2] = s->v[3];
s->bc[0] = u_cbd;
s->bc[1] = v_cbd;
s->bc[2] = w_cbd;
s->cnt = 3;
break;
}
if (v_abcd <= 0.0f && u_acd > 0.0f && v_acd > 0.0f && w_acd > 0.0f) {
/* region ACD */
s->v[1] = s->v[2];
s->v[2] = s->v[3];
s->bc[0] = u_acd;
s->bc[1] = v_acd;
s->bc[2] = w_acd;
s->cnt = 3;
break;
}
if (w_abcd <= 0.0f && u_adb > 0.0f && v_adb > 0.0f && w_adb > 0.0f) {
/* region ADB */
s->v[2] = s->v[1];
s->v[1] = s->v[3];
s->bc[0] = u_adb;
s->bc[1] = v_adb;
s->bc[2] = w_adb;
s->cnt = 3;
break;
}
/* region ABCD */
// assert(u_abcd > 0.0f && v_abcd > 0.0f && w_abcd > 0.0f && x_abcd > 0.0f); // tcc+linux asserts in here: both u_abcd and v_abcd are negative
s->bc[0] = u_abcd;
s->bc[1] = v_abcd;
s->bc[2] = w_abcd;
s->bc[3] = x_abcd;
s->cnt = 4;
} break;}
/* V.) Check if origin is enclosed by tetrahedron */
if (s->cnt == 4) {
s->hit = 1;
return 0;
}
/* VI.) Ensure closing in on origin to prevent multi-step cycling */
vec3 pnt; float denom = 0;
for (int i = 0; i < s->cnt; ++i)
denom += s->bc[i];
denom = 1.0f / denom;
switch (s->cnt) {
case 1: pnt = s->v[0].p; break;
case 2: {
/* --------- Line -------- */
vec3 a = scale3(s->v[0].p, denom * s->bc[0]);
vec3 b = scale3(s->v[1].p, denom * s->bc[1]);
pnt = add3(a, b);
} break;
case 3: {
/* ------- Triangle ------ */
vec3 a = scale3(s->v[0].p, denom * s->bc[0]);
vec3 b = scale3(s->v[1].p, denom * s->bc[1]);
vec3 c = scale3(s->v[2].p, denom * s->bc[2]);
pnt = add3(a, b);
pnt = add3(pnt, c);
} break;
case 4: {
/* ----- Tetrahedron ----- */
vec3 a = scale3(s->v[0].p, denom * s->bc[0]);
vec3 b = scale3(s->v[1].p, denom * s->bc[1]);
vec3 c = scale3(s->v[2].p, denom * s->bc[2]);
vec3 d = scale3(s->v[3].p, denom * s->bc[3]);
pnt = add3(a, b);
pnt = add3(pnt, c);
pnt = add3(pnt, d);
} break;}
float d2 = dot3(pnt, pnt);
if (d2 >= s->D) return 0;
s->D = d2;
/* VII.) New search direction */
switch (s->cnt) {
default: assert(0); break;
case 1: {
/* --------- Point -------- */
*dv = scale3(s->v[0].p, -1);
} break;
case 2: {
/* ------ Line segment ---- */
vec3 ba = sub3(s->v[1].p, s->v[0].p);
vec3 b0 = scale3(s->v[1].p, -1);
vec3 t; t = cross3(ba, b0);
*dv = cross3(t, ba);
} break;
case 3: {
/* ------- Triangle ------- */
vec3 ab = sub3(s->v[1].p, s->v[0].p);
vec3 ac = sub3(s->v[2].p, s->v[0].p);
vec3 n; n = cross3(ab, ac);
if (dot3(n, s->v[0].p) <= 0.0f)
*dv = n;
else *dv = scale3(n, -1);
}}
if (dot3(*dv,*dv) < GJK_EPSILON * GJK_EPSILON)
return 0;
return 1;
}
gjk_result gjk_analyze(const gjk_simplex *s) {
gjk_result r = {0}, *res = &r;
res->iterations = s->iter;
res->hit = s->hit;
/* calculate normalization denominator */
float denom = 0;
for (int i = 0; i < s->cnt; ++i)
denom += s->bc[i];
denom = 1.0f / denom;
/* compute closest points */
switch (s->cnt) {
default: assert(0); break;
case 1: {
/* Point */
res->p0 = s->v[0].a;
res->p1 = s->v[0].b;
} break;
case 2: {
/* Line */
float as = denom * s->bc[0];
float bs = denom * s->bc[1];
vec3 a = scale3(s->v[0].a, as);
vec3 b = scale3(s->v[1].a, bs);
vec3 c = scale3(s->v[0].b, as);
vec3 d = scale3(s->v[1].b, bs);
res->p0 = add3(a, b);
res->p1 = add3(c, d);
} break;
case 3: {
/* Triangle */
float as = denom * s->bc[0];
float bs = denom * s->bc[1];
float cs = denom * s->bc[2];
vec3 a = scale3(s->v[0].a, as);
vec3 b = scale3(s->v[1].a, bs);
vec3 c = scale3(s->v[2].a, cs);
vec3 d = scale3(s->v[0].b, as);
vec3 e = scale3(s->v[1].b, bs);
vec3 f = scale3(s->v[2].b, cs);
res->p0 = add3(a, b);
res->p0 = add3(res->p0, c);
res->p1 = add3(d, e);
res->p1 = add3(res->p1, f);
} break;
case 4: {
/* Tetrahedron */
vec3 a = scale3(s->v[0].a, denom * s->bc[0]);
vec3 b = scale3(s->v[1].a, denom * s->bc[1]);
vec3 c = scale3(s->v[2].a, denom * s->bc[2]);
vec3 d = scale3(s->v[3].a, denom * s->bc[3]);
res->p0 = add3(a, b);
res->p0 = add3(res->p0, c);
res->p0 = add3(res->p0, d);
res->p1 = res->p0;
} break;}
if (!res->hit) {
/* compute distance */
vec3 d= sub3(res->p1, res->p0);
res->distance_squared = dot3(d, d);
} else res->distance_squared = 0;
return r;
}
gjk_result gjk_quad(float a_radius, float b_radius) {
gjk_result r = {0}, *res = &r;
float radius = a_radius + b_radius;
float radius_squared = radius * radius;
if (res->distance_squared > GJK_EPSILON &&
res->distance_squared > radius_squared) {
res->distance_squared -= radius_squared;
/* calculate normal */
vec3 n = sub3(res->p1, res->p0);
float l2 = dot3(n, n);
if (l2 != 0.0f) {
float il = gjk_inv_sqrt(l2);
n = scale3(n,il);
}
vec3 da = scale3(n, a_radius);
vec3 db = scale3(n, b_radius);
/* calculate new collision points */
res->p0 = add3(res->p0, da);
res->p1 = sub3(res->p1, db);
} else {
vec3 p = add3(res->p0, res->p1);
res->p0 = scale3(p, 0.5f);
res->p1 = res->p0;
res->distance_squared = 0;
res->hit = 1;
}
return r;
}
#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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,407 @@
/* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com
*
* Revision History:
* 1.00 (2014-26-1) Initial release.
*
* Basic usage:
* int hz, channels, outputSize;
* short *output;
* jo_read_mp1(input, inputSize, output, outputSize, hz, channels);
* // Do something with the data here
* free(output);
*
* */
#ifndef JO_INCLUDE_MP1_H
#define JO_INCLUDE_MP1_H
#include <stdbool.h>
extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels);
#endif // JO_INCLUDE_MP1_H
#ifndef JO_MP1_HEADER_FILE_ONLY
#if defined(_MSC_VER) && _MSC_VER >= 0x1400
#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
#endif
#include <stdlib.h>
#include <math.h>
#include <limits.h>
static const double s_jo_multTbl[64] = {
2.000000,1.587401,1.259921,1.000000,0.793701,0.629961,0.500000,0.396850,0.314980,0.250000,0.198425,0.157490,0.125000,0.099213,0.078745,0.062500,
0.049606,0.039373,0.031250,0.024803,0.019686,0.015625,0.012402,0.009843,0.007812,0.006201,0.004922,0.003906,0.003100,0.002461,0.001953,0.001550,
0.001230,0.000977,0.000775,0.000615,0.000488,0.000388,0.000308,0.000244,0.000194,0.000154,0.000122,0.000097,0.000077,0.000061,0.000048,0.000038,
0.000031,0.000024,0.000019,0.000015,0.000012,0.000010,0.000008,0.000006,0.000005,0.000004,0.000003,0.000002,0.000002,0.000002,0.000001,1e-20
};
// l = i - 256;
// s = (i & 0x40) ? 1 : -1;
// windowTbl[(i/16)|((i%16)<<5)] = s * 20 * exp(-(l/112)*-(l/112)) * sin(l * M_PI*2 / 112) / l;
static const double s_jo_windowTbl[512] = {
-0.000000,-0.000443,0.003250,-0.007004,0.031082,-0.078629,0.100311,-0.572037,1.144989,0.572037,0.100311,0.078629,0.031082,0.007004,0.003250,0.000443,
-0.000015,-0.000473,0.003326,-0.007919,0.030518,-0.084183,0.090927,-0.600220,1.144287,0.543823,0.108856,0.073059,0.031479,0.006119,0.003174,0.000397,
-0.000015,-0.000534,0.003387,-0.008865,0.029785,-0.089706,0.080688,-0.628296,1.142212,0.515610,0.116577,0.067520,0.031738,0.005295,0.003082,0.000366,
-0.000015,-0.000580,0.003433,-0.009842,0.028885,-0.095169,0.069595,-0.656219,1.138763,0.487473,0.123474,0.061996,0.031845,0.004486,0.002991,0.000320,
-0.000015,-0.000626,0.003464,-0.010849,0.027802,-0.100540,0.057617,-0.683914,1.133926,0.459473,0.129578,0.056534,0.031815,0.003723,0.002899,0.000290,
-0.000015,-0.000687,0.003479,-0.011887,0.026535,-0.105820,0.044785,-0.711319,1.127747,0.431656,0.134888,0.051132,0.031662,0.003006,0.002792,0.000259,
-0.000015,-0.000748,0.003479,-0.012939,0.025085,-0.110947,0.031082,-0.738373,1.120224,0.404083,0.139450,0.045837,0.031387,0.002335,0.002686,0.000244,
-0.000031,-0.000809,0.003464,-0.014023,0.023422,-0.115921,0.016510,-0.765030,1.111374,0.376801,0.143265,0.040634,0.031006,0.001694,0.002579,0.000214,
-0.000031,-0.000885,0.003418,-0.015121,0.021576,-0.120697,0.001068,-0.791214,1.101212,0.349869,0.146362,0.035553,0.030533,0.001099,0.002457,0.000198,
-0.000031,-0.000961,0.003372,-0.016235,0.019531,-0.125259,-0.015228,-0.816864,1.089783,0.323318,0.148773,0.030609,0.029938,0.000549,0.002350,0.000168,
-0.000031,-0.001038,0.003281,-0.017349,0.017258,-0.129562,-0.032379,-0.841949,1.077118,0.297211,0.150497,0.025818,0.029282,0.000031,0.002243,0.000153,
-0.000046,-0.001114,0.003174,-0.018463,0.014801,-0.133591,-0.050354,-0.866364,1.063217,0.271591,0.151596,0.021179,0.028534,-0.000443,0.002121,0.000137,
-0.000046,-0.001205,0.003052,-0.019577,0.012115,-0.137299,-0.069168,-0.890091,1.048157,0.246506,0.152069,0.016708,0.027725,-0.000870,0.002014,0.000122,
-0.000061,-0.001297,0.002884,-0.020691,0.009232,-0.140671,-0.088776,-0.913055,1.031937,0.221985,0.151962,0.012421,0.026840,-0.001266,0.001907,0.000107,
-0.000061,-0.001389,0.002701,-0.021790,0.006134,-0.143677,-0.109161,-0.935196,1.014618,0.198059,0.151306,0.008316,0.025909,-0.001617,0.001785,0.000107,
-0.000076,-0.001480,0.002487,-0.022858,0.002823,-0.146255,-0.130310,-0.956482,0.996246,0.174789,0.150116,0.004395,0.024933,-0.001938,0.001694,0.000092,
-0.000076,-0.001587,0.002228,-0.023911,-0.000687,-0.148422,-0.152206,-0.976852,0.976852,0.152206,0.148422,0.000687,0.023911,-0.002228,0.001587,0.000076,
-0.000092,-0.001694,0.001938,-0.024933,-0.004395,-0.150116,-0.174789,-0.996246,0.956482,0.130310,0.146255,-0.002823,0.022858,-0.002487,0.001480,0.000076,
-0.000107,-0.001785,0.001617,-0.025909,-0.008316,-0.151306,-0.198059,-1.014618,0.935196,0.109161,0.143677,-0.006134,0.021790,-0.002701,0.001389,0.000061,
-0.000107,-0.001907,0.001266,-0.026840,-0.012421,-0.151962,-0.221985,-1.031937,0.913055,0.088776,0.140671,-0.009232,0.020691,-0.002884,0.001297,0.000061,
-0.000122,-0.002014,0.000870,-0.027725,-0.016708,-0.152069,-0.246506,-1.048157,0.890091,0.069168,0.137299,-0.012115,0.019577,-0.003052,0.001205,0.000046,
-0.000137,-0.002121,0.000443,-0.028534,-0.021179,-0.151596,-0.271591,-1.063217,0.866364,0.050354,0.133591,-0.014801,0.018463,-0.003174,0.001114,0.000046,
-0.000153,-0.002243,-0.000031,-0.029282,-0.025818,-0.150497,-0.297211,-1.077118,0.841949,0.032379,0.129562,-0.017258,0.017349,-0.003281,0.001038,0.000031,
-0.000168,-0.002350,-0.000549,-0.029938,-0.030609,-0.148773,-0.323318,-1.089783,0.816864,0.015228,0.125259,-0.019531,0.016235,-0.003372,0.000961,0.000031,
-0.000198,-0.002457,-0.001099,-0.030533,-0.035553,-0.146362,-0.349869,-1.101212,0.791214,-0.001068,0.120697,-0.021576,0.015121,-0.003418,0.000885,0.000031,
-0.000214,-0.002579,-0.001694,-0.031006,-0.040634,-0.143265,-0.376801,-1.111374,0.765030,-0.016510,0.115921,-0.023422,0.014023,-0.003464,0.000809,0.000031,
-0.000244,-0.002686,-0.002335,-0.031387,-0.045837,-0.139450,-0.404083,-1.120224,0.738373,-0.031082,0.110947,-0.025085,0.012939,-0.003479,0.000748,0.000015,
-0.000259,-0.002792,-0.003006,-0.031662,-0.051132,-0.134888,-0.431656,-1.127747,0.711319,-0.044785,0.105820,-0.026535,0.011887,-0.003479,0.000687,0.000015,
-0.000290,-0.002899,-0.003723,-0.031815,-0.056534,-0.129578,-0.459473,-1.133926,0.683914,-0.057617,0.100540,-0.027802,0.010849,-0.003464,0.000626,0.000015,
-0.000320,-0.002991,-0.004486,-0.031845,-0.061996,-0.123474,-0.487473,-1.138763,0.656219,-0.069595,0.095169,-0.028885,0.009842,-0.003433,0.000580,0.000015,
-0.000366,-0.003082,-0.005295,-0.031738,-0.067520,-0.116577,-0.515610,-1.142212,0.628296,-0.080688,0.089706,-0.029785,0.008865,-0.003387,0.000534,0.000015,
-0.000397,-0.003174,-0.006119,-0.031479,-0.073059,-0.108856,-0.543823,-1.144287,0.600220,-0.090927,0.084183,-0.030518,0.007919,-0.003326,0.000473,0.000015,
};
// filterTbl[i][j] = cos(((M_PI/64*i+M_PI/4)*(2*j+1)));
static const double s_jo_filterTbl[64][32] = {
{0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,
0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107},
{0.671559,-0.803208,-0.514103,0.903989,0.336890,-0.970031,-0.146730,0.998795,-0.049068,-0.989177,0.242980,0.941544,-0.427555,-0.857729,0.595699,0.740951,
-0.740951,-0.595699,0.857729,0.427555,-0.941544,-0.242980,0.989177,0.049068,-0.998795,0.146730,0.970031,-0.336890,-0.903989,0.514103,0.803208,-0.671559},
{0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393,
-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393},
{0.595699,-0.941544,-0.049068,0.970031,-0.514103,-0.671559,0.903989,0.146730,-0.989177,0.427555,0.740951,-0.857729,-0.242980,0.998795,-0.336890,-0.803208,
0.803208,0.336890,-0.998795,0.242980,0.857729,-0.740951,-0.427555,0.989177,-0.146730,-0.903989,0.671559,0.514103,-0.970031,0.049068,0.941544,-0.595699},
{0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,
0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570},
{0.514103,-0.998795,0.427555,0.595699,-0.989177,0.336890,0.671559,-0.970031,0.242980,0.740951,-0.941544,0.146730,0.803208,-0.903989,0.049068,0.857729,
-0.857729,-0.049068,0.903989,-0.803208,-0.146730,0.941544,-0.740951,-0.242980,0.970031,-0.671559,-0.336890,0.989177,-0.595699,-0.427555,0.998795,-0.514103},
{0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397,
-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397},
{0.427555,-0.970031,0.803208,-0.049068,-0.740951,0.989177,-0.514103,-0.336890,0.941544,-0.857729,0.146730,0.671559,-0.998795,0.595699,0.242980,-0.903989,
0.903989,-0.242980,-0.595699,0.998795,-0.671559,-0.146730,0.857729,-0.941544,0.336890,0.514103,-0.989177,0.740951,0.049068,-0.803208,0.970031,-0.427555},
{0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,
0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683},
{0.336890,-0.857729,0.989177,-0.671559,0.049068,0.595699,-0.970031,0.903989,-0.427555,-0.242980,0.803208,-0.998795,0.740951,-0.146730,-0.514103,0.941544,
-0.941544,0.514103,0.146730,-0.740951,0.998795,-0.803208,0.242980,0.427555,-0.903989,0.970031,-0.595699,-0.049068,0.671559,-0.989177,0.857729,-0.336890},
{0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285,
-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285},
{0.242980,-0.671559,0.941544,-0.989177,0.803208,-0.427555,-0.049068,0.514103,-0.857729,0.998795,-0.903989,0.595699,-0.146730,-0.336890,0.740951,-0.970031,
0.970031,-0.740951,0.336890,0.146730,-0.595699,0.903989,-0.998795,0.857729,-0.514103,0.049068,0.427555,-0.803208,0.989177,-0.941544,0.671559,-0.242980},
{0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,
0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090},
{0.146730,-0.427555,0.671559,-0.857729,0.970031,-0.998795,0.941544,-0.803208,0.595699,-0.336890,0.049068,0.242980,-0.514103,0.740951,-0.903989,0.989177,
-0.989177,0.903989,-0.740951,0.514103,-0.242980,-0.049068,0.336890,-0.595699,0.803208,-0.941544,0.998795,-0.970031,0.857729,-0.671559,0.427555,-0.146730},
{0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017,
-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017},
{0.049068,-0.146730,0.242980,-0.336890,0.427555,-0.514103,0.595699,-0.671559,0.740951,-0.803208,0.857729,-0.903989,0.941544,-0.970031,0.989177,-0.998795,
0.998795,-0.989177,0.970031,-0.941544,0.903989,-0.857729,0.803208,-0.740951,0.671559,-0.595699,0.514103,-0.427555,0.336890,-0.242980,0.146730,-0.049068},
{0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,
0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000},
{-0.049068,0.146730,-0.242980,0.336890,-0.427555,0.514103,-0.595699,0.671559,-0.740951,0.803208,-0.857729,0.903989,-0.941544,0.970031,-0.989177,0.998795,
-0.998795,0.989177,-0.970031,0.941544,-0.903989,0.857729,-0.803208,0.740951,-0.671559,0.595699,-0.514103,0.427555,-0.336890,0.242980,-0.146730,0.049068},
{-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017,
0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017},
{-0.146730,0.427555,-0.671559,0.857729,-0.970031,0.998795,-0.941544,0.803208,-0.595699,0.336890,-0.049068,-0.242980,0.514103,-0.740951,0.903989,-0.989177,
0.989177,-0.903989,0.740951,-0.514103,0.242980,0.049068,-0.336890,0.595699,-0.803208,0.941544,-0.998795,0.970031,-0.857729,0.671559,-0.427555,0.146730},
{-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,
-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090},
{-0.242980,0.671559,-0.941544,0.989177,-0.803208,0.427555,0.049068,-0.514103,0.857729,-0.998795,0.903989,-0.595699,0.146730,0.336890,-0.740951,0.970031,
-0.970031,0.740951,-0.336890,-0.146730,0.595699,-0.903989,0.998795,-0.857729,0.514103,-0.049068,-0.427555,0.803208,-0.989177,0.941544,-0.671559,0.242980},
{-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285,
0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285},
{-0.336890,0.857729,-0.989177,0.671559,-0.049068,-0.595699,0.970031,-0.903989,0.427555,0.242980,-0.803208,0.998795,-0.740951,0.146730,0.514103,-0.941544,
0.941544,-0.514103,-0.146730,0.740951,-0.998795,0.803208,-0.242980,-0.427555,0.903989,-0.970031,0.595699,0.049068,-0.671559,0.989177,-0.857729,0.336890},
{-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,
-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683},
{-0.427555,0.970031,-0.803208,0.049068,0.740951,-0.989177,0.514103,0.336890,-0.941544,0.857729,-0.146730,-0.671559,0.998795,-0.595699,-0.242980,0.903989,
-0.903989,0.242980,0.595699,-0.998795,0.671559,0.146730,-0.857729,0.941544,-0.336890,-0.514103,0.989177,-0.740951,-0.049068,0.803208,-0.970031,0.427555},
{-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397,
0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397},
{-0.514103,0.998795,-0.427555,-0.595699,0.989177,-0.336890,-0.671559,0.970031,-0.242980,-0.740951,0.941544,-0.146730,-0.803208,0.903989,-0.049068,-0.857729,
0.857729,0.049068,-0.903989,0.803208,0.146730,-0.941544,0.740951,0.242980,-0.970031,0.671559,0.336890,-0.989177,0.595699,0.427555,-0.998795,0.514103},
{-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,
-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570},
{-0.595699,0.941544,0.049068,-0.970031,0.514103,0.671559,-0.903989,-0.146730,0.989177,-0.427555,-0.740951,0.857729,0.242980,-0.998795,0.336890,0.803208,
-0.803208,-0.336890,0.998795,-0.242980,-0.857729,0.740951,0.427555,-0.989177,0.146730,0.903989,-0.671559,-0.514103,0.970031,-0.049068,-0.941544,0.595699},
{-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393,
0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393},
{-0.671559,0.803208,0.514103,-0.903989,-0.336890,0.970031,0.146730,-0.998795,0.049068,0.989177,-0.242980,-0.941544,0.427555,0.857729,-0.595699,-0.740951,
0.740951,0.595699,-0.857729,-0.427555,0.941544,0.242980,-0.989177,-0.049068,0.998795,-0.146730,-0.970031,0.336890,0.903989,-0.514103,-0.803208,0.671559},
{-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,
-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107},
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951},
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
{-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,
-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000},
{-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068,
0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795},
{-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185,
0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185},
{-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730,
-0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177},
{-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785,
-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785},
{-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980,
0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031},
{-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940,
0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940},
{-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890,
-0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544},
{-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,
-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880},
{-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555,
0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989},
{-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921,
0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921},
{-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103,
-0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729},
{-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470,
-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470},
{-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699,
0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208},
{-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010,
0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010},
{-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559,
-0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951}
};
// up to 32-bits
static unsigned jo_readBits(const unsigned char *data, int *at, int num) {
unsigned r = 0;
// read partial starting bits
int sc = (8 - (*at & 7)) & 7;
sc = sc > num ? num : sc;
if(sc) {
r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<<sc)-1);
num -= sc;
*at += sc;
}
// read full bytes
while(num>=8) {
r <<= 8;
r |= data[*at/8];
*at += 8;
num -= 8;
}
// read partial ending bits
if(num) {
r <<= num;
r |= (data[*at/8] >> (8 - num)) & ((1<<num)-1);
*at += num;
}
return r;
}
bool jo_read_mp1(const void *input, int inputSize, short **output_, int *outputSize_, int *hz_, int *channels_) {
int outputSize = 0, hz, channels;
short *output = 0;
int outputMax = 0;
const unsigned char *data = (const unsigned char *)input;
// Buffers for the lapped transform
double buf[2][2 * 512] = { 0 };
int bufOffset[2] = { 64,64 };
int at = 0; // Where in the stream are we?
while (at < inputSize * 8) {
// Sync markers are byte aligned
at = (at + 7)&-8;
while (at < inputSize * 8 - 32) {
if (data[at / 8] == 0xFF && (data[at / 8 + 1] & 0xF0) == 0xF0) {
break;
}
else {
at += 8;
}
}
if (at >= inputSize * 8 - 32) {
break;
}
unsigned header = jo_readBits(data, &at, 32);
//printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header);
// sync = 0xFFF
// ID = 1
// layer = 3 (layer 1)
if ((header & 0xFFFE0000) != 0xFFFE0000) {
return false;
}
static const int bitrateTbl[16] = { 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1 };
int kbps = bitrateTbl[(header >> 12) & 15];
if (kbps < 0) {
return false;
}
static const int hzTbl[4] = { 44100, 48000, 32000, 0 };
hz = hzTbl[(header >> 10) & 3];
if (!hz) {
return false;
}
// mode 0 = stereo
// mode 1 = joint stereo
// mode 2 = dual channel (no idea what this does TODO)
// mode 3 = mono
int mode = (header >> 6) & 3;
int modeExt = (header >> 4) & 3;
channels = mode == 3 ? 1 : 2;
const int bound = mode == 1 ? (modeExt + 1) * 4 : 32;
bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens
if (errorProtection) {
at += 16; // skip the CRC.
}
// Read bit allocations
int bitAlloc[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
bitAlloc[i][ch] = jo_readBits(data, &at, 4);
}
}
for (int i = bound; i < 32; ++i) {
bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4);
}
// Read scale indexes
int scaleIdx[32][2];
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63;
}
}
// Read & compute output samples
short pcm[12][2][32];
for (int s = 0; s < 12; ++s) {
// Read normalized, quantized band samples
int samples[32][2] = { 0 };
for (int i = 0; i < bound; ++i) {
for (int ch = 0; ch < channels; ++ch) {
if (bitAlloc[i][ch]) {
samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1);
}
}
}
for (int i = bound; i < 32; ++i) {
if (bitAlloc[i][0]) {
samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1);
}
}
// Compute bands: Dequantize & Denormalize
double bandTbl[2][32] = { 0 };
for (int i = 0; i < 32; ++i) {
for (int ch = 0; ch < channels; ++ch) {
int b = bitAlloc[i][ch];
if (b++) {
int samp = samples[i][ch];
double f = ((samp >> (b - 1)) & 1) ? 0 : -1;
f += (samp & ((1 << (b - 1)) - 1)) / (double)(1 << (b - 1));
f = (f + 1.0 / (1 << (b - 1))) * (1 << b) / ((1 << b) - 1.0);
f *= s_jo_multTbl[scaleIdx[i][ch]];
bandTbl[ch][i] = f;
}
}
}
// Convert subbands to PCM
for (int ch = 0; ch < channels; ++ch) {
bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff;
double *bufOffsetPtr = buf[ch] + bufOffset[ch];
const double *f = s_jo_filterTbl[0];
for (int i = 0; i < 64; ++i) {
double sum = 0;
for (int j = 0; j < 32; ++j) {
sum += *f++ * bandTbl[ch][j];
}
bufOffsetPtr[i] = sum;
}
const double *w = s_jo_windowTbl;
for (int i = 0; i < 32; ++i) {
double sum = 0;
for (int j = 0; j < 16; ++j) {
int k = i | (j + (j + 1 & -2)) << 5;
sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff];
}
int ss = (int)(sum * 0x8000);
ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss;
pcm[s][ch][i] = ss;
}
}
}
if (at > inputSize * 8) {
printf("file corruption?\n");
return false;
}
if (outputMax == 0) {
// estimate total number of samples (may be totally wrong, but its better than nothing)
at = (at + 7)&-8;
outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) {
outputMax += 384 * channels * sizeof(*output);
output = (short*)REALLOC(output, outputMax);
}
for (int i = 0; i < 12; ++i) {
for (int j = 0; j < 32; ++j) {
for (int k = 0; k < channels; ++k) {
output[outputSize++] = pcm[i][k][j];
}
}
}
}
*outputSize_ = outputSize;
*hz_ = hz;
*channels_ = channels;
*output_ = output;
return outputSize && hz && channels && output;
}
#endif // JO_MP1_HEADER_FILE_ONLY

View File

@ -0,0 +1,264 @@
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
*
* Latest revisions:
* 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh)
* 1.02 (22-03-2017) Fixed AC encoding bug.
* Fixed color space bug (thx r- lyeh!)
* 1.01 (18-10-2016) warning fixes
* 1.00 (25-09-2016) initial release
*
* Basic usage:
* char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused
* FILE *fp = fopen("foo.mpg", "wb");
* jo_write_mpeg(fp, frame, width, height, 60); // frame 0
* jo_write_mpeg(fp, frame, width, height, 60); // frame 1
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
* ...
* fclose(fp);
*
* Notes:
* Only supports 24, 25, 30, 50, or 60 fps
*
* I don't know if decoders support changing of fps, or dimensions for each frame.
* Movie players *should* support it as the spec allows it, but ...
*
* MPEG-1/2 currently has no active patents as far as I am aware.
*
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
* */
#ifndef JO_INCLUDE_MPEG_H
#define JO_INCLUDE_MPEG_H
#include <stdio.h>
// To get a header file for this, either cut and paste the header,
// or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and
// then include jo_mpeg.c from it.
// Returns false on failure
extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps);
#endif // JO_INCLUDE_MPEG_H
#ifndef JO_MPEG_HEADER_FILE_ONLY
#ifndef JO_MPEG_COMPONENTS
#define JO_MPEG_COMPONENTS 4
#endif
#include <stdio.h>
#include <math.h>
#include <memory.h>
// Huffman tables
static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}};
static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}};
static const unsigned char s_jo_HTAC[32][40][2] = {
{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},},
{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}},
{{10,5},{8,8},{22,11},{40,13},{40,14}},
{{14,6},{72,9},{56,13},{38,14}},
{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}},
{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}},
{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}},
};
static const float s_jo_quantTbl[64] = {
0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f,
0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f,
0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f,
0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f,
0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f,
0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f,
0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f,
0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f,
};
static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
typedef struct {
FILE *fp;
int buf, cnt;
} jo_bits_t;
static void jo_writeBits(jo_bits_t *b, int value, int count) {
b->cnt += count;
b->buf |= value << (24 - b->cnt);
while(b->cnt >= 8) {
unsigned char c = (b->buf >> 16) & 255;
putc(c, b->fp);
b->buf <<= 8;
b->cnt -= 8;
}
}
static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) {
float tmp0 = *d0 + *d7;
float tmp7 = *d0 - *d7;
float tmp1 = *d1 + *d6;
float tmp6 = *d1 - *d6;
float tmp2 = *d2 + *d5;
float tmp5 = *d2 - *d5;
float tmp3 = *d3 + *d4;
float tmp4 = *d3 - *d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
*d0 = tmp10 + tmp11; // phase 3
*d4 = tmp10 - tmp11;
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
*d2 = tmp13 + z1; // phase 5
*d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
float z3 = tmp11 * 0.707106781f; // c4
float z11 = tmp7 + z3; // phase 5
float z13 = tmp7 - z3;
*d5 = z13 + z2; // phase 6
*d3 = z13 - z2;
*d1 = z11 + z4;
*d7 = z11 - z4;
}
static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) {
for(int dataOff=0; dataOff<64; dataOff+=8) {
jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]);
}
for(int dataOff=0; dataOff<8; ++dataOff) {
jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]);
}
int Q[64];
for(int i=0; i<64; ++i) {
float v = A[i]*s_jo_quantTbl[i];
Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
}
DC = Q[0] - DC;
int aDC = DC < 0 ? -DC : DC;
int size = 0;
int tempval = aDC;
while(tempval) {
size++;
tempval >>= 1;
}
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
if(DC < 0) aDC ^= (1 << size) - 1;
jo_writeBits(bits, aDC, size);
int endpos = 63;
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
for(int i = 1; i <= endpos;) {
int run = 0;
while (Q[i]==0 && i<endpos) {
++run;
++i;
}
int AC = Q[i++];
int aAC = AC < 0 ? -AC : AC;
int code = 0, size = 0;
if (run<32 && aAC<=40) {
code = s_jo_HTAC[run][aAC-1][0];
size = s_jo_HTAC[run][aAC-1][1];
if (AC < 0) code += 1;
}
if(!size) {
jo_writeBits(bits, 1, 6);
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 8);
} else if(AC > 127) {
jo_writeBits(bits, 0, 8);
}
code = AC&255;
size = 8;
}
jo_writeBits(bits, code, size);
}
jo_writeBits(bits, 2, 2);
return Q[0];
}
void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) {
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
jo_bits_t bits = {fp};
// Sequence Header
fwrite("\x00\x00\x01\xB3", 4, 1, fp);
// 12 bits for width, height
putc((width>>4)&0xFF, fp);
putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp);
putc(height & 0xFF, fp);
// aspect ratio, framerate
if(fps <= 24) putc(0x12, fp);
else if(fps <= 25) putc(0x13, fp);
else if(fps <= 30) putc(0x15, fp);
else if(fps <= 50) putc(0x16, fp);
else putc(0x18, fp); // 60fps
fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp);
fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header
fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header
fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header
jo_writeBits(&bits, 0x10, 6);
for (int vblock=0; vblock<(height+15)/16; vblock++) {
for (int hblock=0; hblock<(width+15)/16; hblock++) {
jo_writeBits(&bits, 3, 2);
float Y[256], CBx[256], CRx[256];
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
int _4 = JO_MPEG_COMPONENTS;
// const unsigned char *c = bgrx + y*width*_4+x*_4; // original
const unsigned char *c = bgrx + ((height-1)-y)*width*_4+x*_4; // flipped
float b = c[0], g = c[1], r = c[2]; // channel swap
Y[i] = ( 0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
float CB[64], CR[64];
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
}
for (int k1=0; k1<2; ++k1) {
for (int k2=0; k2<2; ++k2) {
float block[64];
for (int i=0; i<64; i+=8) {
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
}
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
}
}
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
}
}
jo_writeBits(&bits, 0, 7);
fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence
}
#endif

View File

@ -0,0 +1,440 @@
// JSON5 + SJSON parser module
//
// License:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
// No warranty is implied, use at your own risk.
//
// Credits:
// r-lyeh (fork)
// Dominik Madarasz (@zaklaus) (original code)
#ifndef JSON5_H
#define JSON5_H
#ifndef JSON5_ASSERT
#define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0)
#endif
#include <stdint.h>
#include <stdio.h>
typedef enum json5_type {
JSON5_UNDEFINED, // 0
JSON5_NULL, // 1
JSON5_BOOL, // 2
JSON5_OBJECT, // 3
JSON5_STRING, // 4
JSON5_ARRAY, // 5
JSON5_INTEGER, // 6
JSON5_REAL, // 7
} json5_type;
typedef struct json5 {
char* name;
#ifdef NDEBUG
unsigned type : 3;
#else
json5_type type;
#endif
unsigned count : 29;
union {
struct json5* array;
struct json5* nodes;
int64_t integer;
double real;
char* string;
int boolean;
};
} json5;
char* json5_parse(json5 *root, char *source, int flags);
void json5_write(FILE *fp, const json5 *root);
void json5_free(json5 *root);
#endif // JSON5_H
// json5 ----------------------------------------------------------------------
#ifdef JSON5_C
//#pragma once
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
char *json5__trim(char *p) {
while (*p) {
/**/ if( isspace(*p) ) ++p;
else if( p[0] == '/' && p[1] == '*' ) { // skip C comment
for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {}
if( *p ) p += 2;
}
else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment
for( p += 2; *p && p[0] != '\n'; ++p) {}
if( *p ) ++p;
}
else break;
}
return p;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code);
char *json5__parse_string(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( *p == '"' || *p == '\'' || *p == '`' ) {
obj->type = JSON5_STRING;
obj->string = p + 1;
char eos_char = *p, *b = obj->string, *e = b;
while (*e) {
/**/ if( *e == '\\' && (e[1] == eos_char) ) ++e;
else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' ';
else if( *e == eos_char ) break;
++e;
}
*e = '\0';
return p = e + 1;
}
//JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_object(json5 *obj, char *p, char **err_code) {
assert(obj && p);
if( 1 /* *p == '{' */ ) { /* <-- for SJSON */
int skip = *p == '{'; /* <-- for SJSON */
obj->type = JSON5_OBJECT;
obj->nodes = 0;
obj->count = 0;
while (*p) {
json5 node = { 0 };
do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' );
if( *p == '}' ) {
++p;
break;
}
// @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) {
else if( isalnum(*p) || *p == '_' || *p == '$' || *p == '.' ) { // also || is_unicode(p)
node.name = p;
do {
++p;
} while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p)
char *e = p;
p = json5__trim(p);
*e = '\0';
}
else { //if( *p == '"' || *p == '\'' || *p == '`' ) {
char *ps = json5__parse_string(&node, p, err_code);
if( !ps ) {
return NULL;
}
p = ps;
node.name = node.string;
p = json5__trim(p);
}
// @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_name";
return NULL;
}
p = json5__trim(p + 1);
p = json5__parse_value(&node, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( node.type != JSON5_UNDEFINED ) {
array_push(obj->nodes, node);
++obj->count;
}
if( *p == '}') { ++p; break; }
}
return p;
}
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
char *json5__parse_value(json5 *obj, char *p, char **err_code) {
assert(obj && p);
p = json5__trim(p);
char *is_string = json5__parse_string(obj, p, err_code);
if( is_string ) {
p = is_string;
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '{' ) {
p = json5__parse_object( obj, p, err_code );
if( *err_code[0] ) {
return NULL;
}
}
else if( *p == '[' ) {
obj->type = JSON5_ARRAY;
obj->array = 0;
obj->count = 0;
while (*p) {
json5 elem = { 0 };
do { p = json5__trim(p + 1); } while( *p == ',' );
if( *p == ']') { ++p; break; }
p = json5__parse_value(&elem, p, err_code);
if( *err_code[0] ) {
return NULL;
}
if( elem.type != JSON5_UNDEFINED ) {
array_push(obj->array, elem);
++obj->count;
}
if (*p == ']') { ++p; break; }
}
}
else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) {
const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity", 0 };
const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 };
for( int i = 0; labels[i]; ++i ) {
if( !strncmp(p, labels[i], lenghts[i] ) ) {
p += lenghts[i];
#ifdef _MSC_VER // somehow, NaN is apparently signed in MSC
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN;
#else
/**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN;
#endif
else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2;
else obj->type = JSON5_NULL;
break;
}
}
if( obj->type == JSON5_UNDEFINED ) {
JSON5_ASSERT; *err_code = "json5_error_invalid_value";
return NULL;
}
}
else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) {
char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0;
while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) {
is_hex |= (*p | 32) == 'x';
is_dbl |= *p == '.';
*buf++ = *p++;
}
obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER;
long long unsigned int llu;
long long int lli;
/**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real );
else if( is_hex ) sscanf( buffer, "%llx", &llu ), obj->integer = llu; // SCNx64 -> inttypes.h
else sscanf( buffer, "%lld", &lli ), obj->integer = lli; // SCNd64 -> inttypes.h
}
else {
return NULL;
}
return p;
}
char *json5_parse(json5 *root, char *p, int flags) {
char *err_code = "";
*root = (json5) {0};
if( p && p[0] ) {
p = json5__trim(p);
if( *p == '[' ) { /* <-- for SJSON */
json5__parse_value(root, p, &err_code);
} else {
json5__parse_object(root, p, &err_code); /* <-- for SJSON */
}
} else {
root->type = JSON5_OBJECT;
}
return err_code[0] ? err_code : 0;
}
void json5_free(json5 *root) {
if( root->type == JSON5_ARRAY && root->array ) {
for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) {
json5_free(root->array + i);
}
array_free(root->array);
}
if( root->type == JSON5_OBJECT && root->nodes ) {
for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) {
json5_free(root->nodes + i);
}
array_free(root->nodes);
}
*root = (json5) {0}; // needed?
}
void json5_write(FILE *fp, const json5 *o) {
static __thread int indent = 0;
int tabs = 1; // 0,1,2,4,8
if( o->name ) {
fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":");
}
/**/ if( o->type == JSON5_NULL ) fprintf(fp, "%s", "null");
else if( o->type == JSON5_BOOL ) fprintf(fp, "%s", o->boolean ? "true" : "false");
else if( o->type == JSON5_INTEGER ) fprintf(fp, "%lld", (long long int)o->integer);
else if( o->type == JSON5_REAL ) {
/**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" );
else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" );
else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness
}
#if 0
else if( o->type == JSON5_STRING ) { // write (escaped) string
char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256];
for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i];
const char *b = o->string, *e = strpbrk(b, chars), *sep = "\"";
while( e ) {
fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] );
e = strpbrk( b = e + 1, chars);
sep = "";
}
fprintf(fp, "%s%s\"", sep, b);
}
#else
else if( o->type == JSON5_STRING ) { // write string
fprintf(fp, "\"%s\"", o->string);
}
#endif
else if( o->type == JSON5_ARRAY ) {
const char *sep = "";
fprintf(fp, "%s", tabs ? "[ " : "[");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ", " : ",";
json5_write(fp, o->array + i);
}
fprintf(fp, "%s", tabs ? " ]" : "]");
}
else if( o->type == JSON5_OBJECT ) {
const char *sep = "";
fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":"");
for( int i = 0, cnt = o->count; i < cnt; ++i ) {
fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ",";
json5_write(fp, o->nodes + i);
}
fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, "");
} else {
char p[16] = {0};
JSON5_ASSERT; /* "json5_error_invalid_value"; */
}
}
#ifdef JSON5_BENCH
#include <time.h>
int main() {
// https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/
char *content = 0;
for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) {
fseek(fp, 0L, SEEK_END);
size_t pos = ftell(fp);
fseek(fp, 0L, SEEK_SET);
content = (char*)malloc( pos + 1 );
fread(content, 1, pos, fp);
content[pos] = 0;
}
if( content ) {
clock_t start = clock();
json5 root = {0};
char *error = json5_parse(&root, content, 0);
clock_t end = clock();
double delta = ( end - start ) / (double)CLOCKS_PER_SEC;
if( !error ) {
printf("Parsing time: %.3fms\n", delta*1000);
printf("Total nodes: %d\n", array_count(root.array));
printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string,
root.array[0].nodes[1].string,
root.array[0].nodes[2].string);
} else {
printf("Error: %s\n", error);
}
json5_free(&root);
free(content);
}
}
#define main main__
#endif
#ifdef JSON5_DEMO
int main() {
char source5[] =
" // comments\n" /* json5 sample */
" unquoted: 'and you can quote me on that',\n"
" singleQuotes: 'I can use \"double quotes\" here',\n"
" lineBreaks : \"Look, Mom! \\\n"
"No \\n's!\",\n"
" hexadecimal: 0x100,\n"
" leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n"
" positiveSign: +1,\n"
" trailingComma: 'in objects', andIn: ['arrays', ],\n"
" \"backwardsCompatible\": \"with JSON\",\n"
""
" ip = \"127.0.0.1\"\n" /* sjson sample */
" port = 8888\n"
""
" /* comment //nested comment*/\n" /* tests */
" // comment /*nested comment*/\n"
" nil: null,"
" \"+lšctžýáíé=:\": true,,,,"
" huge: 2.2239333e5, "
" array: [+1,2,-3,4,5], "
" hello: 'world /*comment in string*/ //again', "
" abc: 42.67, def: false, "
" children : { a: 1, b: 2, },"
" invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ],"
""
" multiline: `this is\n"
"a multiline string\n"
"yeah`"
"}\n";
json5 root = { 0 };
char *error = json5_parse(&root, source5, 0);
if( error ) {
printf("Error: %s\n", error);
} else {
json5_write(stdout, &root);
}
json5_free(&root);
}
#define main main__
#endif
#endif // JSON5_C

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,401 @@
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
// - rlyeh, public domain
//
// changelog:
// - ported to FWK api
// - namespaced symbols
// - diverse win32 fixes
// - adaptive cols/rows
// - removed nk_begin()/nk_end() pairs
// - dangling nk_group_begin/end() pairs
// - simplified file<->media_group concept
// - minor cosmetics
#ifdef _WIN32
#include <direct.h> // _getcwd()
#else
#include <unistd.h> // getcwd()
#include <pwd.h> // getpwuid()
#endif
#if 1
#define BROWSER_PRINTF(...) do {} while(0)
#else
#define BROWSER_PRINTF printf
#endif
enum browser_groups {
BROWSER_FOLDER,
BROWSER_HOME,
BROWSER_DESKTOP,
BROWSER_COMPUTER,
BROWSER_PROJECT,
BROWSER_MAXFOLDERS,
BROWSER_MAXTYPES = 64,
};
struct browser_media_group {
unsigned icon;
const char *extensions;
};
struct browser_media {
int font;
int icon_sheet;
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
struct nk_image custom_files[BROWSER_MAXTYPES];
struct browser_media_group group[BROWSER_MAXTYPES];
} media = {0};
void browser_config_dir(struct nk_image icon, unsigned counter) {
if( counter < BROWSER_MAXFOLDERS ) {
media.custom_folders[ counter ] = icon;
}
}
void browser_config_type(struct nk_image icon, const char *extensions) {
static int counter = 0;
if( counter < BROWSER_MAXTYPES ) {
media.custom_files[ counter ] = icon;
media.group[ counter ].icon = counter;
media.group[ counter ].extensions = extensions;
++counter;
}
}
#define BROWSER_MAX_PATH 512
struct browser {
/* path */
char file[BROWSER_MAX_PATH]; // selection
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
char home[BROWSER_MAX_PATH];
char desktop[BROWSER_MAX_PATH];
char computer[BROWSER_MAX_PATH];
char project[BROWSER_MAX_PATH]; // cwd when first invoked
/* directory content */
array(char*) files;
array(char*) directories;
size_t file_count;
size_t dir_count;
/* view mode */
bool listing;
float zooming;
};
static struct nk_image* media_icon_for_file(const char *file) {
/* extract extension .xxx from file */
char *ext = strrchr(file, '.');
if( ext && strlen(ext) < 16 ) {
char ext_dot[16+1];
snprintf(ext_dot, 16, "%s.", ext);
/* check for all file definition of all groups for fitting extension. skip first group (default) */
for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) {
if( strstri(media.group[i].extensions, ext_dot) ) {
return &media.custom_files[ media.group[i].icon ];
}
}
}
// return first (default) group
return &media.custom_files[0];
}
static void browser_reload_directory_content(struct browser *browser, const char *path) {
if(path[0] == '\0') path = va("./");
if(!strend(path, "/")) path = va("%s/", path);
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_resize(browser->files, 0);
array_resize(browser->directories, 0);
BROWSER_PRINTF("searching at %s\n", path);
array(char*) list = file_list(path);
for( int i = 0, end = array_count(list); i < end; ++i ) {
char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
if( file_directory(absolute) ) {
// remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings.
char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0';
dir = file_name(dir); // /home/rlyeh/prj/fwk/art -> art
BROWSER_PRINTF("%s\n", dir);
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->directories, STRDUP(dir));
} else {
const char *fname = file_name(absolute);
BROWSER_PRINTF("%s\n", fname);
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->files, STRDUP(fname));
}
}
browser->file_count = array_count(browser->files);
browser->dir_count = array_count(browser->directories);
}
static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) {
if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH);
browser_reload_directory_content(browser, path);
}
static void browser_init(struct browser *browser) {
memset(browser, 0, sizeof(*browser));
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
#endif
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/"));
{
ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/'
strcat(browser->project, "/");
}
BROWSER_PRINTF("%s\n", browser->home);
BROWSER_PRINTF("%s\n", browser->desktop);
BROWSER_PRINTF("%s\n", browser->computer);
BROWSER_PRINTF("%s\n", browser->project);
browser_chdir_and_reload_directory_content(browser, browser->project);
}
}
static void browser_free(struct browser *browser) {
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_free(browser->files);
array_free(browser->directories);
memset(browser, 0, sizeof(*browser));
}
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
int clicked = 0;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
if( windowed ) nk_menubar_begin(ctx);
{
char *d = browser->directory;
#ifdef _WIN32
char *begin = d;
#else
char *begin = d + 1;
#endif
nk_layout_row_template_begin(ctx, 25);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_end(ctx);
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
browser->listing ^= 1;
}
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
*d++ = '/'; *d = '\0';
browser_chdir_and_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
if( windowed ) nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
if(nk_window_has_focus(ctx)) {
browser->zooming = clampf( browser->zooming + (input(KEY_LCTRL) || input(KEY_RCTRL)) * input_diff(MOUSE_W) * 0.1, 1, 3);
}
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
size_t cols = total_space.w / (100 * browser->zooming);
int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135
/**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2;
else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5;
if( cols < 1 ) cols=1;
/* window layout */
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
if( !compact )
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->computer);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->project);
nk_group_end(ctx);
}
/* output directory content window */
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0;
size_t count = browser->dir_count + browser->file_count;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
if(!tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
/* draw one row of icons */
if (j < browser->dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - browser->dir_count);
icon = media_icon_for_file(browser->files[fileIndex]);
if (nk_button_image(ctx, *icon)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
}}
if(!tiny)
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
/* draw one row of labels */
if (k < browser->dir_count) {
nk_label(ctx, browser->directories[k], NK_TEXT_CENTERED);
} else {
size_t t = k-browser->dir_count;
nk_label(ctx,browser->files[t],NK_TEXT_CENTERED);
}
}}
if(tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
/* draw one row of icons */
if (j < browser->dir_count) {
/* draw and execute directory buttons */
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], browser->directories[j],NK_TEXT_RIGHT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - browser->dir_count);
icon = media_icon_for_file(browser->files[fileIndex]);
size_t t = j-browser->dir_count;
if (nk_button_image_label(ctx, *icon, browser->files[t],NK_TEXT_RIGHT)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
#if 0
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
bool has_popups = ui_popups(); // @fixme: move out of loop
if( !has_popups && has_focus ) {
struct nk_rect bounds = nk_widget_bounds(ctx);
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
char *name = j < browser->dir_count ? browser->directories[j] : browser->files[j-browser->dir_count];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
struct stat t = {0};
if( stat( fullpath, &t ) != -1 ) {
char tooltip[256];
snprintf(tooltip, 256,
"Path: %s\n"
"Type: %lld\n" // file type and mode
"Size: %lld\n" // file size
"Owner: %lld\n" // user ID of file owner
"Modified: %s (%lld)", // last modification date
name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime
);
nk_tooltip(ctx, tooltip);
}
}
}
#endif
}}
}
if (index != -1) {
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
size_t n = strlen(browser->directory);
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
BROWSER_PRINTF("%s\n", browser->directory);
browser_chdir_and_reload_directory_content(browser, browser->directory);
}
nk_group_end(ctx);
}
return clicked;
}
static struct nk_image icon_load(const char *filename) {
texture_t t = texture(filename, 0);
return nk_image_id((int)t.id);
}
static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) {
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
}
/* demo:
struct browser browser = {0};
browser_init(&browser);
browser_config_dir(nk_image, BROWSER_HOME);
browser_config_dir(nk_image, BROWSER_PROJECT);
// [...]
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
// [...]
[...]
if( nk_begin(ctx, "window", ...) ) {
struct nk_rect total_space = nk_window_get_content_region(ctx);
if( browser_run(ctx, &browser, 0, total_space) ) {
puts( browser->directory );
puts( browser->file );
}
}
nk_end();
*/

View File

@ -0,0 +1,549 @@
/*
* Nuklear - 1.32.0 - public domain
* no warrenty implied; use at your own risk.
* authored from 2015-2016 by Micha Mettke
*/
/*
* ==============================================================
*
* API
*
* ===============================================================
*/
#ifndef NK_GLFW_GL3_H_
#define NK_GLFW_GL3_H_
//#include <GLFW/glfw3.h>
enum nk_glfw_init_state{
NK_GLFW3_DEFAULT=0,
NK_GLFW3_INSTALL_CALLBACKS
};
#ifndef NK_GLFW_TEXT_MAX
#define NK_GLFW_TEXT_MAX 256
#endif
struct nk_glfw_device {
struct nk_buffer cmds;
struct nk_draw_null_texture null;
GLuint vbo, vao, ebo;
GLuint prog;
GLuint vert_shdr;
GLuint frag_shdr;
GLint attrib_pos;
GLint attrib_uv;
GLint attrib_col;
GLint uniform_tex;
GLint uniform_proj;
GLuint font_tex;
};
struct nk_glfw {
GLFWwindow *win;
int width, height;
int display_width, display_height;
struct nk_glfw_device ogl;
struct nk_context ctx;
struct nk_font_atlas atlas;
struct nk_vec2 fb_scale;
unsigned int text[NK_GLFW_TEXT_MAX];
int text_len;
struct nk_vec2 scroll, scroll_bak; //< @r-lyeh, added scroll_bak
double last_button_click;
int is_double_click_down;
struct nk_vec2 double_click_pos;
};
NK_API struct nk_context* nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state);
NK_API void nk_glfw3_shutdown(struct nk_glfw* glfw);
NK_API void nk_glfw3_font_stash_begin(struct nk_glfw* glfw, struct nk_font_atlas **atlas);
NK_API void nk_glfw3_font_stash_end(struct nk_glfw* glfw);
NK_API void nk_glfw3_new_frame(struct nk_glfw* glfw);
NK_API void nk_glfw3_render(struct nk_glfw* glfw, enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer);
NK_API void nk_glfw3_device_destroy(struct nk_glfw* glfw);
NK_API void nk_glfw3_device_create(struct nk_glfw* glfw);
NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, int action, int mods);
/*
* ==============================================================
*
* IMPLEMENTATION
*
* ===============================================================
*/
#ifdef NK_GLFW_GL3_IMPLEMENTATION
#ifndef NK_GLFW_DOUBLE_CLICK_LO
#define NK_GLFW_DOUBLE_CLICK_LO 0.02
#endif
#ifndef NK_GLFW_DOUBLE_CLICK_HI
#define NK_GLFW_DOUBLE_CLICK_HI 0.2
#endif
struct nk_glfw_vertex {
float position[2];
float uv[2];
nk_byte col[4];
};
#ifdef __EMSCRIPTEN__
#define NK_SHADER_VERSION "#version 100\n"
#else
#ifdef __APPLE__
#define NK_SHADER_VERSION "#version 150\n"
#else
#define NK_SHADER_VERSION "#version 300 es\n"
#endif
#endif
NK_API void
nk_glfw3_device_create(struct nk_glfw* glfw)
{
GLint status;
static const GLchar *vertex_shader =
NK_SHADER_VERSION
"uniform mat4 ProjMtx;\n"
#ifdef __EMSCRIPTEN__
"attribute vec2 Position;\n"
"attribute vec2 TexCoord;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
#else
"in vec2 Position;\n"
"in vec2 TexCoord;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
#endif
"void main() {\n"
" Frag_UV = TexCoord;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
"}\n";
static const GLchar *fragment_shader =
NK_SHADER_VERSION
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
#ifdef __EMSCRIPTEN__
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
#else
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
#endif
"void main(){\n"
#ifdef __EMSCRIPTEN__
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV);\n"
#else
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
#endif
"}\n";
struct nk_glfw_device *dev = &glfw->ogl;
nk_buffer_init_default(&dev->cmds);
dev->prog = glCreateProgram();
dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
glCompileShader(dev->vert_shdr);
glCompileShader(dev->frag_shdr);
glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glAttachShader(dev->prog, dev->vert_shdr);
glAttachShader(dev->prog, dev->frag_shdr);
glLinkProgram(dev->prog);
glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
assert(status == GL_TRUE);
dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
{
/* buffer setup */
GLsizei vs = sizeof(struct nk_glfw_vertex);
size_t vp = offsetof(struct nk_glfw_vertex, position);
size_t vt = offsetof(struct nk_glfw_vertex, uv);
size_t vc = offsetof(struct nk_glfw_vertex, col);
glGenBuffers(1, &dev->vbo);
glGenBuffers(1, &dev->ebo);
glGenVertexArrays(1, &dev->vao);
glBindVertexArray(dev->vao);
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
glEnableVertexAttribArray((GLuint)dev->attrib_pos);
glEnableVertexAttribArray((GLuint)dev->attrib_uv);
glEnableVertexAttribArray((GLuint)dev->attrib_col);
glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#ifndef __EMSCRIPTEN__
glBindVertexArray(0);
#endif
}
NK_INTERN void
nk_glfw3_device_upload_atlas(struct nk_glfw* glfw, const void *image, int width, int height)
{
struct nk_glfw_device *dev = &glfw->ogl;
glGenTextures(1, &dev->font_tex);
glBindTexture(GL_TEXTURE_2D, dev->font_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image);
}
NK_API void
nk_glfw3_device_destroy(struct nk_glfw* glfw)
{
struct nk_glfw_device *dev = &glfw->ogl;
glDetachShader(dev->prog, dev->vert_shdr);
glDetachShader(dev->prog, dev->frag_shdr);
glDeleteShader(dev->vert_shdr);
glDeleteShader(dev->frag_shdr);
glDeleteProgram(dev->prog);
glDeleteTextures(1, &dev->font_tex);
glDeleteBuffers(1, &dev->vbo);
glDeleteBuffers(1, &dev->ebo);
nk_buffer_free(&dev->cmds);
}
NK_API void
nk_glfw3_render(struct nk_glfw* glfw, enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer)
{
struct nk_glfw_device *dev = &glfw->ogl;
GLfloat ortho[4][4] = {
{2.0f, 0.0f, 0.0f, 0.0f},
{0.0f,-2.0f, 0.0f, 0.0f},
{0.0f, 0.0f,-1.0f, 0.0f},
{-1.0f,1.0f, 0.0f, 1.0f},
};
ortho[0][0] /= (GLfloat)glfw->width;
ortho[1][1] /= (GLfloat)glfw->height;
/* setup global state */
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glActiveTexture(GL_TEXTURE0);
/* setup program */
glUseProgram(dev->prog);
glUniform1i(dev->uniform_tex, 0);
glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
glViewport(0,0,(GLsizei)glfw->display_width,(GLsizei)glfw->display_height);
{
/* convert from command queue into draw list and draw to screen */
const struct nk_draw_command *cmd = NULL;
void *vertices, *elements;
const nk_draw_index *offset = NULL;
/* allocate vertex and element buffer */
glBindVertexArray(dev->vao);
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW);
/* load draw vertices & elements directly into vertex + element buffer */
#ifndef __EMSCRIPTEN__
vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
#else
vertices = malloc((size_t)max_vertex_buffer);
elements = malloc((size_t)max_element_buffer);
#endif
{
/* fill convert configuration */
struct nk_convert_config config;
static const struct nk_draw_vertex_layout_element vertex_layout[] = {
{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)},
{NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)},
{NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)},
{NK_VERTEX_LAYOUT_END}
};
NK_MEMSET(&config, 0, sizeof(config));
config.vertex_layout = vertex_layout;
config.vertex_size = sizeof(struct nk_glfw_vertex);
config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
config.null = dev->null;
config.circle_segment_count = 22;
config.curve_segment_count = 22;
config.arc_segment_count = 22;
config.global_alpha = 1.0f;
config.shape_AA = AA;
config.line_AA = AA;
/* setup buffers to load vertices and elements */
{struct nk_buffer vbuf, ebuf;
nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer);
nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer);
nk_convert(&glfw->ctx, &dev->cmds, &vbuf, &ebuf, &config);}
}
#ifdef __EMSCRIPTEN__
glBufferSubData(GL_ARRAY_BUFFER, 0, (size_t)max_vertex_buffer, vertices);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, (size_t)max_element_buffer, elements);
free(vertices);
free(elements);
#else
glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
#endif
/* iterate over and execute each draw command */
nk_draw_foreach(cmd, &glfw->ctx, &dev->cmds)
{
if (!cmd->elem_count) continue;
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor(
(GLint)(cmd->clip_rect.x * glfw->fb_scale.x),
(GLint)((glfw->height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw->fb_scale.y),
(GLint)(cmd->clip_rect.w * glfw->fb_scale.x),
(GLint)(cmd->clip_rect.h * glfw->fb_scale.y));
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
offset += cmd->elem_count;
}
nk_clear(&glfw->ctx);
}
/* default OpenGL state */
glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#ifndef __EMSCRIPTEN__
glBindVertexArray(0);
#endif
glDisable(GL_BLEND);
glDisable(GL_SCISSOR_TEST);
}
NK_API void
nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint)
{
if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
struct nk_glfw* glfw = glfwGetWindowUserPointer(win);
if (glfw->text_len < NK_GLFW_TEXT_MAX)
glfw->text[glfw->text_len++] = codepoint;
}
NK_API void
nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
{
if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
struct nk_glfw* glfw = glfwGetWindowUserPointer(win);
glfw->scroll.x += (float)xoff;
glfw->scroll.y += (float)yoff;
glfw->scroll_bak.x += (float)xoff; //< @r-lyeh
glfw->scroll_bak.y += (float)yoff; //< @r-lyeh
}
NK_API void
nk_glfw3_mouse_button_callback(GLFWwindow* win, int button, int action, int mods)
{
if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
double x, y;
if (button != GLFW_MOUSE_BUTTON_LEFT) return;
struct nk_glfw* glfw = glfwGetWindowUserPointer(win);
glfwGetCursorPos(win, &x, &y);
if (action == GLFW_PRESS) {
double dt = glfwGetTime() - glfw->last_button_click;
if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) {
glfw->is_double_click_down = nk_true;
glfw->double_click_pos = nk_vec2((float)x, (float)y);
}
glfw->last_button_click = glfwGetTime();
} else glfw->is_double_click_down = nk_false;
}
NK_INTERN void
nk_glfw3_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
{
(void)usr;
struct nk_glfw* glfw = glfwGetWindowUserPointer(window_handle()); // @rlyeh < struct nk_glfw* glfw = usr.ptr;
const char *text = glfwGetClipboardString(glfw->win);
if (text) nk_textedit_paste(edit, text, nk_strlen(text));
}
NK_INTERN void
nk_glfw3_clipboard_copy(nk_handle usr, const char *text, int len)
{
(void)usr;
struct nk_glfw* glfw = glfwGetWindowUserPointer(window_handle()); // @rlyeh < struct nk_glfw* glfw = usr.ptr;
char *str = 0;
if (!len) return;
str = (char*)malloc((size_t)len+1);
if (!str) return;
memcpy(str, text, (size_t)len);
str[len] = '\0';
glfwSetClipboardString(glfw->win, str);
free(str);
}
NK_API struct nk_context*
nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state init_state)
{
glfwSetWindowUserPointer(win, glfw);
glfw->win = win;
if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
glfwSetCharCallback(win, nk_glfw3_char_callback);
glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
}
nk_init_default(&glfw->ctx, 0);
glfw->ctx.clip.copy = nk_glfw3_clipboard_copy;
glfw->ctx.clip.paste = nk_glfw3_clipboard_paste;
glfw->ctx.clip.userdata = nk_handle_ptr(0);
glfw->last_button_click = 0;
nk_glfw3_device_create(glfw);
glfw->is_double_click_down = nk_false;
glfw->double_click_pos = nk_vec2(0, 0);
return &glfw->ctx;
}
NK_API void
nk_glfw3_font_stash_begin(struct nk_glfw* glfw, struct nk_font_atlas **atlas)
{
nk_font_atlas_init_default(&glfw->atlas);
nk_font_atlas_begin(&glfw->atlas);
*atlas = &glfw->atlas;
}
NK_API void
nk_glfw3_font_stash_end(struct nk_glfw* glfw)
{
const void *image; int w, h;
image = nk_font_atlas_bake(&glfw->atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
nk_glfw3_device_upload_atlas(glfw, image, w, h);
nk_font_atlas_end(&glfw->atlas, nk_handle_id((int)glfw->ogl.font_tex), &glfw->ogl.null);
if (glfw->atlas.default_font)
nk_style_set_font(&glfw->ctx, &glfw->atlas.default_font->handle);
nk_style_load_all_cursors(&glfw->ctx, glfw->atlas.cursors); //< @r-lyeh
nk_style_hide_cursor(&glfw->ctx); //< @r-lyeh
}
NK_API void
nk_glfw3_new_frame(struct nk_glfw* glfw)
{
int i;
double x, y;
struct nk_context *ctx = &glfw->ctx;
struct GLFWwindow *win = glfw->win;
glfwGetWindowSize(win, &glfw->width, &glfw->height);
glfwGetFramebufferSize(win, &glfw->display_width, &glfw->display_height);
glfw->fb_scale.x = (float)glfw->display_width/(float)glfw->width;
glfw->fb_scale.y = (float)glfw->display_height/(float)glfw->height;
nk_input_begin(ctx);
for (i = 0; i < glfw->text_len; ++i)
nk_input_unicode(ctx, glfw->text[i]);
#if NK_GLFW_GL3_MOUSE_GRABBING
/* optional grabbing behavior */
if (ctx->input.mouse.grab)
glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
else if (ctx->input.mouse.ungrab)
glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
#endif
nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
} else {
nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_COPY, 0);
nk_input_key(ctx, NK_KEY_PASTE, 0);
nk_input_key(ctx, NK_KEY_CUT, 0);
nk_input_key(ctx, NK_KEY_SHIFT, 0);
}
glfwGetCursorPos(win, &x, &y);
nk_input_motion(ctx, (int)x, (int)y);
#if NK_GLFW_GL3_MOUSE_GRABBING
if (ctx->input.mouse.grabbed) {
glfwSetCursorPos(glfw->win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y);
ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
}
#endif
nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_DOUBLE, glfw->double_click_pos.x, glfw->double_click_pos.y, glfw->is_double_click_down);
nk_input_scroll(ctx, glfw->scroll);
nk_input_end(&glfw->ctx);
glfw->text_len = 0;
glfw->scroll = nk_vec2(0,0);
}
NK_API
void nk_glfw3_shutdown(struct nk_glfw* glfw)
{
nk_font_atlas_clear(&glfw->atlas);
nk_free(&glfw->ctx);
nk_glfw3_device_destroy(glfw);
memset(glfw, 0, sizeof(*glfw));
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,409 @@
/* Progressive Mesh type Polygon Reduction Algorithm
*
* 1998: Original version by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* 2014: Code style upgraded to be more consistent with graphics/gamedev conventions. Relicensed as MIT/PD.
* Stan Melax: "Yes, this code can be licensed with the same license as the original. That should be fine."
*
* 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD.
*/
static inline void array_find_and_remove(array(int) arr, int v) {
for( int i = 0, end = array_count(arr); i < end; i++ )
if( arr[i] == v ) { array_erase_fast(arr, i); --end; break; }
}
#include <assert.h>
#include <math.h>
#include <stdlib.h>
struct triangle_n {
int vertex[3]; // the 3 points (id) that make this tri
vec3 normal; // unit vector othogonal to this face
};
struct vertex {
vec3 position; // location of point in euclidean space
array(int) neighbor; // adjacent vertices
array(int) face; // adjacent triangles
int id; // place of vertex in original Array
int collapse; // candidate vertex (id) for collapse
float objdist; // cached cost of collapsing edge
};
struct mesh {
struct vertex *v;
struct triangle_n *t;
int n_face;
int n_vertex;
};
// array
static inline struct vertex *Vertex(struct mesh *M, int id) { return M->v + id; }
static inline struct triangle_n *Triangle(struct mesh *M, int id) { return M->t + id; }
static inline struct triangle_n *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; }
static void AddVertex(struct mesh *M, const float *v) {
int id = M->n_vertex++;
struct vertex * tmp = Vertex(M, id);
tmp->position = ptr3(v);
tmp->neighbor = NULL;
tmp->face = NULL;
tmp->id = id;
tmp->collapse = -1;
tmp->objdist = 0;
}
static void RemoveVertex(struct mesh *M, int id) {
struct vertex * v = Vertex(M, id);
ASSERT(v->id == id);
ASSERT(array_count(v->face) == 0);
for (int i=0;i<array_count(v->face);i++) {
struct vertex * nv = Vertex(M, v->face[i]);
array_find_and_remove(nv->neighbor, id);
}
v->id = -1; // invalid vertex id
array_free(v->neighbor);
array_free(v->face);
}
static void ComputeNormal(struct mesh *M, struct triangle_n *t) {
struct vertex * v0 = Vertex(M, t->vertex[0]);
struct vertex * v1 = Vertex(M, t->vertex[1]);
struct vertex * v2 = Vertex(M, t->vertex[2]);
vec3 a = sub3(v1->position, v0->position);
vec3 b = sub3(v2->position, v1->position);
t->normal = norm3(cross3(a,b));
}
static void AddNeighbor(struct mesh *M, int vid, int id) {
struct vertex *v = Vertex(M, vid);
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id)
return;
}
array_push(v->neighbor, id);
}
static void AddTriangle(struct mesh *M, const int v[3]) {
if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
return;
ASSERT(v[0] < M->n_vertex);
ASSERT(v[1] < M->n_vertex);
ASSERT(v[2] < M->n_vertex);
int id = M->n_face++;
struct triangle_n * tmp = Triangle(M, id);
tmp->vertex[0] = v[0];
tmp->vertex[1] = v[1];
tmp->vertex[2] = v[2];
ComputeNormal(M, tmp);
for(int i=0;i<3;i++) {
struct vertex *obj = Vertex(M, v[i]);
array_push(obj->face, id);
}
AddNeighbor(M, v[0], v[1]);
AddNeighbor(M, v[0], v[2]);
AddNeighbor(M, v[1], v[0]);
AddNeighbor(M, v[1], v[2]);
AddNeighbor(M, v[2], v[0]);
AddNeighbor(M, v[2], v[1]);
}
static int HasVertex(struct triangle_n * t, int vid) {
return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid);
}
static void RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) {
for (int i=0;i<array_count(v->neighbor);i++) {
if (v->neighbor[i] == id) {
for (int j=0;j<array_count(v->face);j++) {
if (HasVertex(Face(M, v, j), id))
return;
}
// remove from neighbors
array_erase_fast(v->neighbor, i);
return;
}
}
}
static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) {
if (v0 == NULL || v1 == NULL)
return;
RemoveIfNonNeighbor_(M, v0, v1->id);
RemoveIfNonNeighbor_(M, v1, v0->id);
}
static void RemoveTriangle(struct mesh *M, int id) {
struct triangle_n * face = Triangle(M, id);
struct vertex * v[3];
for (int i=0;i<3;i++) {
v[i] = Vertex(M, face->vertex[i]);
if (v[i]->id < 0)
v[i] = NULL;
else {
array_find_and_remove(v[i]->face, id);
}
}
RemoveIfNonNeighbor(M, v[0], v[1]);
RemoveIfNonNeighbor(M, v[1], v[2]);
RemoveIfNonNeighbor(M, v[2], v[0]);
}
static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) {
struct triangle_n * face = Triangle(M, faceid);
ASSERT(oldid >=0 && newid >= 0);
ASSERT(HasVertex(face, oldid));
ASSERT(!HasVertex(face, newid));
if(oldid==face->vertex[0]){
face->vertex[0]=newid;
} else if(oldid==face->vertex[1]){
face->vertex[1]=newid;
} else {
face->vertex[2]=newid;
}
struct vertex *vold = Vertex(M, oldid);
struct vertex *vnew = Vertex(M, newid);
array_find_and_remove(vold->face, faceid);
array_push(vnew->face, faceid);
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1]));
RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2]));
AddNeighbor(M, face->vertex[0], face->vertex[1]);
AddNeighbor(M, face->vertex[0], face->vertex[2]);
AddNeighbor(M, face->vertex[1], face->vertex[0]);
AddNeighbor(M, face->vertex[1], face->vertex[2]);
AddNeighbor(M, face->vertex[2], face->vertex[0]);
AddNeighbor(M, face->vertex[2], face->vertex[1]);
ComputeNormal(M, face);
}
static void MeshInit(struct mesh *M, int vert_n, int tri_n) {
M->n_face = 0;
M->n_vertex = 0;
M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex));
M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle));
}
static void MeshFree(struct mesh *M) {
FREE(M->v);
FREE(M->t);
}
static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) {
// if we collapse edge uv by moving u to v then how
// much different will the model change, i.e. how much "error".
// Texture, vertex normal, and border vertex code was removed
// to keep this demo as simple as possible.
// The method of determining cost was designed in order
// to exploit small and coplanar regions for
// effective polygon reduction.
// Is is possible to add some checks here to see if "folds"
// would be generated. i.e. normal of a remaining face gets
// flipped. I never seemed to run into this problem and
// therefore never added code to detect this case.
struct vertex *v = Vertex(M, vid);
vec3 tmp = sub3(v->position, u->position);
float edgelength = len3(tmp);
float curvature=0;
// find the "sides" triangles that are on the edge uv
array(int) sides = 0;
for (int i = 0; i<array_count(u->face); i++) {
if (HasVertex(Face(M, u, i), vid)) {
array_push(sides, u->face[i]);
}
}
// use the triangle facing most away from the sides
// to determine our curvature term
for (int i = 0; i<array_count(u->face); i++) {
float mincurv=1; // curve for face i and closer side to it
for (int j = 0; j<array_count(sides); j++) {
float dotprod = dot3(Triangle(M, u->face[i])->normal,
Triangle(M, sides[j])->normal); // use dot product of face normals.
float t = (1-dotprod)/2.0f;
if (t < mincurv) {
mincurv = t;
}
}
if (mincurv > curvature)
curvature = mincurv;
}
array_free(sides);
// the more coplanar the lower the curvature term
return edgelength * curvature;
}
static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) {
// compute the edge collapse cost for all edges that start
// from vertex v. Since we are only interested in reducing
// the object by selecting the min cost edge at each step, we
// only cache the cost of the least cost edge at this vertex
// (in member variable collapse) as well as the value of the
// cost (in member variable objdist).
if (array_count(v->neighbor) == 0) {
// v doesn't have neighbors so it costs nothing to collapse
v->collapse=-1;
v->objdist=-0.01f;
return;
}
v->objdist = 1000000;
v->collapse=-1;
// search all neighboring edges for "least cost" edge
for (int i = 0; i<array_count(v->neighbor); i++) {
float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]);
if(dist<v->objdist) {
v->collapse=v->neighbor[i]; // candidate for edge collapse
v->objdist=dist; // cost of the collapse
}
}
}
static void ComputeAllEdgeCollapseCosts(struct mesh *M) {
// For all the edges, compute the difference it would make
// to the model if it was collapsed. The least of these
// per vertex is cached in each vertex object.
for (int i = 0; i<M->n_vertex; i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, i));
}
}
static void Collapse(struct mesh *M, int uid, int vid) {
// Collapse the edge uv by moving vertex u onto v
// Actually remove tris on uv, then update tris that
// have u to have v, and then remove u.
struct vertex *u = Vertex(M, uid);
if(vid < 0) {
// u is a vertex all by itself so just delete it
RemoveVertex(M, uid);
return;
}
array(int) tmp = 0;
// make tmp a Array of all the neighbors of u
for (int i = 0; i<array_count(u->neighbor); i++) {
array_push(tmp, u->neighbor[i]);
}
// delete triangles on edge uv:
for( int i = array_count(u->face); i--; ) {
if (HasVertex(Face(M, u, i), vid)) {
RemoveTriangle(M, u->face[i]);
}
}
// update remaining triangles to have v instead of u
for( int i = array_count(u->face); i--; ) {
ReplaceVertex(M, u->face[i], uid, vid);
}
RemoveVertex(M, uid);
// recompute the edge collapse costs for neighboring vertices
for (int i = 0; i<array_count(tmp); i++) {
ComputeEdgeCostAtVertex(M, Vertex(M, tmp[i]));
}
array_free(tmp);
}
static struct vertex *MinimumCostEdge(struct mesh *M) {
// Find the edge that when collapsed will affect model the least.
// This function actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
// Serious optimization opportunity here: this function currently
// does a sequential search through an unsorted Array :-(
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
struct vertex *mn = NULL;
for (int i = 0; i<M->n_vertex; i++) {
struct vertex *v = Vertex(M, i);
if (v->id >=0) {
if (mn == NULL || v->objdist < mn->objdist) {
mn = v;
}
}
}
return mn;
}
/*
* The function ProgressiveMesh() takes a model in an "indexed face
* set" sort of way. i.e. Array of vertices and Array of triangles.
* The function then does the polygon reduction algorithm
* internally and reduces the model all the way down to 0
* vertices and then returns the order in which the
* vertices are collapsed and to which neighbor each vertex
* is collapsed to. More specifically the returned "permutation"
* indicates how to reorder your vertices so you can render
* an object by using the first n vertices (for the n
* vertex version). After permuting your vertices, the
* map Array indicates to which vertex each vertex is collapsed to.
*/
API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) {
struct mesh M;
MeshInit(&M, vert_n, tri_n);
// put input data into our data structures M
const char * tmp = (const char *)v;
for (int i=0;i<vert_n;i++, tmp += vert_stride ) {
AddVertex(&M, (const float *)tmp);
}
for (int i=0;i<tri_n;i++) {
AddTriangle(&M, &tri[i*3]);
}
ComputeAllEdgeCollapseCosts(&M); // cache all edge collapse costs
for (int i = vert_n-1; i>=0; i--) {
// get the next vertex to collapse
struct vertex *mn = MinimumCostEdge(&M);
// keep track of this vertex, i.e. the collapse ordering
permutation[mn->id] = i;
// keep track of vertex to which we collapse to
map[i] = mn->collapse;
// Collapse this edge
Collapse(&M, mn->id, mn->collapse);
}
// reorder the map Array based on the collapse ordering
for (int i = 0; i<vert_n; i++) {
map[i] = (map[i]==-1)?0:permutation[map[i]];
}
// The caller of this function should reorder their vertices
// according to the returned "permutation".
MeshFree(&M);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Stan Melax
* Copyright (c) 2020 Cloud Wu
*
* 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.
*/

View File

@ -0,0 +1,481 @@
/** 1D, 2D, 3D and 4D float Perlin Simplex noise */
/** Original code, stefan gustavson (PD). */
#ifdef SIMPLEX_C
/* SimplexNoise1234, Simplex noise with true analytic
* derivative in 1D to 4D.
*
* Author: Stefan Gustavson, 2003-2005
* Contact: stefan.gustavson@liu.se
*
* This code was GPL licensed until February 2011.
* As the original author of this code, I hereby
* release it into the public domain.
* Please feel free to use it for whatever you want.
* Credit is appreciated where appropriate, and I also
* appreciate being told where this code finds any use,
* but you may do as you like.
*/
/*
* This implementation is "Simplex Noise" as presented by
* Ken Perlin at a relatively obscure and not often cited course
* session "Real-Time Shading" at Siggraph 2001 (before real
* time shading actually took off), under the title "hardware noise".
* The 3D function is numerically equivalent to his Java reference
* code available in the PDF course notes, although I re-implemented
* it from scratch to get more readable code. The 1D, 2D and 4D cases
* were implemented from scratch by me from Ken Perlin's text.
*
* This file has no dependencies on any other file, not even its own
* header file. The header file is made for use by external code only.
*/
// We don't really need to include this, but play nice and do it anyway.
//#include "noise.c"
#define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) )
//---------------------------------------------------------------------
// Static data
/*
* Permutation table. This is just a random jumble of all numbers 0-255,
* repeated twice to avoid wrapping the index at 255 for each lookup.
* This needs to be exactly the same for all instances on all platforms,
* so it's easiest to just keep it as static explicit data.
* This also removes the need for any initialisation of this class.
*
* Note that making this an int[] instead of a char[] might make the
* code run faster on platforms with a high penalty for unaligned single
* byte addressing. Intel x86 is generally single-byte-friendly, but
* some other CPUs are faster with 4-aligned reads.
* However, a char[] is smaller, which avoids cache trashing, and that
* is probably the most important aspect on most architectures.
* This array is accessed a *lot* by the noise functions.
* A vector-valued noise over 3D accesses it 96 times, and a
* float-valued 4D noise 64 times. We want this to fit in the cache!
*/
unsigned char perm[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,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
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,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
//---------------------------------------------------------------------
/*
* Helper functions to compute gradients-dot-residualvectors (1D to 4D)
* Note that these generate gradients of more than unit length. To make
* a close match with the value range of classic Perlin noise, the final
* noise values need to be rescaled to fit nicely within [-1,1].
* (The simplex noise functions as such also have different scaling.)
* Note also that these noise functions are the most practical and useful
* signed version of Perlin noise. To return values according to the
* RenderMan specification from the SL noise() and pnoise() functions,
* the noise values need to be scaled and offset to [0,1], like this:
* float SLnoise = (noise(x,y,z) + 1.0) * 0.5;
*/
float grad1( int hash, float x ) {
int h = hash & 15;
float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
if (h&8) grad = -grad; // Set a random sign for the gradient
return ( grad * x ); // Multiply the gradient with the distance
}
float grad2( int hash, float x, float y ) {
int h = hash & 7; // Convert low 3 bits of hash code
float u = h<4 ? x : y; // into 8 simple gradient directions,
float v = h<4 ? y : x; // and compute the dot product with (x,y).
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
}
float grad3( int hash, float x, float y , float z ) {
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
float u = h<8 ? x : y; // gradient directions, and compute dot product.
float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
return ((h&1)? -u : u) + ((h&2)? -v : v);
}
float grad4( int hash, float x, float y, float z, float t ) {
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
float u = h<24 ? x : y; // gradient directions, and compute dot product.
float v = h<16 ? y : z;
float w = h<8 ? z : t;
return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w);
}
// A lookup table to traverse the simplex around a given point in 4D.
// Details can be found where this table is used, in the 4D noise method.
/* TODO: This should not be required, backport it from Bill's GLSL code! */
static unsigned char simplex[64][4] = {
{0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0},
{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},
{1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0},
{2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}};
// 1D simplex noise
float snoise1(float x) {
int i0 = FASTFLOOR(x);
int i1 = i0 + 1;
float x0 = x - i0;
float x1 = x0 - 1.0f;
float n0, n1;
float t0 = 1.0f - x0*x0;
// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case
t0 *= t0;
n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0);
float t1 = 1.0f - x1*x1;
// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case
t1 *= t1;
n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1);
// The maximum value of this noise is 8*(3/4)^4 = 2.53125
// A factor of 0.395 would scale to fit exactly within [-1,1], but
// we want to match PRMan's 1D noise, so we scale it down some more.
return 0.25f * (n0 + n1);
}
// 2D simplex noise
float snoise2(float x, float y) {
#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0)
#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0
float n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y)*F2; // Hairy factor for 2D
float xs = x + s;
float ys = y + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
float t = (float)(i+j)*G2;
float X0 = i-t; // Unskew the cell origin back to (x,y) space
float Y0 = j-t;
float x0 = x-X0; // The x,y distances from the cell origin
float y0 = y-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
float y1 = y0 - j1 + G2;
float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
float y2 = y0 - 1.0f + 2.0f * G2;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
// Calculate the contribution from the three corners
float t0 = 0.5f - x0*x0-y0*y0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0);
}
float t1 = 0.5f - x1*x1-y1*y1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1);
}
float t2 = 0.5f - x2*x2-y2*y2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
}
// 3D simplex noise
float snoise3(float x, float y, float z) {
// Simple skewing factors for the 3D case
#define F3 0.333333333
#define G3 0.166666667
float n0, n1, n2, n3; // Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D
float xs = x+s;
float ys = y+s;
float zs = z+s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
float t = (float)(i+j+k)*G3;
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
float Y0 = j-t;
float Z0 = k-t;
float x0 = x-X0; // The x,y,z distances from the cell origin
float y0 = y-Y0;
float z0 = z-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
/* This code would benefit from a backport from the GLSL version! */
if(x0>=y0) {
if(y0>=z0)
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords
float y2 = y0 - j2 + 2.0f*G3;
float z2 = z0 - k2 + 2.0f*G3;
float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords
float y3 = y0 - 1.0f + 3.0f*G3;
float z3 = z0 - 1.0f + 3.0f*G3;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
// Calculate the contribution from the four corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad3(perm[ii+perm[jj+perm[kk]]], x0, y0, z0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad3(perm[ii+i1+perm[jj+j1+perm[kk+k1]]], x1, y1, z1);
}
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad3(perm[ii+i2+perm[jj+j2+perm[kk+k2]]], x2, y2, z2);
}
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3;
if(t3<0.0f) n3 = 0.0f;
else {
t3 *= t3;
n3 = t3 * t3 * grad3(perm[ii+1+perm[jj+1+perm[kk+1]]], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 72.0f * (n0 + n1 + n2 + n3);
}
// 4D simplex noise
float snoise4(float x, float y, float z, float w) {
// The skewing and unskewing factors are hairy again for the 4D case
#define F4 0.309016994 // F4 = (Math.sqrt(5.0)-1.0)/4.0
#define G4 0.138196601 // G4 = (5.0-Math.sqrt(5.0))/20.0
float n0, n1, n2, n3, n4; // Noise contributions from the five corners
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
float s = (x + y + z + w) * F4; // Factor for 4D skewing
float xs = x + s;
float ys = y + s;
float zs = z + s;
float ws = w + s;
int i = FASTFLOOR(xs);
int j = FASTFLOOR(ys);
int k = FASTFLOOR(zs);
int l = FASTFLOOR(ws);
float t = (i + j + k + l) * G4; // Factor for 4D unskewing
float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
float Y0 = j - t;
float Z0 = k - t;
float W0 = l - t;
float x0 = x - X0; // The x,y,z,w distances from the cell origin
float y0 = y - Y0;
float z0 = z - Z0;
float w0 = w - W0;
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// The method below is a good way of finding the ordering of x,y,z,w and
// then find the correct traversal order for the simplex we?re in.
// First, six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to add up binary bits
// for an integer index.
int c1 = (x0 > y0) ? 32 : 0;
int c2 = (x0 > z0) ? 16 : 0;
int c3 = (y0 > z0) ? 8 : 0;
int c4 = (x0 > w0) ? 4 : 0;
int c5 = (y0 > w0) ? 2 : 0;
int c6 = (z0 > w0) ? 1 : 0;
int c = c1 + c2 + c3 + c4 + c5 + c6;
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// The number 3 in the "simplex" array is at the position of the largest coordinate.
i1 = simplex[c][0]>=3 ? 1 : 0;
j1 = simplex[c][1]>=3 ? 1 : 0;
k1 = simplex[c][2]>=3 ? 1 : 0;
l1 = simplex[c][3]>=3 ? 1 : 0;
// The number 2 in the "simplex" array is at the second largest coordinate.
i2 = simplex[c][0]>=2 ? 1 : 0;
j2 = simplex[c][1]>=2 ? 1 : 0;
k2 = simplex[c][2]>=2 ? 1 : 0;
l2 = simplex[c][3]>=2 ? 1 : 0;
// The number 1 in the "simplex" array is at the second smallest coordinate.
i3 = simplex[c][0]>=1 ? 1 : 0;
j3 = simplex[c][1]>=1 ? 1 : 0;
k3 = simplex[c][2]>=1 ? 1 : 0;
l3 = simplex[c][3]>=1 ? 1 : 0;
// The fifth corner has all coordinate offsets = 1, so no need to look that up.
float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
float y1 = y0 - j1 + G4;
float z1 = z0 - k1 + G4;
float w1 = w0 - l1 + G4;
float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords
float y2 = y0 - j2 + 2.0f*G4;
float z2 = z0 - k2 + 2.0f*G4;
float w2 = w0 - l2 + 2.0f*G4;
float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords
float y3 = y0 - j3 + 3.0f*G4;
float z3 = z0 - k3 + 3.0f*G4;
float w3 = w0 - l3 + 3.0f*G4;
float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords
float y4 = y0 - 1.0f + 4.0f*G4;
float z4 = z0 - 1.0f + 4.0f*G4;
float w4 = w0 - 1.0f + 4.0f*G4;
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
int ll = l & 0xff;
// Calculate the contribution from the five corners
float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0;
if(t0 < 0.0f) n0 = 0.0f;
else {
t0 *= t0;
n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0);
}
float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1 - w1*w1;
if(t1 < 0.0f) n1 = 0.0f;
else {
t1 *= t1;
n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1);
}
float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2 - w2*w2;
if(t2 < 0.0f) n2 = 0.0f;
else {
t2 *= t2;
n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2);
}
float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3 - w3*w3;
if(t3 < 0.0f) n3 = 0.0f;
else {
t3 *= t3;
n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3);
}
float t4 = 0.5f - x4*x4 - y4*y4 - z4*z4 - w4*w4;
if(t4 < 0.0f) n4 = 0.0f;
else {
t4 *= t4;
n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4);
}
// Sum up and scale the result to cover the range [-1,1]
return 62.0f * (n0 + n1 + n2 + n3 + n4);
}
#undef F2
#undef G2
#undef F3
#undef G3
#undef F4
#undef G4
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,712 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*xsnprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*xvsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
/* Version 3.20.0 and later */
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
sqlite3_stmt**,const char**);
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
sqlite3_stmt**,const void**);
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
void *(*value_pointer)(sqlite3_value*,const char*);
int (*vtab_nochange)(sqlite3_context*);
int (*value_nochange)(sqlite3_value*);
const char *(*vtab_collation)(sqlite3_index_info*,int);
/* Version 3.24.0 and later */
int (*keyword_count)(void);
int (*keyword_name)(int,const char**,int*);
int (*keyword_check)(const char*,int);
sqlite3_str *(*str_new)(sqlite3*);
char *(*str_finish)(sqlite3_str*);
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
void (*str_append)(sqlite3_str*, const char *zIn, int N);
void (*str_appendall)(sqlite3_str*, const char *zIn);
void (*str_appendchar)(sqlite3_str*, int N, char C);
void (*str_reset)(sqlite3_str*);
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
/* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
/* Version 3.30.0 and later */
int (*drop_modules)(sqlite3*,const char**);
/* Version 3.31.0 and later */
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
const char *(*uri_key)(const char*,int);
const char *(*filename_database)(const char*);
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
const char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
/* Version 3.36.1 and later */
sqlite3_int64 (*changes64)(sqlite3*);
sqlite3_int64 (*total_changes64)(sqlite3*);
/* Version 3.37.0 and later */
int (*autovacuum_pages)(sqlite3*,
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
void*, void(*)(void*));
/* Version 3.38.0 and later */
int (*error_offset)(sqlite3*);
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
int (*vtab_distinct)(sqlite3_index_info*);
int (*vtab_in)(sqlite3_index_info*,int,int);
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
/* Version 3.39.0 and later */
int (*deserialize)(sqlite3*,const char*,unsigned char*,
sqlite3_int64,sqlite3_int64,unsigned);
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
/* Version 3.40.0 and later */
int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->xsnprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
/* Version 3.20.0 and later */
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
#define sqlite3_result_pointer sqlite3_api->result_pointer
#define sqlite3_value_pointer sqlite3_api->value_pointer
/* Version 3.22.0 and later */
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
#define sqlite3_value_nochange sqlite3_api->value_nochange
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
/* Version 3.24.0 and later */
#define sqlite3_keyword_count sqlite3_api->keyword_count
#define sqlite3_keyword_name sqlite3_api->keyword_name
#define sqlite3_keyword_check sqlite3_api->keyword_check
#define sqlite3_str_new sqlite3_api->str_new
#define sqlite3_str_finish sqlite3_api->str_finish
#define sqlite3_str_appendf sqlite3_api->str_appendf
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
#define sqlite3_str_append sqlite3_api->str_append
#define sqlite3_str_appendall sqlite3_api->str_appendall
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
#define sqlite3_str_reset sqlite3_api->str_reset
#define sqlite3_str_errcode sqlite3_api->str_errcode
#define sqlite3_str_length sqlite3_api->str_length
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
#define sqlite3_value_frombind sqlite3_api->value_frombind
/* Version 3.30.0 and later */
#define sqlite3_drop_modules sqlite3_api->drop_modules
/* Version 3.31.0 and later */
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
#define sqlite3_uri_key sqlite3_api->uri_key
#define sqlite3_filename_database sqlite3_api->filename_database
#define sqlite3_filename_journal sqlite3_api->filename_journal
#define sqlite3_filename_wal sqlite3_api->filename_wal
/* Version 3.32.0 and later */
#define sqlite3_create_filename sqlite3_api->create_filename
#define sqlite3_free_filename sqlite3_api->free_filename
#define sqlite3_database_file_object sqlite3_api->database_file_object
/* Version 3.34.0 and later */
#define sqlite3_txn_state sqlite3_api->txn_state
/* Version 3.36.1 and later */
#define sqlite3_changes64 sqlite3_api->changes64
#define sqlite3_total_changes64 sqlite3_api->total_changes64
/* Version 3.37.0 and later */
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
/* Version 3.38.0 and later */
#define sqlite3_error_offset sqlite3_api->error_offset
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
#define sqlite3_vtab_in sqlite3_api->vtab_in
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
/* Version 3.39.0 and later */
#ifndef SQLITE_OMIT_DESERIALIZE
#define sqlite3_deserialize sqlite3_api->deserialize
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
/* Version 3.40.0 and later */
#define sqlite3_value_encoding sqlite3_api->value_encoding
/* Version 3.41.0 and later */
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,511 @@
///////////////////////////////////////////////////////////////////////////////
// sts_mixer.h - v0.02
// written 2016 by Sebastian Steinhauer
//
// LICENSE
// Public domain. See "unlicense" statement at the end of this file.
//
// ABOUT
// A simple stereo audio mixer which is capable of mixing samples and audio streams.
// Samples can be played with different gain, pitch and panning.
// Streams can be played with different gain.
// This library has no malloc/free. All structs have to be "prepared" by the user. So you can enroll your own memory management.
// You have to implement/provide a real audio-backend to hear something from the speakers.
// A good starting point would be SDL2 where you can use an audio callback to feed the audio device.
//
// USAGE
// Please note that most audio systems will run in a separate thread. So you have to take care about locking before modifying the sts_mixer_t state.
// See the example at the end of the file.
//
// VERSION HISTORY
// 0.02 (2022-05-10) allow voice queueing in same channel. ie, chain another sample on same voice channel after current sample playback is done (@r-lyeh)
// 0.01 (2016-05-01) initial version
//
#ifndef __INCLUDED__STS_MIXER_H__
#define __INCLUDED__STS_MIXER_H__
// The number of concurrent voices (channels) which are used to mix the audio.
// If you need more, use a higher number by setting #define STS_MIXER_VOICE n before including this header.
#ifndef STS_MIXER_VOICES
#define STS_MIXER_VOICES 32
#endif // STS_MIXER_VOICES
// Defines the various audio formats. Note that they are all on system endianess.
enum {
STS_MIXER_SAMPLE_FORMAT_NONE, // no format
STS_MIXER_SAMPLE_FORMAT_8, // signed 8-bit
STS_MIXER_SAMPLE_FORMAT_16, // signed 16-bit
STS_MIXER_SAMPLE_FORMAT_32, // signed 32-bit
STS_MIXER_SAMPLE_FORMAT_FLOAT // floats
};
////////////////////////////////////////////////////////////////////////////////
//
// SAMPLES
//
// A sample is a *MONO* piece of audio which is loaded fully to memory.
// It can be played with various gains, pitches and pannings.
//
typedef struct {
unsigned int length; // length in samples (so 1024 samples of STS_MIXER_SAMPLE_FORMAT_16 would be 2048 bytes)
unsigned int frequency; // frequency of this sample (e.g. 44100, 22000 ...)
int audio_format; // one of STS_MIXER_SAMPLE_FORMAT_*
void* data; // pointer to the sample data, sts_mixer makes no copy, so you have to keep them in memory
void* next; // next sample in chain (if any) //< @r-lyeh
} sts_mixer_sample_t;
////////////////////////////////////////////////////////////////////////////////
//
// STREAMS
//
// A stream is *STEREO* audio which will be decoded/loaded as needed.
// It can be played with various gains. No panning or pitching.
//
// The callback which will be called when the stream needs more data.
typedef void (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
typedef struct {
void* userdata; // a userdata pointer which will passed to the callback
sts_mixer_stream_callback callback; // this callback will be called when the stream needs more data
sts_mixer_sample_t sample; // the current stream "sample" which holds the current piece of audio
} sts_mixer_stream_t;
////////////////////////////////////////////////////////////////////////////////
//
// VOICES
//
// A voice is an audio source which will be used during mixing.
// It can play nothing, a sample or a stream.
// Most of those fields are considered "private" and you should not play around with those.
//
typedef struct {
int state;
sts_mixer_sample_t* sample;
sts_mixer_stream_t* stream;
float position;
float gain;
float pitch;
float pan;
} sts_mixer_voice_t;
////////////////////////////////////////////////////////////////////////////////
//
// MIXER
//
// The mixer state.
//
typedef struct {
float gain; // the global gain (you can change it if you want to change to overall volume)
unsigned int frequency; // the frequency for the output of mixed audio data
int audio_format; // the audio format for the output of mixed audio data
sts_mixer_voice_t voices[STS_MIXER_VOICES]; // holding all audio voices for this state
} sts_mixer_t;
////////////////////////////////////////////////////////////////////////////////
//
// API
//
// "Initializes" a new sts_mixer state.
void sts_mixer_init(sts_mixer_t* mixer, unsigned int frequency, int audio_format);
// "Shutdown" the mixer state. It will simply reset all fields.
void sts_mixer_shutdown(sts_mixer_t* mixer);
// Return the number of active voices. Active voices are voices that play either a stream or a sample.
int sts_mixer_get_active_voices(sts_mixer_t* mixer);
// Play the given sample with the gain, pitch and panning.
// Panning can be something between -1.0f (fully left) ... +1.0f (fully right)
// Please note that pitch will be clamped so it cannot reach 0.0f (would be useless).
// Returns the number of the voice where this sample will be played or -1 if no voice was free.
int sts_mixer_play_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample, float gain, float pitch, float pan);
// Plays the given stream with the gain.
// Returns the number of the voice where this stream will be played or -1 if no voice was free.
int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float gain);
// Stops voice with the given voice no. You can pass the returned number of sts_mixer_play_sample / sts_mixer_play_stream here.
void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice);
// Stops all voices playing the given sample. Useful when you want to delete the sample and make sure it is not used anymore.
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample);
// Stops all voices playing the given stream. Useful when you want to delete the stream and make sure it is not used anymore.
void sts_mixer_stop_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream);
// The mixing function. You should call the function if you need to pass more audio data to the audio device.
// Typically this function is called in a separate thread or something like that.
// It will write audio data in the specified format and frequency of the mixer state.
void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples);
#endif // __INCLUDED__STS_MIXER_H__
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
////
//// IMPLEMENTATION
////
////
#ifdef STS_MIXER_IMPLEMENTATION
enum {
STS_MIXER_VOICE_STOPPED,
STS_MIXER_VOICE_PLAYING,
STS_MIXER_VOICE_STREAMING
};
static float sts_mixer__clamp(const float value, const float min, const float max) {
if (value < min) return min;
else if (value > max) return max;
else return value;
}
static float sts_mixer__clamp_sample(const float sample) {
if (sample < -1.0f) return -1.0f;
else if (sample > 1.0f) return 1.0f;
else return sample;
}
static float sts_mixer__get_sample(sts_mixer_sample_t* sample, unsigned int position) {
switch (sample->audio_format) {
case STS_MIXER_SAMPLE_FORMAT_8:
return (float)((char*)sample->data)[position] / 127.0f;
case STS_MIXER_SAMPLE_FORMAT_16:
return (float)((short*)sample->data)[position] / 32767.0f;
case STS_MIXER_SAMPLE_FORMAT_32:
return (float)((int*)sample->data)[position] / 2147483647.0f;
case STS_MIXER_SAMPLE_FORMAT_FLOAT:
return ((float*)sample->data)[position];
default:
return 0.0f;
}
}
static void sts_mixer__reset_voice(sts_mixer_t* mixer, const int i) {
sts_mixer_voice_t* voice = &mixer->voices[i];
voice->state = STS_MIXER_VOICE_STOPPED;
voice->sample = 0;
voice->stream = 0;
voice->position = voice->gain = voice->pitch = voice->pan = 0.0f;
}
static int sts_mixer__find_free_voice(sts_mixer_t* mixer) {
int i;
for (i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].state == STS_MIXER_VOICE_STOPPED) return i;
}
return -1;
}
void sts_mixer_init(sts_mixer_t* mixer, unsigned int frequency, int audio_format) {
int i;
for (i = 0; i < STS_MIXER_VOICES; ++i) sts_mixer__reset_voice(mixer, i);
mixer->frequency = frequency;
mixer->gain = 1.0f;
mixer->audio_format = audio_format;
}
void sts_mixer_shutdown(sts_mixer_t* mixer) {
sts_mixer_init(mixer, 0, 0);
}
int sts_mixer_get_active_voices(sts_mixer_t* mixer) {
int i, active;
for (i = 0, active = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) ++active;
}
return active;
}
int sts_mixer_play_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample, float gain, float pitch, float pan) {
int i;
sts_mixer_voice_t* voice;
i = sts_mixer__find_free_voice(mixer);
if (i >= 0) {
voice = &mixer->voices[i];
voice->gain = gain;
voice->pitch = sts_mixer__clamp(pitch, 0.1f, 10.0f);
voice->pan = sts_mixer__clamp(pan * 0.5f, -0.5f, 0.5f);
voice->position = 0.0f;
voice->sample = sample;
voice->stream = 0;
voice->state = STS_MIXER_VOICE_PLAYING;
}
return i;
}
int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float gain) {
int i;
sts_mixer_voice_t* voice;
i = sts_mixer__find_free_voice(mixer);
if (i >= 0) {
voice = &mixer->voices[i];
voice->gain = gain;
voice->position = 0.0f;
voice->sample = 0;
voice->stream = stream;
voice->state = STS_MIXER_VOICE_STREAMING;
}
return i;
}
void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice) {
if (voice >= 0 && voice < STS_MIXER_VOICES) sts_mixer__reset_voice(mixer, voice);
}
void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample) {
int i;
for (i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].sample == sample) sts_mixer__reset_voice(mixer, i);
}
}
void sts_mixer_stop_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream) {
int i;
for (i = 0; i < STS_MIXER_VOICES; ++i) {
if (mixer->voices[i].stream == stream) sts_mixer__reset_voice(mixer, i);
}
}
void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples) {
sts_mixer_voice_t* voice;
unsigned int i, position;
float left, right, advance, sample;
char* out_8 = (char*)output;
short* out_16 = (short*)output;
int* out_32 = (int*)output;
float* out_float = (float*)output;
// mix all voices
advance = 1.0f / (float)mixer->frequency;
for (; samples > 0; --samples) {
left = right = 0.0f;
for (i = 0; i < STS_MIXER_VOICES; ++i) {
voice = &mixer->voices[i];
if (voice->state == STS_MIXER_VOICE_PLAYING) {
position = (int)voice->position;
if (position < voice->sample->length) {
sample = sts_mixer__clamp_sample(sts_mixer__get_sample(voice->sample, position) * voice->gain);
left += sts_mixer__clamp_sample(sample * (0.5f - voice->pan));
right += sts_mixer__clamp_sample(sample * (0.5f + voice->pan));
voice->position += (float)voice->sample->frequency * advance * voice->pitch;
} else if( voice->sample->next ) { //< @r-lyeh
*voice->sample = *(sts_mixer_sample_t*)voice->sample->next; //< @r-lyeh
voice->position = 0; //< @r-lyeh
} else sts_mixer__reset_voice(mixer, i);
} else if (voice->state == STS_MIXER_VOICE_STREAMING) {
position = ((int)voice->position) * 2;
if (position >= voice->stream->sample.length) {
// buffer empty...refill
voice->stream->callback(&voice->stream->sample, voice->stream->userdata);
voice->position = 0.0f;
position = 0;
}
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
voice->position += (float)voice->stream->sample.frequency * advance;
}
}
// write to buffer.
float _g = mixer->gain; //< @r-lyeh: added master gain
float _127 = 127.0f * _g; //< @r-lyeh: added master gain
float _32767 = 32767.0f * _g; //< @r-lyeh: added master gain
float _2147483647 = 2147483647.0f * _g; //< @r-lyeh: added master gain
left = sts_mixer__clamp_sample(left);
right = sts_mixer__clamp_sample(right);
switch (mixer->audio_format) {
case STS_MIXER_SAMPLE_FORMAT_8:
*out_8++ = (char)(left * _127); //< @r-lyeh: added master gain
*out_8++ = (char)(right * _127); //< @r-lyeh: added master gain
break;
case STS_MIXER_SAMPLE_FORMAT_16:
*out_16++ = (short)(left * _32767); //< @r-lyeh: added master gain
*out_16++ = (short)(right * _32767); //< @r-lyeh: added master gain
break;
case STS_MIXER_SAMPLE_FORMAT_32:
*out_32++ = (int)(left * _2147483647); //< @r-lyeh: added master gain
*out_32++ = (int)(right * _2147483647); //< @r-lyeh: added master gain
break;
case STS_MIXER_SAMPLE_FORMAT_FLOAT:
*out_float++ = left * _g; //< @r-lyeh: added master gain
*out_float++ = right * _g; //< @r-lyeh: added master gain
break;
}
}
}
#endif // STS_MIXER_IMPLEMENTATION
////////////////////////////////////////////////////////////////////////////////
// EXAMPLE
// This is a very simple example loading a stream and a sample using
// dr_flac.h (https://github.com/mackron/dr_libs) and SDL2. You can of course also use stb_vorbis or something similar :)
// Please note how the audio thread of SDL2 will be locked when the mixer state get's modified. This is important!
// Also there's no error checking in the entire example code, so beware.
//
#if 0
#include "SDL.h"
#define DR_FLAC_IMPLEMENTATION
#include "dr_flac.h"
#define STS_MIXER_IMPLEMENTATION
#include "sts_mixer.h"
SDL_AudioDeviceID audio_device = 0;
sts_mixer_t mixer;
// encapsulate drflac and some buffer with the sts_mixer_stream_t
typedef struct {
drflac* flac; // FLAC decoder state
sts_mixer_stream_t stream; // mixer stream
int32_t data[4096*2]; // static sample buffer
} mystream_t;
// SDL2 audio callback
static void audio_callback(void* userdata, Uint8* stream, int len) {
(void)(userdata);
sts_mixer_mix_audio(&mixer, stream, len / (sizeof(int) * 2));
}
// load a sample
static void load_sample(sts_mixer_sample_t* sample, const char *filename) {
drflac* flac = drflac_open_file(filename);
sample->frequency = flac->sampleRate;
sample->audio_format = STS_MIXER_SAMPLE_FORMAT_32;
sample->length = flac->totalSampleCount;
sample->data = malloc(sample->length * sizeof(int32_t));
drflac_read_s32(flac, sample->length, (int32_t*)sample->data);
drflac_close(flac);
}
// the callback to refill the stream data
static void refill_stream(sts_mixer_sample_t* sample, void* userdata) {
mystream_t* stream = (mystream_t*)userdata;
if (drflac_read_s32(stream->flac, sample->length, stream->data) < sample->length) drflac_seek_to_sample(stream->flac, 0);
}
// load a stream
static void load_stream(mystream_t* stream, const char *filename) {
stream->flac = drflac_open_file(filename);
stream->stream.userdata = stream;
stream->stream.callback = refill_stream;
stream->stream.sample.frequency = stream->flac->sampleRate;
stream->stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_32;
stream->stream.sample.length = 4096*2;
stream->stream.sample.data = stream->data;
refill_stream(&stream->stream.sample, stream);
}
// helper to get random [0.0f..1.0f values
static float randf() {
return (float)(rand()) / (float)RAND_MAX;
}
int main(int argc, char *argv[]) {
SDL_AudioSpec want, have;
sts_mixer_sample_t sample;
mystream_t stream;
(void)(argc); (void)(argv);
// init SDL2 + audio
want.format = AUDIO_S32SYS;
want.freq = 44100;
want.channels = 2;
want.userdata = NULL;
want.samples = 4096;
want.callback = audio_callback;
SDL_Init(SDL_INIT_AUDIO);
audio_device = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
// init sts_mixer and load things
sts_mixer_init(&mixer, 44100, STS_MIXER_SAMPLE_FORMAT_32);
load_sample(&sample, "effect.flac");
load_stream(&stream, "music.flac");
// play the stream
sts_mixer_play_stream(&mixer, &stream.stream, 0.7f);
// start audio processing and do a loop for audio effects
SDL_PauseAudioDevice(audio_device, 0);
for (;;) {
// !!!IMPORTANT!!! lock the audio thread before modifying data in the sts_mixer !!!
SDL_LockAudioDevice(audio_device);
// play a sample with random gain, pitch and panning
sts_mixer_play_sample(&mixer, &sample, randf(), 0.5f + randf(), -1.0f + randf() * 2.0f);
// unlock audio thread again
SDL_UnlockAudioDevice(audio_device);
// wait ...
SDL_Delay(76);
}
SDL_PauseAudioDevice(audio_device, 1);
SDL_CloseAudioDevice(audio_device);
SDL_Quit();
return 0;
}
#endif // 0
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/

View File

@ -0,0 +1,290 @@
// https://github.com/BareRose/swrap/blob/master/swrap.h
/*
swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring
rights to this software to the public domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with this software.
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
/*
swrap supports the following three configurations:
#define SWRAP_EXTERN
Default, should be used when using swrap in multiple compilation units within the same project.
#define SWRAP_IMPLEMENTATION
Must be defined in exactly one source file within a project for swrap to be found by the linker.
#define SWRAP_STATIC
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
*/
//include only once
#ifndef SWRAP_H
#define SWRAP_H
//process configuration
#ifdef SWRAP_STATIC
#define SWRAP_IMPLEMENTATION
#define SWDEF static
#else //SWRAP_EXTERN
#define SWDEF extern
#endif
//constants
#define SWRAP_TCP 0
#define SWRAP_UDP 1
#define SWRAP_BIND 0
#define SWRAP_CONNECT 1
#define SWRAP_DEFAULT 0x00
#define SWRAP_NOBLOCK 0x01
#define SWRAP_NODELAY 0x02
//structs
struct swrap_addr {
char data[128]; //enough space to hold any kind of address
};
//function declarations
SWDEF int swrapInit();
SWDEF int swrapSocket(int, int, char, const char*, const char*);
SWDEF void swrapClose(int);
SWDEF void swrapTerminate();
SWDEF int swrapListen(int, int);
SWDEF int swrapAccept(int, struct swrap_addr*);
SWDEF int swrapAddress(int, struct swrap_addr*);
SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t);
SWDEF int swrapSend(int, const char*, size_t);
SWDEF int swrapReceive(int, char*, size_t);
SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t);
SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t);
SWDEF int swrapSelect(int, double);
SWDEF int swrapMultiSelect(int*, size_t, double);
//implementation section
#ifdef SWRAP_IMPLEMENTATION
//includes
#ifdef _WIN32 //windows
#include <ws2tcpip.h>
#else //unix
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <stddef.h> //NULL
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
//general functions
SWDEF int swrapInit () {
//initializes socket functionality, returns 0 on success
#ifdef _WIN32
WSADATA WsaData;
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
#else
return 0;
#endif
}
SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) {
//protocol-agnostically creates a new socket configured according to the given parameters
//sockets have to be created and bound/connected all at once to allow for protocol-agnosticity
//int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
//int: Mode of the socket
// SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server
// SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client
//char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags
// SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking
// SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled
//char*: Host/address as a string, can be IPv4, IPv6, etc...
//char*: Service/port as a string, e.g. "1728" or "http"
//returns socket handle, or -1 on failure
struct addrinfo* result, hint = {
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
AF_UNSPEC, //ai_family
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
0, 0, NULL, NULL, NULL};
//get address info
if (getaddrinfo(host, serv, &hint, &result)) return -1;
//create socket
#ifdef _WIN32
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//convert to int
int sock = wsck;
#else
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) return -1;
#endif
//make sure IPV6_ONLY is disabled
if (result->ai_family == AF_INET6) {
int no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
}
//set TCP_NODELAY if applicable
if (prot == SWRAP_TCP) {
int nodelay = (flags&SWRAP_NODELAY);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
}
//bind if applicable
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
swrapClose(sock);
return -1;
}
//set non-blocking if needed
if (flags&SWRAP_NOBLOCK) {
#ifdef _WIN32
DWORD no_block = 1;
if (ioctlsocket(sock, FIONBIO, &no_block)) {
swrapClose(sock);
return -1;
}
#else
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
swrapClose(sock);
return -1;
}
#endif
}
//connect if applicable (return only relevant if blocking)
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
swrapClose(sock);
return -1;
}
//free address info
freeaddrinfo(result);
//return socket handle
return sock;
}
SWDEF void swrapClose (int sock) {
//closes the given socket
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
SWDEF void swrapTerminate () {
//terminates socket functionality
#ifdef _WIN32
WSACleanup();
#endif
}
//connection functions
SWDEF int swrapListen (int sock, int blog) {
//configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog
//returns 0 on success, non-zero on failure
return listen(sock, blog);
}
SWDEF int swrapAccept (int sock, struct swrap_addr* addr) {
//uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//return new socket
return wsck;
#else
socklen_t addr_size = sizeof(struct swrap_addr);
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
#endif
}
//address functions
SWDEF int swrapAddress (int sock, struct swrap_addr* addr) {
//writes the address the given socket is bound to into given address pointer, useful when automatically assigning port
//returns 0 on success, non-zero on failure
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return getsockname(sock, (struct sockaddr*)addr, &addr_size);
}
SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) {
//writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL
//returns 0 on success, non-zero on failure
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
}
//send/receive functions
SWDEF int swrapSend (int sock, const char* data, size_t data_size) {
//uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapReceive (int sock, char* data, size_t data_size) {
//receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size)
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) {
//uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr));
}
SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) {
//receives data using given socket into given buffer (pointer + size), optionally returning sender's address
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
}
//select functions
SWDEF int swrapSelect (int sock, double timeout) {
//waits either until given socket has new data to receive or given time (in seconds) has passed
//if given socket is -1 an empty select will be performed, which is just a sub-second sleep
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time;
//fd set
FD_ZERO(&set);
if (sock > -1) FD_SET(sock, &set);
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock+1, &set, NULL, NULL, &time);
}
SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) {
//waits either until a socket in given list has new data to receive or given time (in seconds) has passed
//if the given list length is 0 an empty select will be performed, which is just a sub-second sleep
//returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time; int sock_max = -1;
//fd set
FD_ZERO(&set);
for (size_t i = 0; i < socks_size; i++) {
if (socks[i] > sock_max) sock_max = socks[i];
if (socks[i] > -1) FD_SET(socks[i], &set);
}
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock_max+1, &set, NULL, NULL, &time);
}
#endif //SWRAP_IMPLEMENTATION
#endif //SWRAP_H

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