cleanup
parent
7465c37c03
commit
3195aae777
|
@ -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
|
|
@ -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
|
|
@ -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!
|
|
@ -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
|
|
@ -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.
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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\")" );
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -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
|
|
@ -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)) {}//
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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++) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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\")" );
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash 2>nul || goto :windows
|
||||||
|
|
||||||
|
sh ../MAKE.bat demos
|
||||||
|
|
||||||
|
exit
|
||||||
|
|
||||||
|
:windows
|
||||||
|
|
||||||
|
pushd ..
|
||||||
|
call MAKE.bat demos %*
|
||||||
|
popd
|
Binary file not shown.
|
@ -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 );
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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.);
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
|
@ -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
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
void mainImage( out vec4 fragColor, in vec2 fragCoord );
|
||||||
|
void main() {
|
||||||
|
mainImage(fragColor, texcoord.xy * iResolution);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
in vec3 position;
|
||||||
|
in vec2 texcoord;
|
||||||
|
out vec2 Texcoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 1.0);
|
||||||
|
Texcoord = texcoord;
|
||||||
|
}
|
|
@ -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
|
@ -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
|
|
@ -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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gilbert–Johnson–Keerthi (GJK) 3D distance algorithm
|
||||||
|
The Gilbert–Johnson–Keerthi (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
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
@ -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();
|
||||||
|
*/
|
|
@ -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
|
@ -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.
|
||||||
|
*/
|
|
@ -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
|
@ -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
|
@ -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/>
|
||||||
|
*/
|
|
@ -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
Loading…
Reference in New Issue