summaryrefslogtreecommitdiff
path: root/src/extras
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
committerclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
commit9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686 (patch)
treea84756b82513739a2672db3a1f0ec579db6d18ff /src/extras
downloadre3-miami.tar.gz
re3-miami.zip
Import Cai1Hsu/re3 @ miami (reVC / GTA:VC decompilation)HEADmiami
Snapshot import (no upstream history) into git.ancap.in.ua/claude, per @lzcnt. Source: https://github.com/Cai1Hsu/re3 branch miami.
Diffstat (limited to 'src/extras')
-rw-r--r--src/extras/GitSHA1.cpp.in2
-rw-r--r--src/extras/GitSHA1.h1
-rw-r--r--src/extras/arrow.inc16
-rw-r--r--src/extras/cursor.inc16
-rw-r--r--src/extras/custompipes.cpp543
-rw-r--r--src/extras/custompipes.h145
-rw-r--r--src/extras/custompipes_d3d9.cpp724
-rw-r--r--src/extras/custompipes_gl.cpp738
-rw-r--r--src/extras/debugmenu.cpp1312
-rw-r--r--src/extras/debugmenu.h90
-rw-r--r--src/extras/frontendoption.cpp170
-rw-r--r--src/extras/frontendoption.h86
-rw-r--r--src/extras/ini_parser.hpp333
-rw-r--r--src/extras/postfx.cpp491
-rw-r--r--src/extras/postfx.h46
-rw-r--r--src/extras/re3_inttypes.h216
-rw-r--r--src/extras/screendroplets.cpp817
-rw-r--r--src/extras/screendroplets.h78
-rw-r--r--src/extras/shaders/colourfilterVC.frag27
-rw-r--r--src/extras/shaders/colourfilterVC_PS.hlsl23
-rw-r--r--src/extras/shaders/contrast.frag19
-rw-r--r--src/extras/shaders/contrastPS.hlsl21
-rw-r--r--src/extras/shaders/default_UV2.vert25
-rw-r--r--src/extras/shaders/default_UV2_VS.hlsl54
-rw-r--r--src/extras/shaders/im2d.vert19
-rw-r--r--src/extras/shaders/im2d_UV2.vert21
-rw-r--r--src/extras/shaders/lighting.h44
-rw-r--r--src/extras/shaders/make_glsl.sh9
-rw-r--r--src/extras/shaders/make_hlsl.cmd3
-rw-r--r--src/extras/shaders/makeinc_glsl.sh6
-rw-r--r--src/extras/shaders/makeinc_hlsl.sh6
-rw-r--r--src/extras/shaders/neoGloss.frag26
-rw-r--r--src/extras/shaders/neoGloss.vert25
-rw-r--r--src/extras/shaders/neoGloss_PS.hlsl20
-rw-r--r--src/extras/shaders/neoGloss_VS.hlsl35
-rw-r--r--src/extras/shaders/neoRim.vert34
-rw-r--r--src/extras/shaders/neoRimSkin.vert43
-rw-r--r--src/extras/shaders/neoRimSkin_VS.hlsl73
-rw-r--r--src/extras/shaders/neoRim_VS.hlsl61
-rw-r--r--src/extras/shaders/neoVehicle.frag29
-rw-r--r--src/extras/shaders/neoVehicle.vert51
-rw-r--r--src/extras/shaders/neoVehicle_PS.hlsl34
-rw-r--r--src/extras/shaders/neoVehicle_VS.hlsl64
-rw-r--r--src/extras/shaders/neoWorldVC.frag26
-rw-r--r--src/extras/shaders/neoWorldVC_PS.hlsl25
-rw-r--r--src/extras/shaders/obj/contrastPS.csobin0 -> 344 bytes
-rw-r--r--src/extras/shaders/obj/contrastPS.inc31
-rw-r--r--src/extras/shaders/obj/contrast_frag.inc21
-rw-r--r--src/extras/shaders/obj/default_UV2_VS.csobin0 -> 628 bytes
-rw-r--r--src/extras/shaders/obj/default_UV2_VS.inc55
-rw-r--r--src/extras/shaders/obj/default_UV2_vert.inc27
-rw-r--r--src/extras/shaders/obj/im2d_UV2_vert.inc23
-rw-r--r--src/extras/shaders/obj/im2d_vert.inc21
-rw-r--r--src/extras/shaders/obj/neoGloss_PS.csobin0 -> 444 bytes
-rw-r--r--src/extras/shaders/obj/neoGloss_PS.inc39
-rw-r--r--src/extras/shaders/obj/neoGloss_VS.csobin0 -> 764 bytes
-rw-r--r--src/extras/shaders/obj/neoGloss_VS.inc66
-rw-r--r--src/extras/shaders/obj/neoGloss_frag.inc28
-rw-r--r--src/extras/shaders/obj/neoGloss_vert.inc27
-rw-r--r--src/extras/shaders/obj/neoRimSkin_VS.csobin0 -> 2404 bytes
-rw-r--r--src/extras/shaders/obj/neoRimSkin_VS.inc203
-rw-r--r--src/extras/shaders/obj/neoRimSkin_vert.inc45
-rw-r--r--src/extras/shaders/obj/neoRim_VS.csobin0 -> 1384 bytes
-rw-r--r--src/extras/shaders/obj/neoRim_VS.inc118
-rw-r--r--src/extras/shaders/obj/neoRim_vert.inc36
-rw-r--r--src/extras/shaders/obj/neoVehicle_PS.csobin0 -> 476 bytes
-rw-r--r--src/extras/shaders/obj/neoVehicle_PS.inc42
-rw-r--r--src/extras/shaders/obj/neoVehicle_VS.csobin0 -> 1896 bytes
-rw-r--r--src/extras/shaders/obj/neoVehicle_VS.inc160
-rw-r--r--src/extras/shaders/obj/neoVehicle_frag.inc31
-rw-r--r--src/extras/shaders/obj/neoVehicle_vert.inc53
-rw-r--r--src/extras/shaders/obj/screenDroplet_PS.csobin0 -> 324 bytes
-rw-r--r--src/extras/shaders/obj/screenDroplet_PS.inc29
-rw-r--r--src/extras/shaders/obj/screenDroplet_frag.inc20
-rw-r--r--src/extras/shaders/obj/simple_frag.inc19
-rw-r--r--src/extras/shaders/screenDroplet.frag18
-rw-r--r--src/extras/shaders/screenDroplet_PS.hlsl17
-rw-r--r--src/extras/shaders/simple.frag17
-rw-r--r--src/extras/shaders/standardConstants.h28
79 files changed, 7821 insertions, 0 deletions
diff --git a/src/extras/GitSHA1.cpp.in b/src/extras/GitSHA1.cpp.in
new file mode 100644
index 0000000..6168dc7
--- /dev/null
+++ b/src/extras/GitSHA1.cpp.in
@@ -0,0 +1,2 @@
+#define GIT_SHA1 "@GIT_SHA1@"
+const char* g_GIT_SHA1 = GIT_SHA1;
diff --git a/src/extras/GitSHA1.h b/src/extras/GitSHA1.h
new file mode 100644
index 0000000..359bfaf
--- /dev/null
+++ b/src/extras/GitSHA1.h
@@ -0,0 +1 @@
+extern const char* g_GIT_SHA1; \ No newline at end of file
diff --git a/src/extras/arrow.inc b/src/extras/arrow.inc
new file mode 100644
index 0000000..8ea7828
--- /dev/null
+++ b/src/extras/arrow.inc
@@ -0,0 +1,16 @@
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 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, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 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, 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,
diff --git a/src/extras/cursor.inc b/src/extras/cursor.inc
new file mode 100644
index 0000000..e8afd39
--- /dev/null
+++ b/src/extras/cursor.inc
@@ -0,0 +1,16 @@
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
diff --git a/src/extras/custompipes.cpp b/src/extras/custompipes.cpp
new file mode 100644
index 0000000..a485138
--- /dev/null
+++ b/src/extras/custompipes.cpp
@@ -0,0 +1,543 @@
+#define WITHD3D
+#include "common.h"
+
+#ifdef EXTENDED_PIPELINES
+
+#include "main.h"
+#include "RwHelper.h"
+#include "Lights.h"
+#include "Timecycle.h"
+#include "FileMgr.h"
+#include "Clock.h"
+#include "Weather.h"
+#include "TxdStore.h"
+#include "Renderer.h"
+#include "World.h"
+#include "custompipes.h"
+
+#ifndef LIBRW
+#error "Need librw for EXTENDED_PIPELINES"
+#endif
+
+namespace CustomPipes {
+
+rw::int32 CustomMatOffset;
+
+void*
+CustomMatCtor(void *object, int32, int32)
+{
+ CustomMatExt *ext = GetCustomMatExt((rw::Material*)object);
+ ext->glossTex = nil;
+ ext->haveGloss = false;
+ return object;
+}
+
+void*
+CustomMatCopy(void *dst, void *src, int32, int32)
+{
+ CustomMatExt *srcext = GetCustomMatExt((rw::Material*)src);
+ CustomMatExt *dstext = GetCustomMatExt((rw::Material*)dst);
+ dstext->glossTex = srcext->glossTex;
+ dstext->haveGloss = srcext->haveGloss;
+ return dst;
+}
+
+
+
+rw::TexDictionary *neoTxd;
+
+bool bRenderingEnvMap;
+int32 EnvMapSize = 128;
+rw::Camera *EnvMapCam;
+rw::Texture *EnvMapTex;
+rw::Texture *EnvMaskTex;
+static rw::RWDEVICE::Im2DVertex EnvScreenQuad[4];
+static int16 QuadIndices[6] = { 0, 1, 2, 0, 2, 3 };
+
+static rw::Camera*
+CreateEnvMapCam(rw::World *world)
+{
+ rw::Raster *fbuf = rw::Raster::create(EnvMapSize, EnvMapSize, 0, rw::Raster::CAMERATEXTURE);
+ if(fbuf){
+ rw::Raster *zbuf = rw::Raster::create(EnvMapSize, EnvMapSize, 0, rw::Raster::ZBUFFER);
+ if(zbuf){
+ rw::Frame *frame = rw::Frame::create();
+ if(frame){
+ rw::Camera *cam = rw::Camera::create();
+ if(cam){
+ cam->frameBuffer = fbuf;
+ cam->zBuffer = zbuf;
+ cam->setFrame(frame);
+ cam->setNearPlane(0.1f);
+ cam->setFarPlane(250.0f);
+ rw::V2d vw = { 2.0f, 2.0f };
+ cam->setViewWindow(&vw);
+ world->addCamera(cam);
+ EnvMapTex = rw::Texture::create(fbuf);
+ EnvMapTex->setFilter(rw::Texture::LINEAR);
+
+ frame->matrix.right.x = -1.0f;
+ frame->matrix.up.y = -1.0f;
+ frame->matrix.update();
+ return cam;
+ }
+ frame->destroy();
+ }
+ zbuf->destroy();
+ }
+ fbuf->destroy();
+ }
+ return nil;
+}
+
+static void
+DestroyCam(rw::Camera *cam)
+{
+ if(cam == nil)
+ return;
+ if(cam->frameBuffer){
+ cam->frameBuffer->destroy();
+ cam->frameBuffer = nil;
+ }
+ if(cam->zBuffer){
+ cam->zBuffer->destroy();
+ cam->zBuffer = nil;
+ }
+ rw::Frame *f = cam->getFrame();
+ if(f){
+ cam->setFrame(nil);
+ f->destroy();
+ }
+ cam->world->removeCamera(cam);
+ cam->destroy();
+}
+
+void
+RenderEnvMapScene(void)
+{
+ CRenderer::RenderRoads();
+ CRenderer::RenderEverythingBarRoads();
+ CRenderer::RenderFadingInEntities();
+}
+
+void
+EnvMapRender(void)
+{
+ if(VehiclePipeSwitch != VEHICLEPIPE_NEO)
+ return;
+
+ RwCameraEndUpdate(Scene.camera);
+
+ // Neo does this differently, but i'm not quite convinced it's much better
+ rw::V3d camPos = FindPlayerCoors();
+ EnvMapCam->getFrame()->matrix.pos = camPos;
+ EnvMapCam->getFrame()->transform(&EnvMapCam->getFrame()->matrix, rw::COMBINEREPLACE);
+
+ rw::RGBA skycol;
+ skycol.red = CTimeCycle::GetSkyBottomRed();
+ skycol.green = CTimeCycle::GetSkyBottomGreen();
+ skycol.blue = CTimeCycle::GetSkyBottomBlue();
+ skycol.alpha = 255;
+ EnvMapCam->clear(&skycol, rwCAMERACLEARZ|rwCAMERACLEARIMAGE);
+ RwCameraBeginUpdate(EnvMapCam);
+ bRenderingEnvMap = true;
+ RenderEnvMapScene();
+ bRenderingEnvMap = false;
+
+ if(EnvMaskTex){
+ rw::SetRenderState(rw::VERTEXALPHA, TRUE);
+ rw::SetRenderState(rw::SRCBLEND, rw::BLENDZERO);
+ rw::SetRenderState(rw::DESTBLEND, rw::BLENDSRCCOLOR);
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, EnvMaskTex->raster);
+ rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, EnvScreenQuad, 4, QuadIndices, 6);
+ rw::SetRenderState(rw::SRCBLEND, rw::BLENDSRCALPHA);
+ rw::SetRenderState(rw::DESTBLEND, rw::BLENDINVSRCALPHA);
+ }
+ RwCameraEndUpdate(EnvMapCam);
+
+
+ RwCameraBeginUpdate(Scene.camera);
+
+ // debug env map
+// rw::SetRenderStatePtr(rw::TEXTURERASTER, EnvMapTex->raster);
+// rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, EnvScreenQuad, 4, QuadIndices, 6);
+}
+
+static void
+EnvMapInit(void)
+{
+ if(neoTxd)
+ EnvMaskTex = neoTxd->find("CarReflectionMask");
+
+ EnvMapCam = CreateEnvMapCam(Scene.world);
+
+ int width = EnvMapCam->frameBuffer->width;
+ int height = EnvMapCam->frameBuffer->height;
+ float screenZ = RwIm2DGetNearScreenZ();
+ float recipZ = 1.0f/EnvMapCam->nearPlane;
+
+ EnvScreenQuad[0].setScreenX(0.0f);
+ EnvScreenQuad[0].setScreenY(0.0f);
+ EnvScreenQuad[0].setScreenZ(screenZ);
+ EnvScreenQuad[0].setCameraZ(EnvMapCam->nearPlane);
+ EnvScreenQuad[0].setRecipCameraZ(recipZ);
+ EnvScreenQuad[0].setColor(255, 255, 255, 255);
+ EnvScreenQuad[0].setU(0.0f, recipZ);
+ EnvScreenQuad[0].setV(0.0f, recipZ);
+
+ EnvScreenQuad[1].setScreenX(0.0f);
+ EnvScreenQuad[1].setScreenY(height);
+ EnvScreenQuad[1].setScreenZ(screenZ);
+ EnvScreenQuad[1].setCameraZ(EnvMapCam->nearPlane);
+ EnvScreenQuad[1].setRecipCameraZ(recipZ);
+ EnvScreenQuad[1].setColor(255, 255, 255, 255);
+ EnvScreenQuad[1].setU(0.0f, recipZ);
+ EnvScreenQuad[1].setV(1.0f, recipZ);
+
+ EnvScreenQuad[2].setScreenX(width);
+ EnvScreenQuad[2].setScreenY(height);
+ EnvScreenQuad[2].setScreenZ(screenZ);
+ EnvScreenQuad[2].setCameraZ(EnvMapCam->nearPlane);
+ EnvScreenQuad[2].setRecipCameraZ(recipZ);
+ EnvScreenQuad[2].setColor(255, 255, 255, 255);
+ EnvScreenQuad[2].setU(1.0f, recipZ);
+ EnvScreenQuad[2].setV(1.0f, recipZ);
+
+ EnvScreenQuad[3].setScreenX(width);
+ EnvScreenQuad[3].setScreenY(0.0f);
+ EnvScreenQuad[3].setScreenZ(screenZ);
+ EnvScreenQuad[3].setCameraZ(EnvMapCam->nearPlane);
+ EnvScreenQuad[3].setRecipCameraZ(recipZ);
+ EnvScreenQuad[3].setColor(255, 255, 255, 255);
+ EnvScreenQuad[3].setU(1.0f, recipZ);
+ EnvScreenQuad[3].setV(0.0f, recipZ);
+}
+
+static void
+EnvMapShutdown(void)
+{
+ EnvMapTex->raster = nil;
+ EnvMapTex->destroy();
+ EnvMapTex = nil;
+ DestroyCam(EnvMapCam);
+ EnvMapCam = nil;
+}
+
+/*
+ * Tweak values
+ */
+
+#define INTERP_SETUP \
+ int h1 = CClock::GetHours(); \
+ int h2 = (h1+1)%24; \
+ int w1 = CWeather::OldWeatherType; \
+ int w2 = CWeather::NewWeatherType; \
+ float timeInterp = (CClock::GetSeconds()/60.0f + CClock::GetMinutes())/60.0f; \
+ float c0 = (1.0f-timeInterp)*(1.0f-CWeather::InterpolationValue); \
+ float c1 = timeInterp*(1.0f-CWeather::InterpolationValue); \
+ float c2 = (1.0f-timeInterp)*CWeather::InterpolationValue; \
+ float c3 = timeInterp*CWeather::InterpolationValue;
+#define INTERP(v) v[h1][w1]*c0 + v[h2][w1]*c1 + v[h1][w2]*c2 + v[h2][w2]*c3;
+#define INTERPF(v,f) v[h1][w1].f*c0 + v[h2][w1].f*c1 + v[h1][w2].f*c2 + v[h2][w2].f*c3;
+
+InterpolatedFloat::InterpolatedFloat(float init)
+{
+ curInterpolator = 61; // compared against second
+ for(int h = 0; h < 24; h++)
+ for(int w = 0; w < NUMWEATHERS; w++)
+ data[h][w] = init;
+}
+
+void
+InterpolatedFloat::Read(char *s, int line, int field)
+{
+ sscanf(s, "%f", &data[line][field]);
+}
+
+float
+InterpolatedFloat::Get(void)
+{
+ if(curInterpolator != CClock::GetSeconds()){
+ INTERP_SETUP
+ curVal = INTERP(data);
+ curInterpolator = CClock::GetSeconds();
+ }
+ return curVal;
+}
+
+InterpolatedColor::InterpolatedColor(const Color &init)
+{
+ curInterpolator = 61; // compared against second
+ for(int h = 0; h < 24; h++)
+ for(int w = 0; w < NUMWEATHERS; w++)
+ data[h][w] = init;
+}
+
+void
+InterpolatedColor::Read(char *s, int line, int field)
+{
+ int r, g, b, a;
+ sscanf(s, "%i, %i, %i, %i", &r, &g, &b, &a);
+ data[line][field] = Color(r/255.0f, g/255.0f, b/255.0f, a/255.0f);
+}
+
+Color
+InterpolatedColor::Get(void)
+{
+ if(curInterpolator != CClock::GetSeconds()){
+ INTERP_SETUP
+ curVal.r = INTERPF(data, r);
+ curVal.g = INTERPF(data, g);
+ curVal.b = INTERPF(data, b);
+ curVal.a = INTERPF(data, a);
+ curInterpolator = CClock::GetSeconds();
+ }
+ return curVal;
+}
+
+void
+InterpolatedLight::Read(char *s, int line, int field)
+{
+ int r, g, b, a;
+ sscanf(s, "%i, %i, %i, %i", &r, &g, &b, &a);
+ data[line][field] = Color(r/255.0f, g/255.0f, b/255.0f, a/100.0f);
+}
+
+char*
+ReadTweakValueTable(char *fp, InterpolatedValue &interp)
+{
+ char buf[24], *p;
+ int c;
+ int line, field;
+
+ line = 0;
+ c = *fp++;
+ while(c != '\0' && line < 24){
+ field = 0;
+ if(c != '\0' && c != '#'){
+ while(c != '\0' && c != '\n' && field < NUMWEATHERS){
+ p = buf;
+ while(c != '\0' && c == '\t')
+ c = *fp++;
+ *p++ = c;
+ while(c = *fp++, c != '\0' && c != '\t' && c != '\n')
+ *p++ = c;
+ *p++ = '\0';
+ interp.Read(buf, line, field);
+ field++;
+ }
+ line++;
+ }
+ while(c != '\0' && c != '\n')
+ c = *fp++;
+ c = *fp++;
+ }
+ return fp-1;
+}
+
+
+
+/*
+ * Neo Vehicle pipe
+ */
+
+int32 VehiclePipeSwitch = VEHICLEPIPE_MATFX;
+float VehicleShininess = 1.0f;
+float VehicleSpecularity = 1.0f;
+InterpolatedFloat Fresnel(0.4f);
+InterpolatedFloat Power(18.0f);
+InterpolatedLight DiffColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
+InterpolatedLight SpecColor(Color(0.7f, 0.7f, 0.7f, 1.0f));
+rw::ObjPipeline *vehiclePipe;
+
+void
+AttachVehiclePipe(rw::Atomic *atomic)
+{
+ atomic->pipeline = vehiclePipe;
+}
+
+void
+AttachVehiclePipe(rw::Clump *clump)
+{
+ FORLIST(lnk, clump->atomics)
+ AttachVehiclePipe(rw::Atomic::fromClump(lnk));
+}
+
+
+
+/*
+ * Neo World pipe
+ */
+
+bool LightmapEnable;
+float LightmapMult = 1.0f;
+InterpolatedFloat WorldLightmapBlend(1.0f);
+rw::ObjPipeline *worldPipe;
+
+void
+AttachWorldPipe(rw::Atomic *atomic)
+{
+ atomic->pipeline = worldPipe;
+}
+
+void
+AttachWorldPipe(rw::Clump *clump)
+{
+ FORLIST(lnk, clump->atomics)
+ AttachWorldPipe(rw::Atomic::fromClump(lnk));
+}
+
+
+
+
+/*
+ * Neo Gloss pipe
+ */
+
+bool GlossEnable;
+float GlossMult = 1.0f;
+rw::ObjPipeline *glossPipe;
+
+rw::Texture*
+GetGlossTex(rw::Material *mat)
+{
+ if(neoTxd == nil)
+ return nil;
+ CustomMatExt *ext = GetCustomMatExt(mat);
+ if(!ext->haveGloss){
+ char glossname[128];
+ strcpy(glossname, mat->texture->name);
+ strcat(glossname, "_gloss");
+ ext->glossTex = neoTxd->find(glossname);
+ ext->haveGloss = true;
+ }
+ return ext->glossTex;
+}
+
+void
+AttachGlossPipe(rw::Atomic *atomic)
+{
+ atomic->pipeline = glossPipe;
+}
+
+void
+AttachGlossPipe(rw::Clump *clump)
+{
+ FORLIST(lnk, clump->atomics)
+ AttachWorldPipe(rw::Atomic::fromClump(lnk));
+}
+
+
+
+/*
+ * Neo Rim pipes
+ */
+
+bool RimlightEnable;
+float RimlightMult = 1.0f;
+InterpolatedColor RampStart(Color(0.0f, 0.0f, 0.0f, 1.0f));
+InterpolatedColor RampEnd(Color(1.0f, 1.0f, 1.0f, 1.0f));
+InterpolatedFloat Offset(0.5f);
+InterpolatedFloat Scale(1.5f);
+InterpolatedFloat Scaling(2.0f);
+rw::ObjPipeline *rimPipe;
+rw::ObjPipeline *rimSkinPipe;
+
+void
+AttachRimPipe(rw::Atomic *atomic)
+{
+ if(rw::Skin::get(atomic->geometry))
+ atomic->pipeline = rimSkinPipe;
+ else
+ atomic->pipeline = rimPipe;
+}
+
+void
+AttachRimPipe(rw::Clump *clump)
+{
+ FORLIST(lnk, clump->atomics)
+ AttachRimPipe(rw::Atomic::fromClump(lnk));
+}
+
+/*
+ * High level stuff
+ */
+
+void
+CustomPipeInit(void)
+{
+ RwStream *stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "neo/neo.txd");
+ if(stream == nil)
+ printf("Error: couldn't open 'neo/neo.txd'\n");
+ else{
+ if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil))
+ neoTxd = RwTexDictionaryGtaStreamRead(stream);
+ RwStreamClose(stream, nil);
+ }
+
+ EnvMapInit();
+
+ CreateVehiclePipe();
+ CreateWorldPipe();
+ CreateGlossPipe();
+ CreateRimLightPipes();
+}
+
+void
+CustomPipeShutdown(void)
+{
+ DestroyVehiclePipe();
+ DestroyWorldPipe();
+ DestroyGlossPipe();
+ DestroyRimLightPipes();
+
+ EnvMapShutdown();
+
+ if(neoTxd){
+ neoTxd->destroy();
+ neoTxd = nil;
+ }
+}
+
+void
+CustomPipeRegister(void)
+{
+#ifdef RW_OPENGL
+ CustomPipeRegisterGL();
+#endif
+
+ CustomMatOffset = rw::Material::registerPlugin(sizeof(CustomMatExt), MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x80),
+ CustomMatCtor, nil, CustomMatCopy);
+}
+
+
+// Load textures from generic as fallback
+
+rw::TexDictionary *genericTxd;
+rw::Texture *(*defaultFindCB)(const char *name);
+
+static rw::Texture*
+customFindCB(const char *name)
+{
+ rw::Texture *res = defaultFindCB(name);
+ if(res == nil)
+ res = genericTxd->find(name);
+ return res;
+}
+
+void
+SetTxdFindCallback(void)
+{
+ int slot = CTxdStore::FindTxdSlot("generic");
+ CTxdStore::AddRef(slot);
+ // TODO: function for this
+ genericTxd = CTxdStore::GetSlot(slot)->texDict;
+ assert(genericTxd);
+ if(defaultFindCB == nil)
+ defaultFindCB = rw::Texture::findCB;
+ rw::Texture::findCB = customFindCB;
+}
+
+}
+
+#endif
diff --git a/src/extras/custompipes.h b/src/extras/custompipes.h
new file mode 100644
index 0000000..7ad239f
--- /dev/null
+++ b/src/extras/custompipes.h
@@ -0,0 +1,145 @@
+#pragma once
+
+#ifdef LIBRW
+#ifdef EXTENDED_PIPELINES
+
+namespace CustomPipes {
+
+
+extern rw::TexDictionary *neoTxd;
+
+struct CustomMatExt
+{
+ rw::Texture *glossTex;
+ bool haveGloss;
+};
+extern rw::int32 CustomMatOffset;
+inline CustomMatExt *GetCustomMatExt(rw::Material *mat) {
+ return PLUGINOFFSET(CustomMatExt, mat, CustomMatOffset);
+}
+
+
+struct Color
+{
+ float r, g, b, a;
+ Color(void) {}
+ Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {}
+};
+
+class InterpolatedValue
+{
+public:
+ virtual void Read(char *s, int line, int field) = 0;
+};
+
+class InterpolatedFloat : public InterpolatedValue
+{
+public:
+ float data[24][NUMWEATHERS];
+ float curInterpolator;
+ float curVal;
+
+ InterpolatedFloat(float init);
+ void Read(char *s, int line, int field);
+ float Get(void);
+};
+
+class InterpolatedColor : public InterpolatedValue
+{
+public:
+ Color data[24][NUMWEATHERS];
+ float curInterpolator;
+ Color curVal;
+
+ InterpolatedColor(const Color &init);
+ void Read(char *s, int line, int field);
+ Color Get(void);
+};
+
+class InterpolatedLight : public InterpolatedColor
+{
+public:
+ InterpolatedLight(const Color &init) : InterpolatedColor(init) {}
+ void Read(char *s, int line, int field);
+};
+
+char *ReadTweakValueTable(char *fp, InterpolatedValue &interp);
+
+
+
+
+
+void CustomPipeRegister(void);
+void CustomPipeRegisterGL(void);
+void CustomPipeInit(void);
+void CustomPipeShutdown(void);
+void SetTxdFindCallback(void);
+
+extern bool bRenderingEnvMap;
+extern int32 EnvMapSize;
+extern rw::Camera *EnvMapCam;
+extern rw::Texture *EnvMapTex;
+extern rw::Texture *EnvMaskTex;
+void EnvMapRender(void);
+
+enum {
+ VEHICLEPIPE_MATFX,
+ VEHICLEPIPE_NEO
+};
+extern int32 VehiclePipeSwitch;
+extern float VehicleShininess;
+extern float VehicleSpecularity;
+extern InterpolatedFloat Fresnel;
+extern InterpolatedFloat Power;
+extern InterpolatedLight DiffColor;
+extern InterpolatedLight SpecColor;
+extern rw::ObjPipeline *vehiclePipe;
+void CreateVehiclePipe(void);
+void DestroyVehiclePipe(void);
+void AttachVehiclePipe(rw::Atomic *atomic);
+void AttachVehiclePipe(rw::Clump *clump);
+
+extern bool LightmapEnable;
+extern float LightmapMult;
+extern InterpolatedFloat WorldLightmapBlend;
+extern rw::ObjPipeline *worldPipe;
+void CreateWorldPipe(void);
+void DestroyWorldPipe(void);
+void AttachWorldPipe(rw::Atomic *atomic);
+void AttachWorldPipe(rw::Clump *clump);
+
+extern bool GlossEnable;
+extern float GlossMult;
+extern rw::ObjPipeline *glossPipe;
+void CreateGlossPipe(void);
+void DestroyGlossPipe(void);
+void AttachGlossPipe(rw::Atomic *atomic);
+void AttachGlossPipe(rw::Clump *clump);
+rw::Texture *GetGlossTex(rw::Material *mat);
+
+extern bool RimlightEnable;
+extern float RimlightMult;
+extern InterpolatedColor RampStart;
+extern InterpolatedColor RampEnd;
+extern InterpolatedFloat Offset;
+extern InterpolatedFloat Scale;
+extern InterpolatedFloat Scaling;
+extern rw::ObjPipeline *rimPipe;
+extern rw::ObjPipeline *rimSkinPipe;
+void CreateRimLightPipes(void);
+void DestroyRimLightPipes(void);
+void AttachRimPipe(rw::Atomic *atomic);
+void AttachRimPipe(rw::Clump *clump);
+
+}
+
+#endif
+
+namespace WorldRender{
+extern int numBlendInsts[3];
+void AtomicFirstPass(RpAtomic *atomic, int pass);
+void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha);
+void RenderBlendPass(int pass);
+}
+
+#endif
diff --git a/src/extras/custompipes_d3d9.cpp b/src/extras/custompipes_d3d9.cpp
new file mode 100644
index 0000000..3ad824e
--- /dev/null
+++ b/src/extras/custompipes_d3d9.cpp
@@ -0,0 +1,724 @@
+#define WITHD3D
+#include "common.h"
+
+#ifdef RW_D3D9
+#include "main.h"
+#include "RwHelper.h"
+#include "Lights.h"
+#include "Timecycle.h"
+#include "FileMgr.h"
+#include "Clock.h"
+#include "Weather.h"
+#include "TxdStore.h"
+#include "Renderer.h"
+#include "World.h"
+#include "custompipes.h"
+
+#ifdef EXTENDED_PIPELINES
+
+#ifndef LIBRW
+#error "Need librw for EXTENDED_PIPELINES"
+#endif
+
+extern RwTexture *gpWhiteTexture; // from vehicle model info
+
+namespace CustomPipes {
+
+enum {
+ // rim pipe
+ VSLOC_boneMatrices = rw::d3d::VSLOC_afterLights,
+ VSLOC_viewVec = VSLOC_boneMatrices + 64*3,
+ VSLOC_rampStart,
+ VSLOC_rampEnd,
+ VSLOC_rimData,
+
+ // gloss pipe
+ VSLOC_eye = rw::d3d::VSLOC_afterLights,
+
+ VSLOC_reflProps,
+ VSLOC_specLights
+};
+
+/*
+ * Neo Vehicle pipe
+ */
+
+static void *neoVehicle_VS;
+static void *neoVehicle_PS;
+
+void
+uploadSpecLights(void)
+{
+ struct VsLight {
+ rw::RGBAf color;
+ float pos[4]; // unused
+ rw::V3d dir;
+ float power;
+ } specLights[1 + NUMEXTRADIRECTIONALS];
+ memset(specLights, 0, sizeof(specLights));
+ for(int i = 0; i < 1+NUMEXTRADIRECTIONALS; i++)
+ specLights[i].power = 1.0f;
+ float power = Power.Get();
+ Color speccol = SpecColor.Get();
+ specLights[0].color.red = speccol.r;
+ specLights[0].color.green = speccol.g;
+ specLights[0].color.blue = speccol.b;
+ specLights[0].dir = pDirect->getFrame()->getLTM()->at;
+ specLights[0].power = power;
+ for(int i = 0; i < NUMEXTRADIRECTIONALS; i++){
+ if(pExtraDirectionals[i]->getFlags() & rw::Light::LIGHTATOMICS){
+ specLights[1+i].color = pExtraDirectionals[i]->color;
+ specLights[1+i].dir = pExtraDirectionals[i]->getFrame()->getLTM()->at;
+ specLights[1+i].power = power*2.0f;
+ }
+ }
+ rw::d3d::d3ddevice->SetVertexShaderConstantF(VSLOC_specLights, (float*)&specLights, 3*(1 + NUMEXTRADIRECTIONALS));
+}
+
+void
+vehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ // TODO: make this less of a kludge
+ if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){
+ matFXGlobals.pipelines[rw::platform]->render(atomic);
+ return;
+ }
+
+ int vsBits;
+ rw::uint32 flags = atomic->geometry->flags;
+ setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride);
+ setIndices(header->indexBuffer);
+ setVertexDeclaration(header->vertexDeclaration);
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadSpecLights();
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+ setVertexShader(neoVehicle_VS);
+
+ V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_eye, (float*)&eyePos, 1);
+
+ float reflProps[4];
+ reflProps[0] = Fresnel.Get();
+ reflProps[1] = SpecColor.Get().a;
+
+ d3d::setTexture(1, EnvMapTex);
+
+ SetRenderState(SRCBLEND, BLENDONE);
+
+ InstanceData *inst = header->inst;
+ for(rw::uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ reflProps[2] = m->surfaceProps.specular * VehicleShininess;
+ reflProps[3] = m->surfaceProps.specular == 0.0f ? 0.0f : VehicleSpecularity;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_reflProps, reflProps, 1);
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ if(m->texture)
+ d3d::setTexture(0, m->texture);
+ else
+ d3d::setTexture(0, gpWhiteTexture);
+ setPixelShader(neoVehicle_PS);
+
+ drawInst(header, inst);
+ inst++;
+ }
+
+ d3d::setTexture(1, nil);
+
+ SetRenderState(SRCBLEND, BLENDSRCALPHA);
+}
+
+void
+CreateVehiclePipe(void)
+{
+ if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0)
+ printf("Error: couldn't open 'neo/carTweakingTable.dat'\n");
+ else{
+ char *fp = (char*)work_buff;
+ fp = ReadTweakValueTable(fp, Fresnel);
+ fp = ReadTweakValueTable(fp, Power);
+ fp = ReadTweakValueTable(fp, DiffColor);
+ fp = ReadTweakValueTable(fp, SpecColor);
+ }
+
+#include "shaders/obj/neoVehicle_VS.inc"
+ neoVehicle_VS = rw::d3d::createVertexShader(neoVehicle_VS_cso);
+ assert(neoVehicle_VS);
+
+#include "shaders/obj/neoVehicle_PS.inc"
+ neoVehicle_PS = rw::d3d::createPixelShader(neoVehicle_PS_cso);
+ assert(neoVehicle_PS);
+
+
+ rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create();
+ pipe->instanceCB = rw::d3d9::defaultInstanceCB;
+ pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB;
+ pipe->renderCB = vehicleRenderCB;
+ vehiclePipe = pipe;
+}
+
+void
+DestroyVehiclePipe(void)
+{
+ rw::d3d::destroyVertexShader(neoVehicle_VS);
+ neoVehicle_VS = nil;
+
+ rw::d3d::destroyPixelShader(neoVehicle_PS);
+ neoVehicle_PS = nil;
+
+ ((rw::d3d9::ObjPipeline*)vehiclePipe)->destroy();
+ vehiclePipe = nil;
+}
+
+
+
+/*
+ * Neo World pipe
+ */
+
+static void *neoWorld_VS;
+static void *neoWorldVC_PS;
+
+static void
+worldRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ if(!LightmapEnable){
+ defaultRenderCB_Shader(atomic, header);
+ return;
+ }
+
+ int vsBits;
+ setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride);
+ setIndices(header->indexBuffer);
+ setVertexDeclaration(header->vertexDeclaration);
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+
+ float lightfactor[4];
+
+ InstanceData *inst = header->inst;
+ for(rw::uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ if(MatFX::getEffects(m) == MatFX::DUAL){
+ setVertexShader(neoWorld_VS);
+
+ MatFX *matfx = MatFX::get(m);
+ Texture *dualtex = matfx->getDualTexture();
+ if(dualtex == nil)
+ goto notex;
+ d3d::setTexture(1, dualtex);
+ lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult;
+ }else{
+ notex:
+ setVertexShader(default_amb_VS);
+
+ d3d::setTexture(1, nil);
+ lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f;
+ }
+ lightfactor[3] = m->color.alpha/255.0f;
+ d3d::setTexture(0, m->texture);
+ d3ddevice->SetPixelShaderConstantF(1, lightfactor, 1);
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ RGBA color = { 255, 255, 255, m->color.alpha };
+ setMaterial(color, m->surfaceProps);
+
+ if(m->texture)
+ d3d::setTexture(0, m->texture);
+ else
+ d3d::setTexture(0, gpWhiteTexture);
+ setPixelShader(neoWorldVC_PS);
+
+ drawInst(header, inst);
+ inst++;
+ }
+ d3d::setTexture(1, nil);
+}
+
+void
+CreateWorldPipe(void)
+{
+ if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0)
+ printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n");
+ else
+ ReadTweakValueTable((char*)work_buff, WorldLightmapBlend);
+
+#include "shaders/obj/default_UV2_VS.inc"
+ neoWorld_VS = rw::d3d::createVertexShader(default_UV2_VS_cso);
+ assert(neoWorld_VS);
+
+#include "shaders/obj/neoWorldVC_PS.inc"
+ neoWorldVC_PS = rw::d3d::createPixelShader(neoWorldVC_PS_cso);
+ assert(neoWorldVC_PS);
+
+
+ rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create();
+ pipe->instanceCB = rw::d3d9::defaultInstanceCB;
+ pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB;
+ pipe->renderCB = worldRenderCB;
+ worldPipe = pipe;
+}
+
+void
+DestroyWorldPipe(void)
+{
+ rw::d3d::destroyVertexShader(neoWorld_VS);
+ neoWorld_VS = nil;
+ rw::d3d::destroyPixelShader(neoWorldVC_PS);
+ neoWorldVC_PS = nil;
+
+
+ ((rw::d3d9::ObjPipeline*)worldPipe)->destroy();
+ worldPipe = nil;
+}
+
+
+
+
+/*
+ * Neo Gloss pipe
+ */
+
+static void *neoGloss_VS;
+static void *neoGloss_PS;
+
+static void
+glossRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
+{
+ worldRenderCB(atomic, header);
+
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ if(!GlossEnable)
+ return;
+
+ setVertexShader(neoGloss_VS);
+ setPixelShader(neoGloss_PS);
+
+ V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_eye, (float*)&eyePos, 1);
+ d3ddevice->SetPixelShaderConstantF(1, (float*)&GlossMult, 1);
+
+ SetRenderState(VERTEXALPHA, TRUE);
+ SetRenderState(SRCBLEND, BLENDONE);
+ SetRenderState(DESTBLEND, BLENDONE);
+ SetRenderState(ZWRITEENABLE, FALSE);
+ SetRenderState(ALPHATESTFUNC, ALPHAALWAYS);
+
+ InstanceData *inst = header->inst;
+ for(rw::uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ if(m->texture){
+ Texture *tex = GetGlossTex(m);
+ if(tex){
+ d3d::setTexture(0, tex);
+ drawInst(header, inst);
+ }
+ }
+ inst++;
+ }
+
+ SetRenderState(ZWRITEENABLE, TRUE);
+ SetRenderState(ALPHATESTFUNC, ALPHAGREATEREQUAL);
+ SetRenderState(SRCBLEND, BLENDSRCALPHA);
+ SetRenderState(DESTBLEND, BLENDINVSRCALPHA);
+}
+
+void
+CreateGlossPipe(void)
+{
+#include "shaders/obj/neoGloss_VS.inc"
+ neoGloss_VS = rw::d3d::createVertexShader(neoGloss_VS_cso);
+ assert(neoGloss_VS);
+
+#include "shaders/obj/neoGloss_PS.inc"
+ neoGloss_PS = rw::d3d::createPixelShader(neoGloss_PS_cso);
+ assert(neoGloss_PS);
+
+
+ rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create();
+ pipe->instanceCB = rw::d3d9::defaultInstanceCB;
+ pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB;
+ pipe->renderCB = glossRenderCB;
+ glossPipe = pipe;
+}
+
+void
+DestroyGlossPipe(void)
+{
+ rw::d3d::destroyVertexShader(neoGloss_VS);
+ neoGloss_VS = nil;
+
+ rw::d3d::destroyPixelShader(neoGloss_PS);
+ neoGloss_PS = nil;
+
+ ((rw::d3d9::ObjPipeline*)glossPipe)->destroy();
+ glossPipe = nil;
+}
+
+
+
+/*
+ * Neo Rim pipes
+ */
+
+static void *neoRim_VS;
+static void *neoRimSkin_VS;
+
+static void
+uploadRimData(bool enable)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+
+ V3d viewVec = rw::engine->currentCamera->getFrame()->getLTM()->at;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_viewVec, (float*)&viewVec, 1);
+ float rimData[4];
+ rimData[0] = Offset.Get();
+ rimData[1] = Scale.Get();
+ if(enable)
+ rimData[2] = Scaling.Get()*RimlightMult;
+ else
+ rimData[2] = 0.0f;
+ rimData[3] = 0.0f;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_rimData, rimData, 1);
+ Color col = RampStart.Get();
+ d3ddevice->SetVertexShaderConstantF(VSLOC_rampStart, (float*)&col, 1);
+ col = RampEnd.Get();
+ d3ddevice->SetVertexShaderConstantF(VSLOC_rampEnd, (float*)&col, 1);
+}
+
+static void
+rimRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ if(!RimlightEnable){
+ defaultRenderCB_Shader(atomic, header);
+ return;
+ }
+
+ int vsBits;
+ rw::uint32 flags = atomic->geometry->flags;
+ setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride);
+ setIndices(header->indexBuffer);
+ setVertexDeclaration(header->vertexDeclaration);
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+ setVertexShader(neoRim_VS);
+
+ uploadRimData(atomic->geometry->flags & Geometry::LIGHT);
+
+ InstanceData *inst = header->inst;
+ for(rw::uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ if(m->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(header, inst);
+ inst++;
+ }
+}
+
+static void
+rimSkinRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ if(!RimlightEnable){
+ skinRenderCB(atomic, header);
+ return;
+ }
+
+ int vsBits;
+ rw::uint32 flags = atomic->geometry->flags;
+ setStreamSource(0, (IDirect3DVertexBuffer9*)header->vertexStream[0].vertexBuffer,
+ 0, header->vertexStream[0].stride);
+ setIndices((IDirect3DIndexBuffer9*)header->indexBuffer);
+ setVertexDeclaration((IDirect3DVertexDeclaration9*)header->vertexDeclaration);
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+ uploadSkinMatrices(atomic);
+
+ setVertexShader(neoRimSkin_VS);
+
+ uploadRimData(atomic->geometry->flags & Geometry::LIGHT);
+
+ InstanceData *inst = header->inst;
+ for(rw::uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ if(inst->material->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(header, inst);
+ inst++;
+ }
+}
+
+void
+CreateRimLightPipes(void)
+{
+ if(CFileMgr::LoadFile("neo/rimTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0)
+ printf("Error: couldn't open 'neo/rimTweakingTable.dat'\n");
+ else{
+ char *fp = (char*)work_buff;
+ fp = ReadTweakValueTable(fp, RampStart);
+ fp = ReadTweakValueTable(fp, RampEnd);
+ fp = ReadTweakValueTable(fp, Offset);
+ fp = ReadTweakValueTable(fp, Scale);
+ fp = ReadTweakValueTable(fp, Scaling);
+ }
+
+
+#include "shaders/obj/neoRim_VS.inc"
+ neoRim_VS = rw::d3d::createVertexShader(neoRim_VS_cso);
+ assert(neoRim_VS);
+
+#include "shaders/obj/neoRimSkin_VS.inc"
+ neoRimSkin_VS = rw::d3d::createVertexShader(neoRimSkin_VS_cso);
+ assert(neoRimSkin_VS);
+
+
+ rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create();
+ pipe->instanceCB = rw::d3d9::defaultInstanceCB;
+ pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB;
+ pipe->renderCB = rimRenderCB;
+ rimPipe = pipe;
+
+ pipe = rw::d3d9::ObjPipeline::create();
+ pipe->instanceCB = rw::d3d9::skinInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = rimSkinRenderCB;
+ rimSkinPipe = pipe;
+}
+
+void
+DestroyRimLightPipes(void)
+{
+ rw::d3d::destroyVertexShader(neoRim_VS);
+ neoRim_VS = nil;
+
+ rw::d3d::destroyVertexShader(neoRimSkin_VS);
+ neoRimSkin_VS = nil;
+
+ ((rw::d3d9::ObjPipeline*)rimPipe)->destroy();
+ rimPipe = nil;
+
+ ((rw::d3d9::ObjPipeline*)rimSkinPipe)->destroy();
+ rimSkinPipe = nil;
+}
+
+}
+
+#endif
+
+#ifdef NEW_RENDERER
+#ifndef LIBRW
+#error "Need librw for NEW_PIPELINES"
+#endif
+
+namespace WorldRender
+{
+
+struct BuildingInst
+{
+ rw::RawMatrix combinedMat;
+ rw::d3d9::InstanceDataHeader *instHeader;
+ uint8 fadeAlpha;
+ bool lighting;
+};
+BuildingInst blendInsts[3][2000];
+int numBlendInsts[3];
+
+static RwRGBAReal black;
+
+static void
+SetMatrix(BuildingInst *building, rw::Matrix *worldMat)
+{
+ using namespace rw;
+ RawMatrix world, worldview;
+ Camera *cam = engine->currentCamera;
+ convMatrix(&world, worldMat);
+ RawMatrix::mult(&worldview, &world, &cam->devView);
+ RawMatrix::mult(&building->combinedMat, &worldview, &cam->devProj);
+}
+
+static bool
+IsTextureTransparent(RwTexture *tex)
+{
+ if(tex == nil || tex->raster == nil)
+ return false;
+ return PLUGINOFFSET(rw::d3d::D3dRaster, tex->raster, rw::d3d::nativeRasterOffset)->hasAlpha;
+}
+
+// Render all opaque meshes and put atomics that needs blending
+// into the deferred list.
+void
+AtomicFirstPass(RpAtomic *atomic, int pass)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]];
+
+ atomic->getPipeline()->instance(atomic);
+ building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData;
+ assert(building->instHeader != nil);
+ assert(building->instHeader->platform == PLATFORM_D3D9);
+ building->fadeAlpha = 255;
+ building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT);
+ rw::uint32 flags = atomic->geometry->flags;
+
+ bool setupDone = false;
+ bool defer = false;
+ SetMatrix(building, atomic->getFrame()->getLTM());
+
+ InstanceData *inst = building->instHeader->inst;
+ for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){
+ Material *m = inst->material;
+
+ if(inst->vertexAlpha || m->color.alpha != 255 ||
+ IsTextureTransparent(m->texture)){
+ defer = true;
+ continue;
+ }
+
+ // alright we're rendering this atomic
+ if(!setupDone){
+ setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride);
+ setIndices(building->instHeader->indexBuffer);
+ setVertexDeclaration(building->instHeader->vertexDeclaration);
+ setVertexShader(default_amb_VS);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4);
+ if(building->lighting)
+ setAmbient(pAmbient->color);
+ else
+ setAmbient(black);
+ setupDone = true;
+ }
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ if(m->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(building->instHeader, inst);
+ }
+ if(defer)
+ numBlendInsts[pass]++;
+}
+
+void
+AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]];
+
+ atomic->getPipeline()->instance(atomic);
+ building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData;
+ assert(building->instHeader != nil);
+ assert(building->instHeader->platform == PLATFORM_D3D9);
+ building->fadeAlpha = fadeAlpha;
+ building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT);
+ SetMatrix(building, atomic->getFrame()->getLTM());
+ numBlendInsts[pass]++;
+}
+
+void
+RenderBlendPass(int pass)
+{
+ using namespace rw;
+ using namespace rw::d3d;
+ using namespace rw::d3d9;
+
+ setVertexShader(default_amb_VS);
+
+ int i;
+ for(i = 0; i < numBlendInsts[pass]; i++){
+ BuildingInst *building = &blendInsts[pass][i];
+
+ setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride);
+ setIndices(building->instHeader->indexBuffer);
+ setVertexDeclaration(building->instHeader->vertexDeclaration);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4);
+ if(building->lighting)
+ setAmbient(pAmbient->color);
+ else
+ setAmbient(black);
+
+ InstanceData *inst = building->instHeader->inst;
+ for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){
+ Material *m = inst->material;
+ if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255)
+ continue; // already done this one
+
+ rw::RGBA color = m->color;
+ color.alpha = (color.alpha * building->fadeAlpha)/255;
+ setMaterial(color, m->surfaceProps); // always modulate here
+
+ if(m->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(building->instHeader, inst);
+ }
+ }
+}
+}
+#endif
+
+#endif
diff --git a/src/extras/custompipes_gl.cpp b/src/extras/custompipes_gl.cpp
new file mode 100644
index 0000000..fd68c74
--- /dev/null
+++ b/src/extras/custompipes_gl.cpp
@@ -0,0 +1,738 @@
+#include "common.h"
+
+#ifdef RW_OPENGL
+#include "main.h"
+#include "RwHelper.h"
+#include "Lights.h"
+#include "Timecycle.h"
+#include "FileMgr.h"
+#include "Clock.h"
+#include "Weather.h"
+#include "TxdStore.h"
+#include "Renderer.h"
+#include "World.h"
+#include "custompipes.h"
+
+#ifdef EXTENDED_PIPELINES
+
+#ifndef LIBRW
+#error "Need librw for EXTENDED_PIPELINES"
+#endif
+
+namespace CustomPipes {
+
+static int32 u_viewVec;
+static int32 u_rampStart;
+static int32 u_rampEnd;
+static int32 u_rimData;
+
+static int32 u_lightMap;
+
+static int32 u_eye;
+static int32 u_reflProps;
+static int32 u_specDir;
+static int32 u_specColor;
+
+#define U(i) currentShader->uniformLocations[i]
+
+/*
+ * Neo Vehicle pipe
+ */
+
+rw::gl3::Shader *neoVehicleShader;
+
+static void
+uploadSpecLights(void)
+{
+ using namespace rw::gl3;
+
+ rw::RGBAf colors[1 + NUMEXTRADIRECTIONALS];
+ struct {
+ rw::V3d dir;
+ float power;
+ } dirs[1 + NUMEXTRADIRECTIONALS];
+ memset(colors, 0, sizeof(colors));
+ memset(dirs, 0, sizeof(dirs));
+ for(int i = 0; i < 1+NUMEXTRADIRECTIONALS; i++)
+ dirs[i].power = 1.0f;
+ float power = Power.Get();
+ Color speccol = SpecColor.Get();
+ colors[0].red = speccol.r;
+ colors[0].green = speccol.g;
+ colors[0].blue = speccol.b;
+ dirs[0].dir = pDirect->getFrame()->getLTM()->at;
+ dirs[0].power = power;
+ for(int i = 0; i < NUMEXTRADIRECTIONALS; i++){
+ if(pExtraDirectionals[i]->getFlags() & rw::Light::LIGHTATOMICS){
+ colors[1+i] = pExtraDirectionals[i]->color;
+ dirs[1+i].dir = pExtraDirectionals[i]->getFrame()->getLTM()->at;
+ dirs[1+i].power = power*2.0f;
+ }
+ }
+ glUniform4fv(U(u_specDir), 1 + NUMEXTRADIRECTIONALS, (float*)&dirs);
+ glUniform4fv(U(u_specColor), 1 + NUMEXTRADIRECTIONALS, (float*)&colors);
+}
+
+static void
+vehicleRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ // TODO: make this less of a kludge
+ if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){
+ matFXGlobals.pipelines[rw::platform]->render(atomic);
+ return;
+ }
+
+ Material *m;
+
+ rw::uint32 flags = atomic->geometry->flags;
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ rw::int32 n = header->numMeshes;
+
+ neoVehicleShader->use();
+
+ V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos;
+ glUniform3fv(U(u_eye), 1, (float*)&eyePos);
+
+ uploadSpecLights();
+
+ float reflProps[4];
+ reflProps[0] = Fresnel.Get();
+ reflProps[1] = SpecColor.Get().a;
+
+ setTexture(1, EnvMapTex);
+
+ SetRenderState(SRCBLEND, BLENDONE);
+
+ while(n--){
+ m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ reflProps[2] = m->surfaceProps.specular * VehicleShininess;
+ reflProps[3] = m->surfaceProps.specular == 0.0f ? 0.0f : VehicleSpecularity;
+ glUniform4fv(U(u_reflProps), 1, reflProps);
+
+ drawInst(header, inst);
+ inst++;
+ }
+
+ setTexture(1, nil);
+
+ SetRenderState(SRCBLEND, BLENDSRCALPHA);
+
+ teardownVertexInput(header);
+}
+
+void
+CreateVehiclePipe(void)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0)
+ printf("Error: couldn't open 'neo/carTweakingTable.dat'\n");
+ else{
+ char *fp = (char*)work_buff;
+ fp = ReadTweakValueTable(fp, Fresnel);
+ fp = ReadTweakValueTable(fp, Power);
+ fp = ReadTweakValueTable(fp, DiffColor);
+ fp = ReadTweakValueTable(fp, SpecColor);
+ }
+
+
+ {
+#include "shaders/obj/neoVehicle_frag.inc"
+#include "shaders/obj/neoVehicle_vert.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, neoVehicle_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, neoVehicle_frag_src, nil };
+ neoVehicleShader = Shader::create(vs, fs);
+ assert(neoVehicleShader);
+ }
+
+
+ rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create();
+ pipe->instanceCB = rw::gl3::defaultInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = vehicleRenderCB;
+ vehiclePipe = pipe;
+}
+
+void
+DestroyVehiclePipe(void)
+{
+ neoVehicleShader->destroy();
+ neoVehicleShader = nil;
+
+ ((rw::gl3::ObjPipeline*)vehiclePipe)->destroy();
+ vehiclePipe = nil;
+}
+
+
+
+/*
+ * Neo World pipe
+ */
+
+rw::gl3::Shader *neoWorldShader;
+
+static void
+worldRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ if(!LightmapEnable){
+ gl3::defaultRenderCB(atomic, header);
+ return;
+ }
+
+ Material *m;
+
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ rw::int32 n = header->numMeshes;
+
+ neoWorldShader->use();
+
+ float lightfactor[4];
+
+ while(n--){
+ m = inst->material;
+
+ if(MatFX::getEffects(m) == MatFX::DUAL){
+ MatFX *matfx = MatFX::get(m);
+ Texture *dualtex = matfx->getDualTexture();
+ if(dualtex == nil)
+ goto notex;
+ setTexture(1, dualtex);
+ lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult;
+ }else{
+ notex:
+ setTexture(1, nil);
+ lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f;
+ }
+ lightfactor[3] = m->color.alpha/255.0f;
+ glUniform4fv(U(u_lightMap), 1, lightfactor);
+
+ RGBA color = { 255, 255, 255, m->color.alpha };
+ setMaterial(color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ drawInst(header, inst);
+ inst++;
+ }
+ setTexture(1, nil);
+ teardownVertexInput(header);
+}
+
+void
+CreateWorldPipe(void)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0)
+ printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n");
+ else
+ ReadTweakValueTable((char*)work_buff, WorldLightmapBlend);
+
+ {
+#include "shaders/obj/neoWorldVC_frag.inc"
+#include "shaders/obj/default_UV2_vert.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, default_UV2_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, neoWorldVC_frag_src, nil };
+ neoWorldShader = Shader::create(vs, fs);
+ assert(neoWorldShader);
+ }
+
+
+ rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create();
+ pipe->instanceCB = rw::gl3::defaultInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = worldRenderCB;
+ worldPipe = pipe;
+}
+
+void
+DestroyWorldPipe(void)
+{
+ neoWorldShader->destroy();
+ neoWorldShader = nil;
+
+ ((rw::gl3::ObjPipeline*)worldPipe)->destroy();
+ worldPipe = nil;
+}
+
+
+
+
+/*
+ * Neo Gloss pipe
+ */
+
+rw::gl3::Shader *neoGlossShader;
+
+static void
+glossRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ worldRenderCB(atomic, header);
+ if(!GlossEnable)
+ return;
+
+ Material *m;
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ rw::int32 n = header->numMeshes;
+
+ neoGlossShader->use();
+
+ V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos;
+ glUniform3fv(U(u_eye), 1, (float*)&eyePos);
+ float reflProps[4];
+ reflProps[0] = GlossMult;
+ reflProps[1] = 0.0f;
+ reflProps[2] = 0.0f;
+ reflProps[3] = 0.0f;
+ glUniform4fv(U(u_reflProps), 1, reflProps);
+
+ SetRenderState(VERTEXALPHA, TRUE);
+ SetRenderState(SRCBLEND, BLENDONE);
+ SetRenderState(DESTBLEND, BLENDONE);
+ SetRenderState(ZWRITEENABLE, FALSE);
+ SetRenderState(ALPHATESTFUNC, ALPHAALWAYS);
+
+ while(n--){
+ m = inst->material;
+
+ RGBA color = { 255, 255, 255, m->color.alpha };
+ setMaterial(color, m->surfaceProps);
+
+ if(m->texture){
+ Texture *tex = GetGlossTex(m);
+ if(tex){
+ setTexture(0, tex);
+ drawInst(header, inst);
+ }
+ }
+ inst++;
+ }
+
+ SetRenderState(ZWRITEENABLE, TRUE);
+ SetRenderState(ALPHATESTFUNC, ALPHAGREATEREQUAL);
+ SetRenderState(SRCBLEND, BLENDSRCALPHA);
+ SetRenderState(DESTBLEND, BLENDINVSRCALPHA);
+
+ teardownVertexInput(header);
+}
+
+void
+CreateGlossPipe(void)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ {
+#include "shaders/obj/neoGloss_frag.inc"
+#include "shaders/obj/neoGloss_vert.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, neoGloss_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, neoGloss_frag_src, nil };
+ neoGlossShader = Shader::create(vs, fs);
+ assert(neoGlossShader);
+ }
+
+ rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create();
+ pipe->instanceCB = rw::gl3::defaultInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = glossRenderCB;
+ glossPipe = pipe;
+}
+
+void
+DestroyGlossPipe(void)
+{
+ neoGlossShader->destroy();
+ neoGlossShader = nil;
+
+ ((rw::gl3::ObjPipeline*)glossPipe)->destroy();
+ glossPipe = nil;
+}
+
+
+
+/*
+ * Neo Rim pipes
+ */
+
+rw::gl3::Shader *neoRimShader;
+rw::gl3::Shader *neoRimSkinShader;
+
+static void
+uploadRimData(bool enable)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ V3d viewVec = rw::engine->currentCamera->getFrame()->getLTM()->at;
+ glUniform3fv(U(u_viewVec), 1, (float*)&viewVec);
+ float rimData[4];
+ rimData[0] = Offset.Get();
+ rimData[1] = Scale.Get();
+ if(enable)
+ rimData[2] = Scaling.Get()*RimlightMult;
+ else
+ rimData[2] = 0.0f;
+ rimData[3] = 0.0f;
+ glUniform3fv(U(u_rimData), 1, rimData);
+ Color col = RampStart.Get();
+ glUniform4fv(U(u_rampStart), 1, (float*)&col);
+ col = RampEnd.Get();
+ glUniform4fv(U(u_rampEnd), 1, (float*)&col);
+}
+
+static void
+rimSkinRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ if(!RimlightEnable){
+ gl3::skinRenderCB(atomic, header);
+ return;
+ }
+
+ Material *m;
+
+ rw::uint32 flags = atomic->geometry->flags;
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ rw::int32 n = header->numMeshes;
+
+ neoRimSkinShader->use();
+
+ uploadRimData(atomic->geometry->flags & Geometry::LIGHT);
+
+ uploadSkinMatrices(atomic);
+
+ while(n--){
+ m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ drawInst(header, inst);
+ inst++;
+ }
+ teardownVertexInput(header);
+}
+
+static void
+rimRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ if(!RimlightEnable){
+ gl3::defaultRenderCB(atomic, header);
+ return;
+ }
+
+ Material *m;
+
+ rw::uint32 flags = atomic->geometry->flags;
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ rw::int32 n = header->numMeshes;
+
+ neoRimShader->use();
+
+ uploadRimData(atomic->geometry->flags & Geometry::LIGHT);
+
+ while(n--){
+ m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ drawInst(header, inst);
+ inst++;
+ }
+ teardownVertexInput(header);
+}
+
+void
+CreateRimLightPipes(void)
+{
+ using namespace rw::gl3;
+
+ if(CFileMgr::LoadFile("neo/rimTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0)
+ printf("Error: couldn't open 'neo/rimTweakingTable.dat'\n");
+ else{
+ char *fp = (char*)work_buff;
+ fp = ReadTweakValueTable(fp, RampStart);
+ fp = ReadTweakValueTable(fp, RampEnd);
+ fp = ReadTweakValueTable(fp, Offset);
+ fp = ReadTweakValueTable(fp, Scale);
+ fp = ReadTweakValueTable(fp, Scaling);
+ }
+
+ {
+#include "shaders/obj/simple_frag.inc"
+#include "shaders/obj/neoRimSkin_vert.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, neoRimSkin_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
+ neoRimSkinShader = Shader::create(vs, fs);
+ assert(neoRimSkinShader);
+ }
+
+ {
+#include "shaders/obj/simple_frag.inc"
+#include "shaders/obj/neoRim_vert.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, neoRim_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
+ neoRimShader = Shader::create(vs, fs);
+ assert(neoRimShader);
+ }
+
+
+ rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create();
+ pipe->instanceCB = rw::gl3::defaultInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = rimRenderCB;
+ rimPipe = pipe;
+
+ pipe = rw::gl3::ObjPipeline::create();
+ pipe->instanceCB = rw::gl3::skinInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = rimSkinRenderCB;
+ rimSkinPipe = pipe;
+}
+
+void
+DestroyRimLightPipes(void)
+{
+ neoRimShader->destroy();
+ neoRimShader = nil;
+
+ neoRimSkinShader->destroy();
+ neoRimSkinShader = nil;
+
+ ((rw::gl3::ObjPipeline*)rimPipe)->destroy();
+ rimPipe = nil;
+
+ ((rw::gl3::ObjPipeline*)rimSkinPipe)->destroy();
+ rimSkinPipe = nil;
+}
+
+
+
+void
+CustomPipeRegisterGL(void)
+{
+ u_viewVec = rw::gl3::registerUniform("u_viewVec");
+ u_rampStart = rw::gl3::registerUniform("u_rampStart");
+ u_rampEnd = rw::gl3::registerUniform("u_rampEnd");
+ u_rimData = rw::gl3::registerUniform("u_rimData");
+
+ u_lightMap = rw::gl3::registerUniform("u_lightMap");
+
+ u_eye = rw::gl3::registerUniform("u_eye");
+ u_reflProps = rw::gl3::registerUniform("u_reflProps");
+ u_specDir = rw::gl3::registerUniform("u_specDir");
+ u_specColor = rw::gl3::registerUniform("u_specColor");
+}
+
+
+}
+
+#endif
+
+#ifdef NEW_RENDERER
+#ifndef LIBRW
+#error "Need librw for NEW_PIPELINES"
+#endif
+
+namespace WorldRender
+{
+
+struct BuildingInst
+{
+ rw::Matrix matrix;
+ rw::gl3::InstanceDataHeader *instHeader;
+ uint8 fadeAlpha;
+ bool lighting;
+};
+BuildingInst blendInsts[3][2000];
+int numBlendInsts[3];
+
+static RwRGBAReal black;
+
+static bool
+IsTextureTransparent(RwTexture *tex)
+{
+ if(tex == nil || tex->raster == nil)
+ return false;
+ return PLUGINOFFSET(rw::gl3::Gl3Raster, tex->raster, rw::gl3::nativeRasterOffset)->hasAlpha;
+}
+
+// Render all opaque meshes and put atomics that needs blending
+// into the deferred list.
+void
+AtomicFirstPass(RpAtomic *atomic, int pass)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]];
+
+ atomic->getPipeline()->instance(atomic);
+ building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData;
+ assert(building->instHeader != nil);
+ assert(building->instHeader->platform == PLATFORM_GL3);
+ building->fadeAlpha = 255;
+ building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT);
+ rw::uint32 flags = atomic->geometry->flags;
+
+ WorldLights lights;
+ lights.numAmbients = 1;
+ lights.numDirectionals = 0;
+ lights.numLocals = 0;
+ if(building->lighting)
+ lights.ambient = pAmbient->color;
+ else
+ lights.ambient = black;
+
+ bool setupDone = false;
+ bool defer = false;
+ building->matrix = *atomic->getFrame()->getLTM();
+
+ InstanceData *inst = building->instHeader->inst;
+ for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){
+ Material *m = inst->material;
+
+ if(inst->vertexAlpha || m->color.alpha != 255 ||
+ IsTextureTransparent(m->texture)){
+ defer = true;
+ continue;
+ }
+
+ // alright we're rendering this atomic
+ if(!setupDone){
+ defaultShader->use();
+ setWorldMatrix(&building->matrix);
+ setupVertexInput(building->instHeader);
+ setLights(&lights);
+ setupDone = true;
+ }
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ drawInst(building->instHeader, inst);
+ }
+ teardownVertexInput(building->instHeader);
+ if(defer)
+ numBlendInsts[pass]++;
+}
+
+void
+AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]];
+
+ atomic->getPipeline()->instance(atomic);
+ building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData;
+ assert(building->instHeader != nil);
+ assert(building->instHeader->platform == PLATFORM_GL3);
+ building->fadeAlpha = fadeAlpha;
+ building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT);
+ building->matrix = *atomic->getFrame()->getLTM();
+ numBlendInsts[pass]++;
+}
+
+void
+RenderBlendPass(int pass)
+{
+ using namespace rw;
+ using namespace rw::gl3;
+
+ defaultShader->use();
+ WorldLights lights;
+ lights.numAmbients = 1;
+ lights.numDirectionals = 0;
+ lights.numLocals = 0;
+
+ int i;
+ for(i = 0; i < numBlendInsts[pass]; i++){
+ BuildingInst *building = &blendInsts[pass][i];
+
+ setupVertexInput(building->instHeader);
+ setWorldMatrix(&building->matrix);
+ if(building->lighting)
+ lights.ambient = pAmbient->color;
+ else
+ lights.ambient = black;
+ setLights(&lights);
+
+ InstanceData *inst = building->instHeader->inst;
+ for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){
+ Material *m = inst->material;
+ if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255)
+ continue; // already done this one
+
+ rw::RGBA color = m->color;
+ color.alpha = (color.alpha * building->fadeAlpha)/255;
+ setMaterial(color, m->surfaceProps); // always modulate here
+
+ setTexture(0, m->texture);
+
+ drawInst(building->instHeader, inst);
+ }
+ teardownVertexInput(building->instHeader);
+ }
+}
+}
+#endif
+
+#endif
diff --git a/src/extras/debugmenu.cpp b/src/extras/debugmenu.cpp
new file mode 100644
index 0000000..533b97f
--- /dev/null
+++ b/src/extras/debugmenu.cpp
@@ -0,0 +1,1312 @@
+#include "common.h"
+#ifdef DEBUGMENU
+#include "RwHelper.h"
+#include "Pad.h"
+#include "ControllerConfig.h"
+#include "Timer.h"
+#include "rtcharse.h"
+#include "re3_inttypes.h"
+#include "debugmenu.h"
+#include <new>
+
+#ifdef _WIN32
+#define snprintf _snprintf
+
+#define strdup _strdup
+#endif
+
+
+// Font stuff
+struct Pt
+{
+ int x, y;
+};
+
+enum MenuFontStyle
+{
+ MENUFONT_NORMAL,
+ MENUFONT_SEL_ACTIVE,
+ MENUFONT_SEL_INACTIVE,
+ MENUFONT_MOUSE
+};
+
+RtCharset *fontStyles[4];
+RtCharsetDesc fontDesc;
+int fontscale = 1; // not supported right now
+
+Pt
+fontGetStringSize(const char *s)
+{
+ Pt sz = { 0, 0 };
+ int x;
+ char c;
+ sz.y = fontDesc.height*fontscale; // always assume one line;
+ x = 0;
+ while(c = *s++){
+ if(c == '\n'){
+ sz.y += fontDesc.height*fontscale;
+ if(x > sz.x)
+ sz.x = x;
+ x = 0;
+ }else
+ x += fontDesc.width*fontscale;
+ }
+ if(x > sz.x)
+ sz.x = x;
+ return sz;
+}
+
+Pt
+fontPrint(const char *s, float x, float y, int style)
+{
+ RtCharsetPrintBuffered(fontStyles[style], s, x, y, false);
+ return fontGetStringSize(s);
+}
+
+int
+fontGetLen(int len)
+{
+ return len*fontDesc.width*fontscale;
+}
+
+
+void
+createMenuFont(void)
+{
+ OpenCharsetSafe();
+
+ RwRGBA fg_normal = { 255, 255, 255, 255 };
+ RwRGBA bg_normal = { 255, 255, 255, 0 };
+ fontStyles[MENUFONT_NORMAL] = RtCharsetCreate(&fg_normal, &bg_normal);
+ assert(fontStyles[MENUFONT_NORMAL]);
+
+ RwRGBA fg_sel_active = { 200, 200, 200, 255 };
+ RwRGBA bg_sel_active = { 132, 132, 132, 255 };
+ fontStyles[MENUFONT_SEL_ACTIVE] = RtCharsetCreate(&fg_sel_active, &bg_sel_active);
+ assert(fontStyles[MENUFONT_SEL_ACTIVE]);
+
+ RwRGBA fg_sel_inactive = { 200, 200, 200, 255 };
+ RwRGBA bg_sel_inactive = { 200, 200, 200, 0 };
+ fontStyles[MENUFONT_SEL_INACTIVE] = RtCharsetCreate(&fg_sel_inactive, &bg_sel_inactive);
+ assert(fontStyles[MENUFONT_SEL_INACTIVE]);
+
+ RwRGBA fg_mouse = { 255, 255, 255, 255 };
+ RwRGBA bg_mouse = { 132, 132, 132, 255 };
+ fontStyles[MENUFONT_MOUSE] = RtCharsetCreate(&fg_mouse, &bg_mouse);
+ assert(fontStyles[MENUFONT_MOUSE]);
+
+ RtCharsetGetDesc(fontStyles[MENUFONT_NORMAL], &fontDesc);
+}
+
+void
+destroyMenuFont(void)
+{
+ RtCharsetDestroy(fontStyles[MENUFONT_NORMAL]);
+ fontStyles[MENUFONT_NORMAL] = nil;
+ RtCharsetDestroy(fontStyles[MENUFONT_SEL_ACTIVE]);
+ fontStyles[MENUFONT_SEL_ACTIVE] = nil;
+ RtCharsetDestroy(fontStyles[MENUFONT_SEL_INACTIVE]);
+ fontStyles[MENUFONT_SEL_INACTIVE] = nil;
+ RtCharsetDestroy(fontStyles[MENUFONT_MOUSE]);
+ fontStyles[MENUFONT_MOUSE] = nil;
+}
+
+
+
+
+
+
+enum EntryType
+{
+ MENUEMPTY = 0,
+ MENUSUB,
+ MENUVAR,
+
+ MENUVAR_INT,
+ MENUVAR_FLOAT,
+ MENUVAR_CMD,
+
+ MENUSCROLL // dummy
+};
+
+struct Menu
+{
+ Menu *parent;
+ RwRect r;
+ MenuEntry *entries;
+ int numEntries;
+ int maxNameWidth, maxValWidth;
+
+ MenuEntry *findEntry(const char *entryname);
+ void insertEntrySorted(MenuEntry *entry);
+ void appendEntry(MenuEntry *entry);
+
+ bool isScrollingUp, isScrollingDown;
+ int scrollStart;
+ int numVisible;
+ RwRect scrollUpR, scrollDownR;
+ void scroll(int off);
+
+ int selection;
+ MenuEntry *selectedEntry; // updated by update
+ void changeSelection(int newsel);
+ void changeSelection(MenuEntry *e);
+
+ void update(void);
+ void draw(void);
+ Menu(void){ memset(this, 0, sizeof(Menu)); }
+ ~Menu(void);
+};
+extern Menu toplevel;
+
+struct MenuEntry_Sub : MenuEntry
+{
+ Menu *submenu;
+
+ MenuEntry_Sub(const char *name, Menu *menu);
+ ~MenuEntry_Sub(void) { delete submenu; }
+};
+
+struct MenuEntry_Var : MenuEntry
+{
+ int maxvallen;
+ int vartype;
+ bool wrapAround;
+
+ virtual void processInput(bool mouseOver, bool selected) = 0;
+ int getValWidth(void) { return maxvallen; }
+ virtual void getValStr(char *str, int len) = 0;
+ MenuEntry_Var(const char *name, int type);
+};
+
+struct MenuEntry_Int : MenuEntry_Var
+{
+ virtual void setStrings(const char **strings) = 0;
+ virtual int findStringLen(void) = 0;
+ MenuEntry_Int(const char *name);
+};
+
+#define INTTYPES \
+ X(Int8, int8, 4, "%4" PRId8) \
+ X(Int16, int16, 6, "%6" PRId16) \
+ X(Int32, int32, 11, "%11" PRId32) \
+ X(Int64, int64, 21, "%21" PRId64) \
+ X(UInt8, uint8, 4, "%4" PRIu8) \
+ X(UInt16, uint16, 6, "%6" PRIu16) \
+ X(UInt32, uint32, 11, "%11" PRIu32) \
+ X(UInt64, uint64, 21, "%21" PRIu64)
+#define FLOATTYPES \
+ X(Float32, float, 11, "%11.3f") \
+ X(Float64, double, 11, "%11.3lf")
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+struct MenuEntry_##NAME : MenuEntry_Int \
+{ \
+ TYPE *variable; \
+ TYPE lowerBound, upperBound; \
+ TYPE step; \
+ TriggerFunc triggerFunc; \
+ const char *fmt; \
+ const char **strings; \
+ \
+ void processInput(bool mouseOver, bool selected); \
+ void getValStr(char *str, int len); \
+ \
+ void setStrings(const char **strings); \
+ int findStringLen(void); \
+ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings); \
+};
+INTTYPES
+#undef X
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+struct MenuEntry_##NAME : MenuEntry_Var \
+{ \
+ TYPE *variable; \
+ TYPE lowerBound, upperBound; \
+ TYPE step; \
+ TriggerFunc triggerFunc; \
+ const char *fmt; \
+ \
+ void processInput(bool mouseOver, bool selected); \
+ void getValStr(char *str, int len); \
+ \
+ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound); \
+};
+FLOATTYPES
+#undef X
+
+struct MenuEntry_Cmd : MenuEntry_Var
+{
+ TriggerFunc triggerFunc;
+
+ void processInput(bool mouseOver, bool selected);
+ void getValStr(char *str, int len);
+
+ MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc);
+};
+
+
+Menu *findMenu(const char *name);
+
+
+
+#define MUHKEYS \
+ X(leftjustdown, rsLEFT) \
+ X(rightjustdown, rsRIGHT) \
+ X(upjustdown, rsUP) \
+ X(downjustdown, rsDOWN) \
+ X(pgupjustdown, rsPGUP) \
+ X(pgdnjustdown, rsPGDN)
+
+#define MUHBUTTONS \
+ X(button1justdown, 1) \
+ X(button2justdown, 2) \
+ X(button3justdown, 3)
+
+#define REPEATDELAY 700
+#define REPEATINTERVAL 50
+#define X(var, keycode) static int var;
+MUHKEYS
+#undef X
+static int downtime;
+static int repeattime;
+static int lastkeydown;
+static int *keyptr;
+
+static int buttondown[3];
+static int lastbuttondown;
+static int *buttonptr;
+static int button1justdown, button2justdown, button3justdown;
+static float mouseX, mouseY;
+
+static int menuOn;
+static int menuInitialized;
+static int screenWidth, screenHeight;
+static RwRaster *cursor, *arrow;
+
+static int firstBorder = 10;
+static int topBorder = 40; //10;
+static int leading = 4;
+static int gap = 10;
+static int minwidth = 100;
+
+void drawMouse(void);
+void drawArrow(RwRect r, int direction, int style);
+
+Menu toplevel;
+Menu *activeMenu = &toplevel;
+Menu *deepestMenu = &toplevel;
+Menu *mouseOverMenu;
+MenuEntry *mouseOverEntry;
+MenuEntry scrollUpEntry("SCROLLUP"), scrollDownEntry("SCROLLDOWN"); // dummies
+
+
+#define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k)
+#define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k)
+#define CTRLJUSTDOWN(key) \
+ ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \
+ (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key))
+#define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key))
+
+
+bool
+isMouseInRect(RwRect r)
+{
+ return (mouseX >= r.x && mouseX < r.x+r.w &&
+ mouseY >= r.y && mouseY < r.y+r.h);
+}
+
+/*
+ * MenuEntry
+ */
+
+MenuEntry::MenuEntry(const char *name)
+{
+ this->type = MENUEMPTY;
+ this->name = strdup(name);
+ this->next = nil;
+ this->menu = nil;
+}
+
+MenuEntry_Sub::MenuEntry_Sub(const char *name, Menu *menu)
+: MenuEntry(name)
+{
+ this->type = MENUSUB;
+ this->submenu = menu;
+}
+
+MenuEntry_Var::MenuEntry_Var(const char *name, int vartype)
+: MenuEntry(name)
+{
+ this->type = MENUVAR;
+ this->vartype = vartype;
+ this->maxvallen = 0;
+ this->wrapAround = false;
+}
+
+/*
+ * *****************************
+ * MenuEntry_Int
+ * *****************************
+ */
+
+MenuEntry_Int::MenuEntry_Int(const char *name)
+: MenuEntry_Var(name, MENUVAR_INT)
+{
+}
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+int \
+MenuEntry_##NAME::findStringLen(void){ \
+ TYPE i; \
+ int len, maxlen = 0; \
+ for(i = this->lowerBound; i <= this->upperBound; i++){ \
+ len = strlen(this->strings[i-this->lowerBound]); \
+ if(len > maxlen) \
+ maxlen = len; \
+ } \
+ return maxlen; \
+} \
+void \
+MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \
+{ \
+ TYPE v, oldv; \
+ int overflow = 0; \
+ int underflow = 0; \
+ \
+ v = *this->variable; \
+ oldv = v; \
+ \
+ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \
+ v -= this->step; \
+ if(v > oldv) \
+ underflow = 1; \
+ } \
+ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \
+ v += this->step; \
+ if(v < oldv) \
+ overflow = 1; \
+ } \
+ if(this->wrapAround){ \
+ if(v > this->upperBound || overflow) v = this->lowerBound; \
+ if(v < this->lowerBound || underflow) v = this->upperBound; \
+ }else{ \
+ if(v > this->upperBound || overflow) v = this->upperBound; \
+ if(v < this->lowerBound || underflow) v = this->lowerBound; \
+ } \
+ \
+ *this->variable = v; \
+ if(oldv != v && this->triggerFunc) \
+ this->triggerFunc(); \
+} \
+void \
+MenuEntry_##NAME::getValStr(char *str, int len) \
+{ \
+ static char tmp[20]; \
+ if(this->strings){ \
+ snprintf(tmp, 20, "%%%ds", this->maxvallen); \
+ if(*this->variable < this->lowerBound || *this->variable > this->upperBound){ \
+ snprintf(str, len, "ERROR"); \
+ return; \
+ } \
+ snprintf(str, len, tmp, this->strings[*this->variable-this->lowerBound]); \
+ }else \
+ snprintf(str, len, this->fmt, *this->variable); \
+} \
+void \
+MenuEntry_##NAME::setStrings(const char **strings) \
+{ \
+ this->strings = strings; \
+ if(this->strings) \
+ this->maxvallen = findStringLen(); \
+} \
+MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \
+: MenuEntry_Int(name) \
+{ \
+ this->variable = ptr; \
+ this->step = step; \
+ this->lowerBound = lowerBound; \
+ this->upperBound = upperBound; \
+ this->triggerFunc = triggerFunc; \
+ this->maxvallen = MAXLEN; \
+ this->fmt = FMT; \
+ this->setStrings(strings); \
+}
+INTTYPES
+#undef X
+
+/*
+ * *****************************
+ * MenuEntry_Float
+ * *****************************
+ */
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \
+: MenuEntry_Var(name, MENUVAR_FLOAT) \
+{ \
+ this->variable = ptr; \
+ this->step = step; \
+ this->lowerBound = lowerBound; \
+ this->upperBound = upperBound; \
+ this->triggerFunc = triggerFunc; \
+ this->maxvallen = MAXLEN; \
+ this->fmt = FMT; \
+} \
+void \
+MenuEntry_##NAME::getValStr(char *str, int len) \
+{ \
+ snprintf(str, len, this->fmt, *this->variable); \
+} \
+void \
+MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \
+{ \
+ float v, oldv; \
+ int overflow = 0; \
+ int underflow = 0; \
+ \
+ v = *this->variable; \
+ oldv = v; \
+ \
+ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \
+ v -= this->step; \
+ if(v > oldv) \
+ underflow = 1; \
+ } \
+ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \
+ v += this->step; \
+ if(v < oldv) \
+ overflow = 1; \
+ } \
+ if(this->wrapAround){ \
+ if(v > this->upperBound || overflow) v = this->lowerBound; \
+ if(v < this->lowerBound || underflow) v = this->upperBound; \
+ }else{ \
+ if(v > this->upperBound || overflow) v = this->upperBound; \
+ if(v < this->lowerBound || underflow) v = this->lowerBound; \
+ } \
+ \
+ *this->variable = v; \
+ if(oldv != v && this->triggerFunc) \
+ this->triggerFunc(); \
+}
+
+FLOATTYPES
+#undef X
+
+/*
+ * *****************************
+ * MenuEntry_Cmd
+ * *****************************
+ */
+
+void
+MenuEntry_Cmd::processInput(bool mouseOver, bool selected)
+{
+ // Don't execute on button3
+ if(this->triggerFunc && (selected && (leftjustdown || rightjustdown) || (mouseOver && button1justdown)))
+ this->triggerFunc();
+}
+
+void
+MenuEntry_Cmd::getValStr(char *str, int len)
+{
+ strncpy(str, "<", len);
+}
+
+MenuEntry_Cmd::MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc)
+: MenuEntry_Var(name, MENUVAR_CMD)
+{
+ this->maxvallen = 1;
+ this->triggerFunc = triggerFunc;
+}
+
+
+/*
+ * *****************************
+ * Menu
+ * *****************************
+ */
+
+void
+Menu::scroll(int off) {
+ if(isScrollingUp && off < 0)
+ scrollStart += off;
+ if(isScrollingDown && off > 0)
+ scrollStart += off;
+ if(scrollStart < 0) scrollStart = 0;
+ if(scrollStart > numEntries-numVisible) scrollStart = numEntries-numVisible;
+}
+
+void
+Menu::changeSelection(int newsel){
+ selection = newsel;
+ if(selection < 0) selection = 0;
+ if(selection >= numEntries) selection = numEntries-1;
+ if(selection < scrollStart) scrollStart = selection;
+ if(selection >= scrollStart+numVisible) scrollStart = selection-numVisible+1;
+}
+
+void
+Menu::changeSelection(MenuEntry *sel)
+{
+ MenuEntry *e;
+ int i = 0;
+ for(e = this->entries; e; e = e->next){
+ if(e == sel){
+ this->selection = i;
+ this->selectedEntry = sel;
+ break;
+ }
+ i++;
+ }
+}
+
+
+
+MenuEntry*
+Menu::findEntry(const char *entryname)
+{
+ MenuEntry *m;
+ for(m = this->entries; m; m = m->next)
+ if(strcmp(entryname, m->name) == 0)
+ return m;
+ return nil;
+}
+
+void
+Menu::insertEntrySorted(MenuEntry *entry)
+{
+ MenuEntry **mp;
+ int cmp;
+ for(mp = &this->entries; *mp; mp = &(*mp)->next){
+ cmp = strcmp(entry->name, (*mp)->name);
+ if(cmp == 0)
+ return;
+ if(cmp < 0)
+ break;
+ }
+ entry->next = *mp;
+ *mp = entry;
+ entry->menu = this;
+ this->numEntries++;
+}
+
+void
+Menu::appendEntry(MenuEntry *entry)
+{
+ MenuEntry **mp;
+ for(mp = &this->entries; *mp; mp = &(*mp)->next);
+ entry->next = *mp;
+ *mp = entry;
+ entry->menu = this;
+ this->numEntries++;
+}
+
+void
+Menu::update(void)
+{
+ int i;
+ int x, y;
+ Pt sz;
+ MenuEntry *e;
+ int onscreen;
+ x = this->r.x;
+ y = this->r.y + 18;
+ int end = this->r.y+this->r.h - 18;
+ this->numVisible = 0;
+
+ deepestMenu = this;
+
+ int bottomy = end;
+ onscreen = 1;
+ i = 0;
+ this->maxNameWidth = 0;
+ this->maxValWidth = 0;
+ this->isScrollingUp = this->scrollStart > 0;
+ this->isScrollingDown = false;
+ this->selectedEntry = nil;
+ for(e = this->entries; e; e = e->next){
+ sz = fontGetStringSize(e->name);
+ e->r.x = x;
+ e->r.y = y;
+ e->r.w = sz.x;
+ e->r.h = sz.y;
+
+ if(i == this->selection)
+ this->selectedEntry = e;
+
+ if(i >= this->scrollStart)
+ y += sz.y + leading*fontscale;
+ if(y >= end){
+ this->isScrollingDown = true;
+ onscreen = 0;
+ }else
+ bottomy = y;
+ if(i >= this->scrollStart && onscreen)
+ this->numVisible++;
+
+ if(e->type == MENUVAR){
+ int valwidth = fontGetLen(((MenuEntry_Var*)e)->getValWidth());
+ if(valwidth > maxValWidth)
+ maxValWidth = valwidth;
+ }
+ if(e->r.w > maxNameWidth)
+ maxNameWidth = e->r.w;
+ i++;
+ }
+ if(this->r.w < maxNameWidth + maxValWidth + gap*fontscale)
+ this->r.w = maxNameWidth + maxValWidth + gap*fontscale;
+
+ this->scrollUpR = this->r;
+ this->scrollUpR.h = 16;
+ this->scrollDownR = this->scrollUpR;
+ this->scrollDownR.y = bottomy;
+
+ // Update active submenu
+ if(this->selectedEntry && this->selectedEntry->type == MENUSUB){
+ Menu *submenu = ((MenuEntry_Sub*)this->selectedEntry)->submenu;
+ submenu->r.x = this->r.x+this->r.w + 10;
+ submenu->r.y = this->r.y;
+ submenu->r.w = minwidth; // update menu will expand
+ submenu->r.h = this->r.h;
+ submenu->update();
+ }
+}
+
+void
+Menu::draw(void)
+{
+ static char val[100];
+ int i;
+ MenuEntry *e;
+ i = 0;
+ for(e = this->entries; e; e = e->next){
+ if(i >= this->scrollStart+this->numVisible)
+ break;
+ if(i >= this->scrollStart){
+ int style = MENUFONT_NORMAL;
+ if(i == this->selection)
+ style = this == activeMenu ? MENUFONT_SEL_ACTIVE : MENUFONT_SEL_INACTIVE;
+ if(style != MENUFONT_SEL_ACTIVE && e == mouseOverEntry)
+ style = MENUFONT_MOUSE;
+ fontPrint(e->name, e->r.x, e->r.y, style);
+ if(e->type == MENUVAR){
+ int valw = fontGetLen(((MenuEntry_Var*)e)->getValWidth());
+ ((MenuEntry_Var*)e)->getValStr(val, 100);
+ fontPrint(val, e->r.x+this->r.w-valw, e->r.y, style);
+ }
+ }
+ i++;
+ }
+
+ if(this->isScrollingUp)
+ drawArrow(this->scrollUpR, -1, isMouseInRect(this->scrollUpR));
+ if(this->isScrollingDown)
+ drawArrow(this->scrollDownR, 1, isMouseInRect(this->scrollDownR));
+
+ if(this->selectedEntry && this->selectedEntry->type == MENUSUB)
+ ((MenuEntry_Sub*)this->selectedEntry)->submenu->draw();
+}
+
+Menu::~Menu(void)
+{
+ MenuEntry *e, *next;
+ for(e = entries; e; e = next){
+ next = e->next;
+ delete e;
+ }
+ memset(this, 0, sizeof(Menu));
+}
+
+Menu*
+findMenu(const char *name)
+{
+ Menu *m;
+ MenuEntry *e;
+ char *tmppath = strdup(name);
+ char *next, *curname;
+
+ curname = tmppath;
+ next = curname;
+
+ m = &toplevel;
+ while(*next){
+ curname = next;
+ while(*next){
+ if(*next == '|'){
+ *next++ = '\0';
+ break;
+ }
+ next++;
+ }
+ e = m->findEntry(curname);
+ if(e){
+ // return an error if the entry exists but isn't a menu
+ if(e->type != MENUSUB){
+ free(tmppath);
+ return nil;
+ }
+ m = ((MenuEntry_Sub*)e)->submenu;
+ }else{
+ // Create submenus that don't exist yet
+ Menu *submenu = new Menu();
+ submenu->parent = m;
+ MenuEntry *me = new MenuEntry_Sub(curname, submenu);
+ // Don't sort submenus outside the toplevel menu
+ if(m == &toplevel)
+ m->insertEntrySorted(me);
+ else
+ m->appendEntry(me);
+ m = submenu;
+ }
+ }
+
+ free(tmppath);
+ return m;
+}
+
+/*
+ * ****************
+ * debug menu
+ * ****************
+ */
+
+static uint8 cursorPx[] = {
+#include "cursor.inc"
+};
+
+static uint8 arrowPx[] = {
+#include "arrow.inc"
+};
+
+void
+DebugMenuInit(void)
+{
+ createMenuFont();
+
+ RwInt32 w, h, d, flags;
+ RwImage *img = RwImageCreate(16, 16, 32);
+ assert(img);
+ RwImageSetPixels(img, cursorPx);
+ RwImageSetStride(img, RwImageGetWidth(img)*4);
+ RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags);
+ cursor = RwRasterCreate(w, h, d, flags);
+ cursor = RwRasterSetFromImage(cursor, img);
+ assert(cursor);
+ RwImageDestroy(img);
+
+ img = RwImageCreate(32, 16, 32);
+ assert(img);
+ RwImageSetPixels(img, arrowPx);
+ RwImageSetStride(img, RwImageGetWidth(img)*4);
+ RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags);
+ arrow = RwRasterCreate(w, h, d, flags);
+ arrow = RwRasterSetFromImage(arrow, img);
+ assert(arrow);
+ RwImageDestroy(img);
+
+
+ menuInitialized = true;
+}
+
+void
+DebugMenuShutdown(void)
+{
+ if(menuInitialized){
+ destroyMenuFont();
+ RwRasterDestroy(cursor);
+ cursor = nil;
+ RwRasterDestroy(arrow);
+ arrow = nil;
+
+ toplevel.~Menu();
+ new (&toplevel) Menu();
+
+ activeMenu = &toplevel;
+ deepestMenu = &toplevel;
+ mouseOverMenu = nil;
+ mouseOverEntry = nil;
+ }
+ menuInitialized = false;
+}
+
+void
+processInput(void)
+{
+ int shift = KEYDOWN(rsRSHIFT) || KEYDOWN(rsLSHIFT);
+#define X(var, keycode) var = KEYJUSTDOWN(keycode);
+ MUHKEYS
+#undef X
+
+ // Implement auto-repeat
+#define X(var, keycode) \
+ if(var){ \
+ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \
+ lastkeydown = keycode; \
+ keyptr = &var; \
+ }
+ MUHKEYS
+#undef X
+ if(lastkeydown){
+ if(KEYDOWN(lastkeydown)){
+ int curtime = CTimer::GetTimeInMilliseconds();
+ if(curtime - downtime > REPEATDELAY){
+ if(curtime - repeattime > REPEATINTERVAL){
+ repeattime = curtime;
+ *keyptr = 1;
+ }
+ }
+ }else{
+ lastkeydown = 0;
+ }
+ }
+
+ // Also for mouse buttons
+#define X(var, num) \
+ if(var){ \
+ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \
+ lastbuttondown = num; \
+ buttonptr = &var; \
+ }
+ MUHBUTTONS
+#undef X
+ if(lastbuttondown){
+ if(buttondown[lastbuttondown-1]){
+ int curtime = CTimer::GetTimeInMilliseconds();
+ if(curtime - downtime > REPEATDELAY){
+ if(curtime - repeattime > REPEATINTERVAL){
+ repeattime = curtime;
+ *buttonptr = 1;
+ }
+ }
+ }else{
+ lastbuttondown = 0;
+ }
+ }
+
+ // Walk through all visible menus and figure out which one the mouse is over
+ mouseOverMenu = nil;
+ mouseOverEntry = nil;
+ Menu *menu;
+ for(menu = deepestMenu; menu; menu = menu->parent)
+ if(isMouseInRect(menu->r)){
+ mouseOverMenu = menu;
+ break;
+ }
+ if(mouseOverMenu){
+ // Walk all visibile entries and figure out which one the mouse is over
+ MenuEntry *e;
+ int i = 0;
+ for(e = mouseOverMenu->entries; e; e = e->next){
+ if(i >= mouseOverMenu->scrollStart+mouseOverMenu->numVisible)
+ break;
+ if(i >= mouseOverMenu->scrollStart){
+ RwRect r = e->r;
+ r.w = mouseOverMenu->r.w; // span the whole menu
+ if(isMouseInRect(r)){
+ mouseOverEntry = e;
+ break;
+ }
+ }
+ i++;
+ }
+ if(mouseOverMenu->isScrollingUp && isMouseInRect(mouseOverMenu->scrollUpR)){
+ mouseOverEntry = &scrollUpEntry;
+ mouseOverEntry->r = mouseOverMenu->scrollUpR;
+ mouseOverEntry->menu = mouseOverMenu;
+ mouseOverEntry->type = MENUSCROLL;
+ }
+ if(mouseOverMenu->isScrollingDown && isMouseInRect(mouseOverMenu->scrollDownR)){
+ mouseOverEntry = &scrollDownEntry;
+ mouseOverEntry->r = mouseOverMenu->scrollDownR;
+ mouseOverEntry->menu = mouseOverMenu;
+ mouseOverEntry->type = MENUSCROLL;
+ }
+ }
+
+ if(pgupjustdown)
+ activeMenu->scroll(shift ? -5 : -1);
+ if(pgdnjustdown)
+ activeMenu->scroll(shift ? 5 : 1);
+ if(downjustdown)
+ activeMenu->changeSelection(activeMenu->selection + (shift ? 5 : 1));
+ if(upjustdown)
+ activeMenu->changeSelection(activeMenu->selection - (shift ? 5 : 1));
+
+ if(CPad::NewMouseControllerState.WHEELUP){
+ if(mouseOverMenu)
+ activeMenu = mouseOverMenu;
+ activeMenu->scroll(shift ? -5 : -1);
+ }
+ if(CPad::NewMouseControllerState.WHEELDN){
+ if(mouseOverMenu)
+ activeMenu = mouseOverMenu;
+ activeMenu->scroll(shift ? 5 : 1);
+ }
+
+ if(mouseOverEntry == &scrollUpEntry){
+ if(button1justdown){
+ activeMenu = mouseOverEntry->menu;
+ activeMenu->scroll(shift ? -5 : -1);
+ }
+ }
+ if(mouseOverEntry == &scrollDownEntry){
+ if(button1justdown){
+ activeMenu = mouseOverEntry->menu;
+ activeMenu->scroll(shift ? 5 : 1);
+ }
+ }
+
+ // Have to call this before processInput below because menu entry can change
+ if((button1justdown || button3justdown) && mouseOverEntry){
+ activeMenu = mouseOverEntry->menu;
+ activeMenu->changeSelection(mouseOverEntry);
+ }
+ if(KEYJUSTDOWN(rsENTER)){
+ if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUSUB)
+ activeMenu = ((MenuEntry_Sub*)activeMenu->selectedEntry)->submenu;
+ }else if(KEYJUSTDOWN(rsBACKSP)){
+ if(activeMenu->parent)
+ activeMenu = activeMenu->parent;
+ }else{
+ if(mouseOverEntry && mouseOverEntry->type == MENUVAR)
+ ((MenuEntry_Var*)mouseOverEntry)->processInput(true, mouseOverEntry == activeMenu->selectedEntry);
+ if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUVAR &&
+ mouseOverEntry != activeMenu->selectedEntry)
+ ((MenuEntry_Var*)activeMenu->selectedEntry)->processInput(false, true);
+ }
+}
+
+void
+updateMouse(void)
+{
+ CPad *pad = CPad::GetPad(0);
+ int dirX = 1;
+ int dirY = 1;
+
+ if(MousePointerStateHelper.bInvertHorizontally) dirX = -1;
+ if(MousePointerStateHelper.bInvertVertically) dirY = -1;
+
+ mouseX += pad->NewMouseControllerState.x*dirX;
+ mouseY += pad->NewMouseControllerState.y*dirY;
+
+ if(mouseX < 0.0f) mouseX = 0.0f;
+ if(mouseY < 0.0f) mouseY = 0.0f;
+ if(mouseX >= screenWidth) mouseX = screenWidth;
+ if(mouseY >= screenHeight) mouseY = screenHeight;
+
+ button1justdown = pad->NewMouseControllerState.LMB && !pad->OldMouseControllerState.LMB;
+ button2justdown = pad->NewMouseControllerState.MMB && !pad->OldMouseControllerState.MMB;
+ button3justdown = pad->NewMouseControllerState.RMB && !pad->OldMouseControllerState.RMB;
+ buttondown[0] = pad->NewMouseControllerState.LMB;
+ buttondown[1] = pad->NewMouseControllerState.MMB;
+ buttondown[2] = pad->NewMouseControllerState.RMB;
+
+ // Zero the mouse position so the camera won't move
+ pad->NewMouseControllerState.x = 0.0f;
+ pad->NewMouseControllerState.y = 0.0f;
+}
+
+void
+DebugMenuProcess(void)
+{
+ // We only process some input here
+
+ CPad *pad = CPad::GetPad(0);
+ if(CTRLJUSTDOWN('M'))
+ menuOn = !menuOn;
+ if(!menuOn)
+ return;
+
+ pad->DisablePlayerControls = 1;
+ // TODO: this could happen earlier
+ if(!menuInitialized)
+ DebugMenuInit();
+ updateMouse();
+
+}
+
+void
+DebugMenuRender(void)
+{
+ if(!menuOn)
+ return;
+
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, 0);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, 0);
+ RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
+
+ RwCamera *cam = RwCameraGetCurrentCamera();
+ screenWidth = RwRasterGetWidth(RwCameraGetRaster(cam));
+ screenHeight = RwRasterGetHeight(RwCameraGetRaster(cam));
+
+// if(screenHeight > 1080)
+// fontscale = 2;
+// else
+ fontscale = 1;
+
+ Pt sz;
+ sz = fontPrint("Debug Menu", firstBorder*fontscale+30, topBorder, 0);
+
+ toplevel.r.x = firstBorder*fontscale;
+ toplevel.r.y = topBorder + sz.y + 10;
+ toplevel.r.w = minwidth; // update menu will expand
+ toplevel.r.h = screenHeight - 10 - toplevel.r.y;
+ toplevel.update();
+ toplevel.draw();
+ processInput();
+ RtCharsetBufferFlush();
+
+ drawMouse();
+}
+
+
+
+void
+drawArrow(RwRect r, int direction, int style)
+{
+ static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 };
+ static RwIm2DVertex arrowVerts[4];
+
+ RwCamera *cam = RwCameraGetCurrentCamera();
+ float recipz = 1.0f/RwCameraGetNearClipPlane(cam);
+
+ int width = RwRasterGetWidth(arrow);
+ int height = RwRasterGetHeight(arrow);
+
+ int left = r.x + (r.w - width)/2;
+ int right = left + width;
+ int top = r.y;
+ int bottom = r.y+r.h;
+
+ float umin = HALFPX / width;
+ float vmin = HALFPX / height;
+ float umax = (width + HALFPX) / width;
+ float vmax = (height + HALFPX) / height;
+ if(direction < 0){
+ vmin = (height - HALFPX) / height;
+ vmax = -HALFPX / height;
+ }
+
+ if(style){
+ RwIm2DVertexSetScreenX(&arrowVerts[0], r.x);
+ RwIm2DVertexSetScreenY(&arrowVerts[0], r.y-1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[0], 132, 132, 132, 255);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[1], r.x+r.w);
+ RwIm2DVertexSetScreenY(&arrowVerts[1], r.y-1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[1], 132, 132, 132, 255);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[2], r.x);
+ RwIm2DVertexSetScreenY(&arrowVerts[2], r.y+r.h+1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[2], 132, 132, 132, 255);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[3], r.x+r.w);
+ RwIm2DVertexSetScreenY(&arrowVerts[3], r.y+r.h+1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[3], 132, 132, 132, 255);
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6);
+ }
+
+
+ RwIm2DVertexSetScreenX(&arrowVerts[0], left);
+ RwIm2DVertexSetScreenY(&arrowVerts[0], top);
+ RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[0], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[0], umin, recipz);
+ RwIm2DVertexSetV(&arrowVerts[0], vmin, recipz);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[1], right);
+ RwIm2DVertexSetScreenY(&arrowVerts[1], top);
+ RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[1], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[1], umax, recipz);
+ RwIm2DVertexSetV(&arrowVerts[1], vmin, recipz);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[2], left);
+ RwIm2DVertexSetScreenY(&arrowVerts[2], bottom);
+ RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[2], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[2], umin, recipz);
+ RwIm2DVertexSetV(&arrowVerts[2], vmax, recipz);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[3], right);
+ RwIm2DVertexSetScreenY(&arrowVerts[3], bottom);
+ RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[3], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[3], umax, recipz);
+ RwIm2DVertexSetV(&arrowVerts[3], vmax, recipz);
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, arrow);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6);
+}
+
+void
+drawMouse(void)
+{
+ static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 };
+ static RwIm2DVertex vertices[4];
+ RwIm2DVertex *vert;
+ RwCamera *cam;
+ cam = RwCameraGetCurrentCamera();
+ float x = mouseX;
+ float y = mouseY;
+ float w = RwRasterGetWidth(cursor);
+ float h = RwRasterGetHeight(cursor);
+ float recipz = 1.0f/RwCameraGetNearClipPlane(cam);
+
+ float umin = HALFPX / w;
+ float vmin = HALFPX / h;
+ float umax = (w + HALFPX) / w;
+ float vmax = (h + HALFPX) / h;
+
+ vert = vertices;
+ RwIm2DVertexSetScreenX(vert, x);
+ RwIm2DVertexSetScreenY(vert, y);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umin, recipz);
+ RwIm2DVertexSetV(vert, vmin, recipz);
+ vert++;
+
+ RwIm2DVertexSetScreenX(vert, x+w);
+ RwIm2DVertexSetScreenY(vert, y);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umax, recipz);
+ RwIm2DVertexSetV(vert, vmin, recipz);
+ vert++;
+
+ RwIm2DVertexSetScreenX(vert, x);
+ RwIm2DVertexSetScreenY(vert, y+h);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umin, recipz);
+ RwIm2DVertexSetV(vert, vmax, recipz);
+ vert++;
+
+ RwIm2DVertexSetScreenX(vert, x+w);
+ RwIm2DVertexSetScreenY(vert, y+h);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umax, recipz);
+ RwIm2DVertexSetV(vert, vmax, recipz);
+ vert++;
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, cursor);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, vertices, 4, indices, 6);
+}
+
+
+
+
+/*
+ * Generate interfaces
+ */
+
+
+#define X(NAME, TYPE, unused1, unused2) \
+MenuEntry* \
+DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \
+{ \
+ Menu *m = findMenu(path); \
+ if(m == nil) \
+ return nil; \
+ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound, strings); \
+ m->appendEntry(e); \
+ return e; \
+}
+INTTYPES
+#undef X
+
+#define X(NAME, TYPE, unused1, unused2) \
+MenuEntry* \
+DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \
+{ \
+ Menu *m = findMenu(path); \
+ if(m == nil) \
+ return nil; \
+ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound); \
+ m->appendEntry(e); \
+ return e; \
+}
+FLOATTYPES
+#undef X
+
+MenuEntry* \
+DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc)
+{
+ Menu *m = findMenu(path);
+ if(m == nil)
+ return nil;
+ MenuEntry *e = new MenuEntry_Cmd(name, triggerFunc);
+ m->appendEntry(e);
+ return e;
+}
+
+void
+DebugMenuEntrySetWrap(MenuEntry *e, bool wrap)
+{
+ if(e && e->type == MENUVAR)
+ ((MenuEntry_Var*)e)->wrapAround = wrap;
+}
+
+void
+DebugMenuEntrySetStrings(MenuEntry *e, const char **strings)
+{
+ if(e && e->type == MENUVAR_INT)
+ ((MenuEntry_Int*)e)->setStrings(strings);
+}
+
+void
+DebugMenuEntrySetAddress(MenuEntry *e, void *addr)
+{
+ if(e && e->type == MENUVAR){
+ MenuEntry_Var *ev = (MenuEntry_Var*)e;
+ // HACK - we know the variable field is at the same address
+ // for all int/float classes. let's hope it stays that way.
+ if(ev->vartype = MENUVAR_INT)
+ ((MenuEntry_Int32*)e)->variable = (int32*)addr;
+ else if(ev->vartype = MENUVAR_FLOAT)
+ ((MenuEntry_Float32*)e)->variable = (float*)addr;
+ }
+}
+#endif \ No newline at end of file
diff --git a/src/extras/debugmenu.h b/src/extras/debugmenu.h
new file mode 100644
index 0000000..c2198ac
--- /dev/null
+++ b/src/extras/debugmenu.h
@@ -0,0 +1,90 @@
+#pragma once
+
+#ifdef DEBUGMENU
+
+typedef void (*TriggerFunc)(void);
+
+struct Menu;
+
+struct MenuEntry
+{
+ int type;
+ const char *name;
+ MenuEntry *next;
+ RwRect r;
+ Menu *menu;
+
+ MenuEntry(const char *name);
+ virtual ~MenuEntry(void) { free((void*)name); }
+};
+
+typedef MenuEntry DebugMenuEntry;
+
+MenuEntry *DebugMenuAddInt8(const char *path, const char *name, int8 *ptr, TriggerFunc triggerFunc, int8 step, int8 lowerBound, int8 upperBound, const char **strings);
+MenuEntry *DebugMenuAddInt16(const char *path, const char *name, int16 *ptr, TriggerFunc triggerFunc, int16 step, int16 lowerBound, int16 upperBound, const char **strings);
+MenuEntry *DebugMenuAddInt32(const char *path, const char *name, int32 *ptr, TriggerFunc triggerFunc, int32 step, int32 lowerBound, int32 upperBound, const char **strings);
+MenuEntry *DebugMenuAddInt64(const char *path, const char *name, int64 *ptr, TriggerFunc triggerFunc, int64 step, int64 lowerBound, int64 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt8(const char *path, const char *name, uint8 *ptr, TriggerFunc triggerFunc, uint8 step, uint8 lowerBound, uint8 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt16(const char *path, const char *name, uint16 *ptr, TriggerFunc triggerFunc, uint16 step, uint16 lowerBound, uint16 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt32(const char *path, const char *name, uint32 *ptr, TriggerFunc triggerFunc, uint32 step, uint32 lowerBound, uint32 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt64(const char *path, const char *name, uint64 *ptr, TriggerFunc triggerFunc, uint64 step, uint64 lowerBound, uint64 upperBound, const char **strings);
+MenuEntry *DebugMenuAddFloat32(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound);
+MenuEntry *DebugMenuAddFloat64(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound);
+MenuEntry *DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc);
+void DebugMenuEntrySetWrap(MenuEntry *e, bool wrap);
+void DebugMenuEntrySetStrings(MenuEntry *e, const char **strings);
+void DebugMenuEntrySetAddress(MenuEntry *e, void *addr);
+void DebugMenuInit(void);
+void DebugMenuShutdown(void);
+void DebugMenuProcess(void);
+void DebugMenuRender(void);
+
+
+// Some overloads for simplicity
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings)
+{ return DebugMenuAddInt8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings)
+{ return DebugMenuAddInt16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings)
+{ return DebugMenuAddInt32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings)
+{ return DebugMenuAddInt64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings)
+{ return DebugMenuAddUInt8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings)
+{ return DebugMenuAddUInt16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings)
+{ return DebugMenuAddUInt32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings)
+{ return DebugMenuAddUInt64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound)
+{ return DebugMenuAddFloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound)
+{ return DebugMenuAddFloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
+
+inline DebugMenuEntry *DebugMenuAddVarBool32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc)
+{
+ static const char *boolstr[] = { "Off", "On" };
+ DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
+ DebugMenuEntrySetWrap(e, true);
+ return e;
+}
+inline DebugMenuEntry *DebugMenuAddVarBool16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc)
+{
+ static const char *boolstr[] = { "Off", "On" };
+ DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
+ DebugMenuEntrySetWrap(e, true);
+ return e;
+}
+inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc)
+{
+ static const char *boolstr[] = { "Off", "On" };
+ DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
+ DebugMenuEntrySetWrap(e, true);
+ return e;
+}
+inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, bool *ptr, TriggerFunc triggerFunc)
+{
+ return DebugMenuAddVarBool8(path, name, (int8_t*)ptr, triggerFunc);
+}
+#endif \ No newline at end of file
diff --git a/src/extras/frontendoption.cpp b/src/extras/frontendoption.cpp
new file mode 100644
index 0000000..2660e75
--- /dev/null
+++ b/src/extras/frontendoption.cpp
@@ -0,0 +1,170 @@
+#include "common.h"
+
+#ifdef CUSTOM_FRONTEND_OPTIONS
+#include "Frontend.h"
+#include "Text.h"
+
+int lastOgScreen = MENUPAGES; // means no new pages
+
+int numCustomFrontendOptions = 0;
+int numCustomFrontendScreens = 0;
+
+int optionCursor = -2;
+int currentMenu;
+bool optionOverwrite = false;
+
+void GoBack()
+{
+ FrontEndMenuManager.SwitchToNewScreen(-1);
+}
+
+uint8
+GetNumberOfMenuOptions(int screen)
+{
+ uint8 Rows = 0;
+ for (int i = 0; i < NUM_MENUROWS; i++) {
+ if (aScreens[screen].m_aEntries[i].m_Action == MENUACTION_NOTHING)
+ break;
+
+ ++Rows;
+ }
+ return Rows;
+}
+
+uint8
+GetLastMenuScreen()
+{
+ int8 page = -1;
+ for (int i = 0; i < MENUPAGES; i++) {
+ if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].m_PreviousPage == MENUPAGE_NONE)
+ break;
+
+ ++page;
+ }
+ return page;
+}
+
+int8 RegisterNewScreen(const char *name, int prevPage, ReturnPrevPageFunc returnPrevPageFunc)
+{
+ if (lastOgScreen == MENUPAGES)
+ lastOgScreen = GetLastMenuScreen();
+
+ numCustomFrontendScreens++;
+ int id = lastOgScreen + numCustomFrontendScreens;
+ assert(id < MENUPAGES && "No room for new custom frontend screens! Increase MENUPAGES");
+ strncpy(aScreens[id].m_ScreenName, name, 8);
+ aScreens[id].m_PreviousPage = prevPage;
+ aScreens[id].returnPrevPageFunc = returnPrevPageFunc;
+ return id;
+}
+
+int8 RegisterNewOption()
+{
+ numCustomFrontendOptions++;
+ uint8 numOptions = GetNumberOfMenuOptions(currentMenu);
+ uint8 curIdx;
+ if (optionCursor < 0) {
+ optionCursor = curIdx = numOptions + optionCursor + 1;
+ } else
+ curIdx = optionCursor;
+
+ if (!optionOverwrite) {
+ if (aScreens[currentMenu].m_aEntries[curIdx].m_Action != MENUACTION_NOTHING) {
+ for (int i = numOptions - 1; i >= curIdx; i--) {
+ memcpy(&aScreens[currentMenu].m_aEntries[i + 1], &aScreens[currentMenu].m_aEntries[i], sizeof(CMenuScreenCustom::CMenuEntry));
+ }
+ }
+ }
+ optionCursor++;
+ return curIdx;
+}
+
+void FrontendOptionSetCursor(int screen, int8 option, bool overwrite)
+{
+ currentMenu = screen;
+ optionCursor = option;
+ optionOverwrite = overwrite;
+}
+
+void FrontendOptionAddBuiltinAction(const char* gxtKey, uint16 x, uint16 y, uint8 align, int action, int targetMenu, int saveSlot) {
+ int8 screenOptionOrder = RegisterNewOption();
+
+ CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder];
+
+ // We can't use custom text on those :shrug:
+ switch (action) {
+ case MENUACTION_SCREENRES:
+ strcpy(option.m_EntryName, "FED_RES");
+ break;
+ case MENUACTION_AUDIOHW:
+ strcpy(option.m_EntryName, "FEA_3DH");
+ break;
+ default:
+ strncpy(option.m_EntryName, gxtKey, 8);
+ break;
+ }
+ option.m_X = x;
+ option.m_Y = y;
+ option.m_Align = align;
+ option.m_Action = action;
+ option.m_SaveSlot = saveSlot;
+ option.m_TargetMenu = targetMenu;
+}
+
+void FrontendOptionAddSelect(const char* gxtKey, uint16 x, uint16 y, uint8 align, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveCat, const char* saveName, bool disableIfGameLoaded)
+{
+ int8 screenOptionOrder = RegisterNewOption();
+
+ CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder];
+ option.m_Action = MENUACTION_CFO_SELECT;
+ option.m_X = x;
+ option.m_Y = y;
+ option.m_Align = align;
+ strncpy(option.m_EntryName, gxtKey, 8);
+ option.m_CFOSelect = new CCFOSelect();
+ option.m_CFOSelect->rightTexts = (char**)malloc(numRightTexts * sizeof(char*));
+ memcpy(option.m_CFOSelect->rightTexts, rightTexts, numRightTexts * sizeof(char*));
+ option.m_CFOSelect->numRightTexts = numRightTexts;
+ option.m_CFOSelect->value = var;
+ if (var) {
+ option.m_CFOSelect->displayedValue = *var;
+ option.m_CFOSelect->lastSavedValue = *var;
+ }
+ option.m_CFOSelect->saveCat = saveCat;
+ option.m_CFOSelect->save = saveName;
+ option.m_CFOSelect->onlyApplyOnEnter = onlyApplyOnEnter;
+ option.m_CFOSelect->changeFunc = changeFunc;
+ option.m_CFOSelect->disableIfGameLoaded = disableIfGameLoaded;
+}
+
+void FrontendOptionAddDynamic(const char* gxtKey, uint16 x, uint16 y, uint8 align, DrawFunc drawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveCat, const char* saveName)
+{
+ int8 screenOptionOrder = RegisterNewOption();
+
+ CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder];
+ option.m_Action = MENUACTION_CFO_DYNAMIC;
+ option.m_X = x;
+ option.m_Y = y;
+ option.m_Align = align;
+ strncpy(option.m_EntryName, gxtKey, 8);
+ option.m_CFODynamic = new CCFODynamic();
+ option.m_CFODynamic->drawFunc = drawFunc;
+ option.m_CFODynamic->buttonPressFunc = buttonPressFunc;
+ option.m_CFODynamic->value = var;
+ option.m_CFODynamic->saveCat = saveCat;
+ option.m_CFODynamic->save = saveName;
+}
+
+// lineHeight = 0 means game will use MENU_DEFAULT_LINE_HEIGHT
+uint8 FrontendScreenAdd(const char* gxtKey, int prevPage, int lineHeight, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc) {
+
+ uint8 screenOrder = RegisterNewScreen(gxtKey, prevPage, returnPrevPageFunc);
+
+ CCustomScreenLayout *screen = new CCustomScreenLayout();
+ aScreens[screenOrder].layout = screen;
+ screen->lineHeight = lineHeight;
+ screen->showLeftRightHelper = showLeftRightHelper;
+
+ return screenOrder;
+}
+#endif
diff --git a/src/extras/frontendoption.h b/src/extras/frontendoption.h
new file mode 100644
index 0000000..05cd5fa
--- /dev/null
+++ b/src/extras/frontendoption.h
@@ -0,0 +1,86 @@
+#pragma once
+#include "common.h"
+
+#ifdef CUSTOM_FRONTEND_OPTIONS
+
+// ! There are 2 ways to use CFO,
+// 1st; by adding a new option to the array in MenuScreensCustom.cpp and passing attributes/CBs to it
+// 2nd; by calling the functions listed at the bottom of this file.
+
+// -- Option types
+//
+// Static/select: You allocate the variable, pass it to function and game sets it from user input among the strings given to function,
+// optionally you can add post-change event via ChangeFunc(only called on enter if onlyApplyOnEnter set, or set immediately)
+// You can store the option in an INI file if you pass the key(as a char array) to corresponding parameter.
+//
+// Dynamic: Passing variable to function is only needed if you want to store it, otherwise you should do
+// all the operations with ButtonPressFunc, this includes allocating the variable.
+// Left-side text is passed while creating and static, but ofc right-side text is dynamic -
+// you should return it in DrawFunc, which is called on every draw.
+//
+// Built-in action: As the name suggests, any action that game has built-in. But as an extra you can set the option text,
+
+// -- Returned via ButtonPressFunc() action param.
+#define FEOPTION_ACTION_LEFT 0
+#define FEOPTION_ACTION_RIGHT 1
+#define FEOPTION_ACTION_SELECT 2
+#define FEOPTION_ACTION_FOCUSLOSS 3
+
+// -- Callbacks
+
+// pretty much in everything I guess, and optional in all of them
+typedef void (*ReturnPrevPageFunc)();
+
+// for static options
+typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value.
+ // only called on enter if onlyApplyOnEnter set, otherwise called on every value change
+
+// for dynamic options
+typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must return a pointer for right text.
+ // you can also set *disabled if you want to gray it out.
+typedef void (*ButtonPressFunc)(int8 action); // see FEOPTION_ACTIONs above
+
+// -- Internal things
+void CustomFrontendOptionsPopulate();
+extern int lastOgScreen; // for reloading
+extern int numCustomFrontendOptions;
+extern int numCustomFrontendScreens;
+
+// -- To be used in ButtonPressFunc / ChangeFunc(this one would be weird):
+void GoBack(void);
+
+uint8 GetNumberOfMenuOptions(int screen);
+
+// !!! We're now moved to MenuScreensCustom.cpp, which houses an array that keeps all original+custom options.
+// But you can still use the APIs below, and manipulate aScreens while in game.
+
+// Limits:
+// The code relies on that you won't use more then NUM_MENUROWS(18) options on one page, and won't exceed the MENUPAGES of pages.
+// Also congrats if you can make 18 options visible at once.
+
+// Texts:
+// All text parameters accept char[8] GXT key.
+
+// Execute direction:
+// All of the calls below eventually manipulate the aScreens array, so keep in mind to add/replace options in order,
+// i.e. don't set cursor to 8 first and then 3.
+
+
+// -- Placing the cursor to append/overwrite option
+//
+// Done via FrontendOptionSetCursor(screen, position, overwrite = false), parameters explained below:
+// Screen: as the name suggests. Also accepts the screen IDs returned from FrontendScreenAdd.
+// Option: if positive, next AddOption call will put the option to there and progress the cursor.
+// if negative, cursor will be placed on bottom-(pos+1), so -1 means the very bottom, -2 means before the back button etc.
+// Overwrite: Use to overwrite the options, not appending a new one. AddOption calls will still progress the cursor.
+
+void FrontendOptionSetCursor(int screen, int8 option, bool overwrite = false);
+
+// var is optional in AddDynamic, enables you to save them in an INI file(also needs passing char array to saveCat and saveKey param. obv), otherwise pass nil/0
+void FrontendOptionAddBuiltinAction(const char* gxtKey, uint16 x, uint16 y, uint8 align, int action, int targetMenu = MENUPAGE_NONE, int saveSlot = SAVESLOT_NONE);
+void FrontendOptionAddSelect(const char* gxtKey, uint16 x, uint16 y, uint8 align, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveCat = nil, const char* saveKey = nil, bool disableIfGameLoaded = false);
+void FrontendOptionAddDynamic(const char* gxtKey, uint16 x, uint16 y, uint8 align, DrawFunc rightTextDrawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveCat = nil, const char* saveKey = nil);
+
+// lineHeight = 0 means game will use MENU_DEFAULT_LINE_HEIGHT
+uint8 FrontendScreenAdd(const char* gxtKey, int prevPage, int lineHeight, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc = nil);
+#endif
diff --git a/src/extras/ini_parser.hpp b/src/extras/ini_parser.hpp
new file mode 100644
index 0000000..7bea024
--- /dev/null
+++ b/src/extras/ini_parser.hpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2013-2015 Denilson das Mercês Amorim <[email protected]>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#ifndef LINB_INI_PARSER_HPP
+#define LINB_INI_PARSER_HPP
+
+/*
+ * STL-like INI Container
+ */
+
+#include <string> // for std::string
+#include <map> // for std::map
+#include <cstdio> // for std::FILE
+#include <algorithm> // for std::find_if
+#include <functional> // for std::function
+
+namespace linb
+{
+ template<
+ class CharT = char, /* Not compatible with other type here, since we're using C streams */
+ class StringType = std::basic_string<CharT>,
+ class KeyContainer = std::map<StringType, StringType>,
+ class SectionContainer = std::map<StringType, KeyContainer>
+ > class basic_ini
+ {
+ public:
+ typedef CharT char_type;
+ typedef StringType string_type;
+ typedef KeyContainer key_container;
+ typedef SectionContainer section_container;
+
+ // Typedef container values types
+ typedef typename section_container::value_type value_type;
+ typedef typename section_container::key_type key_type;
+ typedef typename section_container::mapped_type mapped_type;
+
+ // Typedef common types
+ typedef typename section_container::size_type size_type;
+ typedef typename section_container::difference_type difference_type;
+
+ // Typedef iterators
+ typedef typename section_container::iterator iterator;
+ typedef typename section_container::const_iterator const_iterator;
+ typedef typename section_container::reverse_iterator reverse_iterator;
+ typedef typename section_container::const_reverse_iterator const_reverse_iterator;
+
+ // typedef References and pointers
+ typedef typename section_container::reference reference;
+ typedef typename section_container::const_reference const_reference;
+ typedef typename section_container::pointer pointer;
+ typedef typename section_container::const_pointer const_pointer;
+
+ private:
+ section_container data;
+
+ public:
+
+ basic_ini()
+ { }
+
+ basic_ini(const char_type* filename)
+ { this->read_file(filename); }
+
+ /* Iterator methods */
+ iterator begin()
+ { return data.begin(); }
+ const_iterator begin() const
+ { return data.begin(); }
+ iterator end()
+ { return data.end(); }
+ const_iterator end() const
+ { return data.end(); }
+ const_iterator cbegin() const
+ { return data.cbegin(); }
+ const_iterator cend() const
+ { return data.cend(); }
+
+ /* Reverse iterator methods */
+ reverse_iterator rbegin()
+ { return data.rbegin(); }
+ const_reverse_iterator rbegin() const
+ { return data.rbegin(); }
+ reverse_iterator rend()
+ { return data.rend(); }
+ const_reverse_iterator rend() const
+ { return data.rend(); }
+ const_reverse_iterator crbegin() const
+ { return data.crbegin(); }
+ const_reverse_iterator crend() const
+ { return data.crend(); }
+
+ /* Acessing index methods */
+ mapped_type& operator[](const string_type& sect)
+ { return data[sect]; }
+ mapped_type& operator[](string_type&& sect)
+ { return data[std::forward<string_type>(sect)]; }
+ mapped_type& at( const string_type& sect)
+ { return data.at(sect); }
+ const mapped_type& at(const string_type& sect) const
+ { return data.at(sect); }
+
+ /* Capacity information */
+ bool empty() const
+ { return data.empty(); }
+ size_type size() const
+ { return data.size(); }
+ size_type max_size() const
+ { return data.max_size(); }
+
+ /* Modifiers */
+ void clear()
+ { return data.clear(); }
+
+ /* Lookup */
+ size_type count(const string_type& sect)
+ { return data.count(sect); }
+ iterator find(const string_type& sect)
+ { return data.find(sect); }
+
+ /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */
+ string_type get(const string_type& sect, const key_type& key, const string_type& default_value)
+ {
+ auto it = this->find(sect);
+ if(it != this->end())
+ {
+ auto itv = it->second.find(key);
+ if(itv != it->second.end())
+ return itv->second;
+ }
+ return default_value;
+ }
+
+ /* Sets the value of a value in the ini */
+ void set(const string_type& sect, const key_type& key, const string_type& value)
+ {
+ (*this)[sect][key] = value; // no emplace since overwrite!
+ }
+
+ /* Too lazy to continue this container... If you need more methods, just add it */
+
+ // re3
+ void remove(const string_type& sect, const key_type& key)
+ {
+ auto it = this->find(sect);
+ if(it != this->end())
+ {
+ it->second.erase(key);
+ }
+ }
+
+ int category_size(const string_type& sect)
+ {
+ auto it = this->find(sect);
+ if(it != this->end())
+ {
+ return it->second.size();
+ }
+ return 0;
+ }
+
+#if 1
+ bool read_file(const char_type* filename)
+ {
+ /* Using C stream in a STL-like container, funny?
+ */
+ if(FILE* f = fopen(filename, "r"))
+ {
+ key_container* keys = nullptr;
+ char_type buf[2048];
+ string_type line;
+ string_type key;
+ string_type value;
+ string_type null_string;
+ size_type pos;
+
+ // Trims an string
+ auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type&
+ {
+ if(s.size())
+ {
+ // Ignore UTF-8 BOM
+ while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF))
+ s.erase(s.begin(), s.begin() + 3);
+
+ if(trimLeft)
+ s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function<int(int)>(::isspace))));
+ if(trimRight)
+ s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function<int(int)>(::isspace))).base(), s.end());
+ }
+ return s;
+ };
+
+ // Start parsing
+ while(fgets(buf, sizeof(buf), f))
+ {
+ // What a thing, reading into a char buffer and then putting in the string...
+ line = buf;
+
+ // Find comment and remove anything after it from the line
+ if((pos = line.find_first_of(';')) != line.npos)
+ line.erase(pos);
+
+ // Trim the string, and if it gets empty, skip this line
+ if(trim(line, true, true).empty())
+ continue;
+
+ // Find section name
+ if(line.front() == '[' && line.back() == ']')
+ {
+ pos = line.length() - 1; //line.find_first_of(']');
+ if(pos != line.npos)
+ {
+ trim(key.assign(line, 1, pos-1), true, true);
+ keys = &data[std::move(key)]; // Create section
+ }
+ else
+ keys = nullptr;
+ }
+ else
+ {
+ // Find key and value positions
+ pos = line.find_first_of('=');
+ if(pos == line.npos)
+ {
+ // There's only the key
+ key = line; // No need for trim, line is already trimmed
+ value.clear();
+ }
+ else
+ {
+ // There's the key and the value
+ trim(key.assign(line, 0, pos), false, true); // trim the right
+ trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left
+ }
+
+ // Put the key/value into the current keys object, or into the section "" if no section has been found
+ #if __cplusplus >= 201103L || _MSC_VER >= 1800
+ (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value));
+ #else
+ (keys ? *keys : data[null_string])[key] = value;
+ key.clear(); value.clear();
+ #endif
+ }
+ }
+
+ fclose(f);
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Dumps the content of this container into an ini file
+ */
+ bool write_file(const char_type* filename)
+ {
+ if(FILE* f = fopen(filename, "w"))
+ {
+ bool first = true;
+ for(auto& sec : this->data)
+ {
+ fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str());
+ first = false;
+ for(auto& kv : sec.second)
+ {
+ if(kv.second.empty())
+ fprintf(f, "%s\n", kv.first.c_str());
+ else
+ fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str());
+ }
+ }
+ fclose(f);
+ return true;
+ }
+ return false;
+ }
+
+
+ /*
+ */
+ bool load_file(const char_type* filename)
+ {
+ return read_file(filename);
+ }
+
+ bool load_file(const StringType& filename)
+ {
+ return load_file(filename.c_str());
+ }
+
+ bool write_file(const StringType& filename)
+ {
+ return write_file(filename.c_str());
+ }
+#endif
+
+
+
+ };
+
+
+ /* Use default basic_ini
+ *
+ * Limitations:
+ * * Not unicode aware
+ * * Case sensitive
+ * * Sections must have unique keys
+ */
+ typedef basic_ini<> ini;
+}
+
+#endif
+
diff --git a/src/extras/postfx.cpp b/src/extras/postfx.cpp
new file mode 100644
index 0000000..f698bd5
--- /dev/null
+++ b/src/extras/postfx.cpp
@@ -0,0 +1,491 @@
+#define WITHD3D
+#include "common.h"
+
+#ifdef EXTENDED_COLOURFILTER
+
+#ifndef LIBRW
+#error "Need librw for EXTENDED_COLOURFILTER"
+#endif
+
+#include "main.h"
+#include "RwHelper.h"
+#include "Camera.h"
+#include "MBlur.h"
+#include "postfx.h"
+
+RwRaster *CPostFX::pFrontBuffer;
+RwRaster *CPostFX::pBackBuffer;
+bool CPostFX::bJustInitialised;
+int CPostFX::EffectSwitch = POSTFX_NORMAL;
+bool CPostFX::BlurOn = false;
+bool CPostFX::MotionBlurOn = false;
+
+static RwIm2DVertex Vertex[4];
+static RwIm2DVertex Vertex2[4];
+static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 };
+
+#ifdef RW_D3D9
+void *colourfilterVC_PS;
+void *contrast_PS;
+#endif
+#ifdef RW_OPENGL
+int32 u_blurcolor;
+int32 u_contrastAdd;
+int32 u_contrastMult;
+rw::gl3::Shader *colourFilterVC;
+rw::gl3::Shader *contrast;
+#endif
+
+void
+CPostFX::InitOnce(void)
+{
+#ifdef RW_OPENGL
+ u_blurcolor = rw::gl3::registerUniform("u_blurcolor");
+ u_contrastAdd = rw::gl3::registerUniform("u_contrastAdd");
+ u_contrastMult = rw::gl3::registerUniform("u_contrastMult");
+#endif
+}
+
+void
+CPostFX::Open(RwCamera *cam)
+{
+ uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1);
+ uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1);
+ uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam));
+ pFrontBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE);
+ pBackBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE);
+ bJustInitialised = true;
+
+ float zero, xmax, ymax;
+
+ if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){
+ zero = HALFPX;
+ xmax = width + HALFPX;
+ ymax = height + HALFPX;
+ }else{
+ zero = -HALFPX;
+ xmax = width - HALFPX;
+ ymax = height - HALFPX;
+ }
+
+ RwIm2DVertexSetScreenX(&Vertex[0], zero);
+ RwIm2DVertexSetScreenY(&Vertex[0], zero);
+ RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255);
+
+ RwIm2DVertexSetScreenX(&Vertex[1], zero);
+ RwIm2DVertexSetScreenY(&Vertex[1], ymax);
+ RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255);
+
+ RwIm2DVertexSetScreenX(&Vertex[2], xmax);
+ RwIm2DVertexSetScreenY(&Vertex[2], ymax);
+ RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255);
+
+ RwIm2DVertexSetScreenX(&Vertex[3], xmax);
+ RwIm2DVertexSetScreenY(&Vertex[3], zero);
+ RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255);
+
+
+ RwIm2DVertexSetScreenX(&Vertex2[0], zero + 2.0f);
+ RwIm2DVertexSetScreenY(&Vertex2[0], zero + 2.0f);
+ RwIm2DVertexSetScreenZ(&Vertex2[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex2[0], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex2[0], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex2[0], 255, 255, 255, 255);
+
+ RwIm2DVertexSetScreenX(&Vertex2[1], 2.0f);
+ RwIm2DVertexSetScreenY(&Vertex2[1], ymax + 2.0f);
+ RwIm2DVertexSetScreenZ(&Vertex2[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex2[1], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex2[1], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex2[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex2[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex2[1], 255, 255, 255, 255);
+
+ RwIm2DVertexSetScreenX(&Vertex2[2], xmax + 2.0f);
+ RwIm2DVertexSetScreenY(&Vertex2[2], ymax + 2.0f);
+ RwIm2DVertexSetScreenZ(&Vertex2[2], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex2[2], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex2[2], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex2[2], 255, 255, 255, 255);
+
+ RwIm2DVertexSetScreenX(&Vertex2[3], xmax + 2.0f);
+ RwIm2DVertexSetScreenY(&Vertex2[3], zero + 2.0f);
+ RwIm2DVertexSetScreenZ(&Vertex2[3], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&Vertex2[3], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&Vertex2[3], 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetU(&Vertex2[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetV(&Vertex2[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetIntRGBA(&Vertex2[3], 255, 255, 255, 255);
+
+
+#ifdef RW_D3D9
+#include "shaders/obj/colourfilterVC_PS.inc"
+ colourfilterVC_PS = rw::d3d::createPixelShader(colourfilterVC_PS_cso);
+#include "shaders/obj/contrastPS.inc"
+ contrast_PS = rw::d3d::createPixelShader(contrastPS_cso);
+#endif
+#ifdef RW_OPENGL
+ using namespace rw::gl3;
+
+ {
+#include "shaders/obj/im2d_vert.inc"
+#include "shaders/obj/colourfilterVC_frag.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, colourfilterVC_frag_src, nil };
+ colourFilterVC = Shader::create(vs, fs);
+ assert(colourFilterVC);
+ }
+
+ {
+#include "shaders/obj/im2d_vert.inc"
+#include "shaders/obj/contrast_frag.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, contrast_frag_src, nil };
+ contrast = Shader::create(vs, fs);
+ assert(contrast);
+ }
+
+#endif
+}
+
+void
+CPostFX::Close(void)
+{
+ if(pFrontBuffer){
+ RwRasterDestroy(pFrontBuffer);
+ pFrontBuffer = nil;
+ }
+ if(pBackBuffer){
+ RwRasterDestroy(pBackBuffer);
+ pBackBuffer = nil;
+ }
+#ifdef RW_D3D9
+ if(colourfilterVC_PS){
+ rw::d3d::destroyPixelShader(colourfilterVC_PS);
+ colourfilterVC_PS = nil;
+ }
+ if(contrast_PS){
+ rw::d3d::destroyPixelShader(contrast_PS);
+ contrast_PS = nil;
+ }
+#endif
+#ifdef RW_OPENGL
+ if(colourFilterVC){
+ colourFilterVC->destroy();
+ colourFilterVC = nil;
+ }
+ if(contrast){
+ contrast->destroy();
+ contrast = nil;
+ }
+#endif
+}
+
+void
+CPostFX::RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
+{
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+
+ RwIm2DVertexSetIntRGBA(&Vertex[0], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex[1], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex[2], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex[3], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex2[0], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex2[1], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex2[2], r*2, g*2, b*2, 30);
+ RwIm2DVertexSetIntRGBA(&Vertex2[3], r*2, g*2, b*2, 30);
+
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, BlurOn ? Vertex2 : Vertex, 4, Index, 6);
+
+
+ RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a);
+ RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a);
+
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, BlurOn ? Vertex2 : Vertex, 4, Index, 6);
+}
+
+void
+CPostFX::RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
+{
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+
+ RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, 80);
+ RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, 80);
+ RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, 80);
+ RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, 80);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
+}
+
+float CPostFX::Intensity = 1.0f;
+
+void
+CPostFX::RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a)
+{
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pBackBuffer);
+
+ if(EffectSwitch == POSTFX_MOBILE){
+ float mult[3], add[3];
+ mult[0] = (r-64)/256.0f + 1.4f;
+ mult[1] = (g-64)/256.0f + 1.4f;
+ mult[2] = (b-64)/256.0f + 1.4f;
+ add[0] = r/1536.f - 0.05f;
+ add[1] = g/1536.f - 0.05f;
+ add[2] = b/1536.f - 0.05f;
+#ifdef RW_D3D9
+ rw::d3d::d3ddevice->SetPixelShaderConstantF(10, mult, 1);
+ rw::d3d::d3ddevice->SetPixelShaderConstantF(11, add, 1);
+
+ rw::d3d::im2dOverridePS = contrast_PS;
+#endif
+#ifdef RW_OPENGL
+ rw::gl3::im2dOverrideShader = contrast;
+ contrast->use();
+ glUniform3fv(contrast->uniformLocations[u_contrastMult], 1, mult);
+ glUniform3fv(contrast->uniformLocations[u_contrastAdd], 1, add);
+#endif
+ }else{
+ float f = Intensity;
+ float blurcolors[4];
+ blurcolors[0] = r*f/255.0f;
+ blurcolors[1] = g*f/255.0f;
+ blurcolors[2] = b*f/255.0f;
+ blurcolors[3] = 30/255.0f;
+#ifdef RW_D3D9
+ rw::d3d::d3ddevice->SetPixelShaderConstantF(10, blurcolors, 1);
+ rw::d3d::im2dOverridePS = colourfilterVC_PS;
+#endif
+#ifdef RW_OPENGL
+ rw::gl3::im2dOverrideShader = colourFilterVC;
+ colourFilterVC->use();
+ glUniform4fv(colourFilterVC->uniformLocations[u_blurcolor], 1, blurcolors);
+#endif
+ }
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
+#ifdef RW_D3D9
+ rw::d3d::im2dOverridePS = nil;
+#endif
+#ifdef RW_OPENGL
+ rw::gl3::im2dOverrideShader = nil;
+#endif
+}
+
+void
+CPostFX::RenderMotionBlur(RwCamera *cam, uint32 blur)
+{
+ if(blur == 0)
+ return;
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+
+ RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, blur);
+ RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, blur);
+ RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, blur);
+ RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, blur);
+
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
+}
+
+bool
+CPostFX::NeedBackBuffer(void)
+{
+ // Current frame -- needed for non-blur effect
+ switch(EffectSwitch){
+ case POSTFX_OFF:
+ case POSTFX_SIMPLE:
+ // no actual rendering here
+ return false;
+ case POSTFX_NORMAL:
+ if(MotionBlurOn)
+ return false;
+ else
+ return true;
+ case POSTFX_MOBILE:
+ return true;
+ }
+ return false;
+}
+
+bool
+CPostFX::NeedFrontBuffer(int32 type)
+{
+ // Last frame -- needed for motion blur
+ if(CMBlur::Drunkness > 0.0f)
+ return true;
+ if(type == MOTION_BLUR_SNIPER)
+ return true;
+
+ switch(EffectSwitch){
+ case POSTFX_OFF:
+ case POSTFX_SIMPLE:
+ // no actual rendering here
+ return false;
+ case POSTFX_NORMAL:
+ if(MotionBlurOn)
+ return true;
+ else
+ return false;
+ case POSTFX_MOBILE:
+ return false;
+ }
+ return false;
+}
+
+void
+CPostFX::GetBackBuffer(RwCamera *cam)
+{
+ RwRasterPushContext(pBackBuffer);
+ RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
+ RwRasterPopContext();
+}
+
+void
+CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha)
+{
+ PUSH_RENDERGROUP("CPostFX::Render");
+
+ if(pFrontBuffer == nil)
+ Open(cam);
+ assert(pFrontBuffer);
+ assert(pBackBuffer);
+
+ if(type == MOTION_BLUR_LIGHT_SCENE){
+ SmoothColor(red, green, blue, blur);
+ red = AvgRed;
+ green = AvgGreen;
+ blue = AvgBlue;
+ blur = AvgAlpha;
+ }
+
+ if(NeedBackBuffer())
+ GetBackBuffer(cam);
+
+ DefinedState();
+
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+
+ if(type == MOTION_BLUR_SNIPER){
+ if(!bJustInitialised)
+ RenderOverlaySniper(cam, red, green, blue, blur);
+ }else switch(EffectSwitch){
+ case POSTFX_OFF:
+ case POSTFX_SIMPLE:
+ // no actual rendering here
+ break;
+ case POSTFX_NORMAL:
+ if(MotionBlurOn){
+ if(!bJustInitialised)
+ RenderOverlayBlur(cam, red, green, blue, blur);
+ }else{
+ RenderOverlayShader(cam, red, green, blue, blur);
+ }
+ break;
+ case POSTFX_MOBILE:
+ RenderOverlayShader(cam, red, green, blue, blur);
+ break;
+ }
+
+ if(!bJustInitialised)
+ RenderMotionBlur(cam, 175.0f * CMBlur::Drunkness);
+
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+
+ if(NeedFrontBuffer(type)){
+ RwRasterPushContext(pFrontBuffer);
+ RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
+ RwRasterPopContext();
+ bJustInitialised = false;
+ }else
+ bJustInitialised = true;
+
+ POP_RENDERGROUP();
+}
+
+int CPostFX::PrevRed[NUMAVERAGE], CPostFX::AvgRed;
+int CPostFX::PrevGreen[NUMAVERAGE], CPostFX::AvgGreen;
+int CPostFX::PrevBlue[NUMAVERAGE], CPostFX::AvgBlue;
+int CPostFX::PrevAlpha[NUMAVERAGE], CPostFX::AvgAlpha;
+int CPostFX::Next;
+int CPostFX::NumValues;
+
+// This is rather annoying...the blur color can flicker slightly
+// which becomes very visible when amplified by the shader
+void
+CPostFX::SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha)
+{
+ PrevRed[Next] = red;
+ PrevGreen[Next] = green;
+ PrevBlue[Next] = blue;
+ PrevAlpha[Next] = alpha;
+ Next = (Next+1) % NUMAVERAGE;
+ NumValues = Min(NumValues+1, NUMAVERAGE);
+
+ AvgRed = 0;
+ AvgGreen = 0;
+ AvgBlue = 0;
+ AvgAlpha = 0;
+ for(int i = 0; i < NumValues; i++){
+ AvgRed += PrevRed[i];
+ AvgGreen += PrevGreen[i];
+ AvgBlue += PrevBlue[i];
+ AvgAlpha += PrevAlpha[i];
+ }
+ AvgRed /= NumValues;
+ AvgGreen /= NumValues;
+ AvgBlue /= NumValues;
+ AvgAlpha /= NumValues;
+}
+
+#endif
diff --git a/src/extras/postfx.h b/src/extras/postfx.h
new file mode 100644
index 0000000..db702bf
--- /dev/null
+++ b/src/extras/postfx.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#ifdef EXTENDED_COLOURFILTER
+
+class CPostFX
+{
+public:
+ enum {
+ POSTFX_OFF,
+ POSTFX_SIMPLE,
+ POSTFX_NORMAL,
+ POSTFX_MOBILE
+ };
+ static RwRaster *pFrontBuffer;
+ static RwRaster *pBackBuffer;
+ static bool bJustInitialised;
+ static int EffectSwitch;
+ static bool BlurOn; // or use CMblur for that?
+ static bool MotionBlurOn; // or use CMblur for that?
+ static float Intensity;
+
+ // smooth blur color
+ enum { NUMAVERAGE = 20 };
+ static int PrevRed[NUMAVERAGE], AvgRed;
+ static int PrevGreen[NUMAVERAGE], AvgGreen;
+ static int PrevBlue[NUMAVERAGE], AvgBlue;
+ static int PrevAlpha[NUMAVERAGE], AvgAlpha;
+ static int Next;
+ static int NumValues;
+
+ static void InitOnce(void);
+ static void Open(RwCamera *cam);
+ static void Close(void);
+ static void RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
+ static void RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
+ static void RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a);
+ static void RenderMotionBlur(RwCamera *cam, uint32 blur);
+ static void Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha);
+ static void SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha);
+ static bool NeedBackBuffer(void);
+ static bool NeedFrontBuffer(int32 type);
+ static void GetBackBuffer(RwCamera *cam);
+ static bool UseBlurColours(void) { return EffectSwitch != POSTFX_SIMPLE; }
+};
+
+#endif
diff --git a/src/extras/re3_inttypes.h b/src/extras/re3_inttypes.h
new file mode 100644
index 0000000..bf0c53e
--- /dev/null
+++ b/src/extras/re3_inttypes.h
@@ -0,0 +1,216 @@
+#define PRId8 "hhd"
+#define PRId16 "hd"
+#define PRId32 "ld"
+#define PRId64 "lld"
+
+#define PRIdFAST8 "hhd"
+#define PRIdFAST16 "hd"
+#define PRIdFAST32 "ld"
+#define PRIdFAST64 "lld"
+
+#define PRIdLEAST8 "hhd"
+#define PRIdLEAST16 "hd"
+#define PRIdLEAST32 "ld"
+#define PRIdLEAST64 "lld"
+
+#define PRIdMAX "lld"
+#define PRIdPTR "lld"
+
+#define PRIi8 "hhi"
+#define PRIi16 "hi"
+#define PRIi32 "li"
+#define PRIi64 "lli"
+
+#define PRIiFAST8 "hhi"
+#define PRIiFAST16 "hi"
+#define PRIiFAST32 "li"
+#define PRIiFAST64 "lli"
+
+#define PRIiLEAST8 "hhi"
+#define PRIiLEAST16 "hi"
+#define PRIiLEAST32 "li"
+#define PRIiLEAST64 "lli"
+
+#define PRIiMAX "lli"
+#define PRIiPTR "lli"
+
+#define PRIo8 "hho"
+#define PRIo16 "ho"
+#define PRIo32 "lo"
+#define PRIo64 "llo"
+
+#define PRIoFAST8 "hho"
+#define PRIoFAST16 "ho"
+#define PRIoFAST32 "lo"
+#define PRIoFAST64 "llo"
+
+#define PRIoLEAST8 "hho"
+#define PRIoLEAST16 "ho"
+#define PRIoLEAST32 "lo"
+#define PRIoLEAST64 "llo"
+
+#define PRIoMAX "llo"
+#define PRIoPTR "llo"
+
+#define PRIu8 "hhu"
+#define PRIu16 "hu"
+#define PRIu32 "lu"
+#define PRIu64 "llu"
+
+#define PRIuFAST8 "hhu"
+#define PRIuFAST16 "hu"
+#define PRIuFAST32 "lu"
+#define PRIuFAST64 "llu"
+
+#define PRIuLEAST8 "hhu"
+#define PRIuLEAST16 "hu"
+#define PRIuLEAST32 "lu"
+#define PRIuLEAST64 "llu"
+
+#define PRIuMAX "llu"
+#define PRIuPTR "llu"
+
+#define PRIx8 "hhx"
+#define PRIx16 "hx"
+#define PRIx32 "lx"
+#define PRIx64 "llx"
+
+#define PRIxFAST8 "hhx"
+#define PRIxFAST16 "hx"
+#define PRIxFAST32 "lx"
+#define PRIxFAST64 "llx"
+
+#define PRIxLEAST8 "hhx"
+#define PRIxLEAST16 "hx"
+#define PRIxLEAST32 "lx"
+#define PRIxLEAST64 "llx"
+
+#define PRIxMAX "llx"
+#define PRIxPTR "llx"
+
+#define PRIX8 "hhX"
+#define PRIX16 "hX"
+#define PRIX32 "lX"
+#define PRIX64 "llX"
+
+#define PRIXFAST8 "hhX"
+#define PRIXFAST16 "hX"
+#define PRIXFAST32 "lX"
+#define PRIXFAST64 "llX"
+
+#define PRIXLEAST8 "hhX"
+#define PRIXLEAST16 "hX"
+#define PRIXLEAST32 "lX"
+#define PRIXLEAST64 "llX"
+
+#define PRIXMAX "llX"
+#define PRIXPTR "llX"
+
+ /* SCAN FORMAT MACROS */
+#define SCNd8 "hhd"
+#define SCNd16 "hd"
+#define SCNd32 "ld"
+#define SCNd64 "lld"
+
+#define SCNdFAST8 "hhd"
+#define SCNdFAST16 "hd"
+#define SCNdFAST32 "ld"
+#define SCNdFAST64 "lld"
+
+#define SCNdLEAST8 "hhd"
+#define SCNdLEAST16 "hd"
+#define SCNdLEAST32 "ld"
+#define SCNdLEAST64 "lld"
+
+#define SCNdMAX "lld"
+#define SCNdPTR "lld"
+
+#define SCNi8 "hhi"
+#define SCNi16 "hi"
+#define SCNi32 "li"
+#define SCNi64 "lli"
+
+#define SCNiFAST8 "hhi"
+#define SCNiFAST16 "hi"
+#define SCNiFAST32 "li"
+#define SCNiFAST64 "lli"
+
+#define SCNiLEAST8 "hhi"
+#define SCNiLEAST16 "hi"
+#define SCNiLEAST32 "li"
+#define SCNiLEAST64 "lli"
+
+#define SCNiMAX "lli"
+#define SCNiPTR "lli"
+
+#define SCNo8 "hho"
+#define SCNo16 "ho"
+#define SCNo32 "lo"
+#define SCNo64 "llo"
+
+#define SCNoFAST8 "hho"
+#define SCNoFAST16 "ho"
+#define SCNoFAST32 "lo"
+#define SCNoFAST64 "llo"
+
+#define SCNoLEAST8 "hho"
+#define SCNoLEAST16 "ho"
+#define SCNoLEAST32 "lo"
+#define SCNoLEAST64 "llo"
+
+#define SCNoMAX "llo"
+#define SCNoPTR "llo"
+
+#define SCNu8 "hhu"
+#define SCNu16 "hu"
+#define SCNu32 "lu"
+#define SCNu64 "llu"
+
+#define SCNuFAST8 "hhu"
+#define SCNuFAST16 "hu"
+#define SCNuFAST32 "lu"
+#define SCNuFAST64 "llu"
+
+#define SCNuLEAST8 "hhu"
+#define SCNuLEAST16 "hu"
+#define SCNuLEAST32 "lu"
+#define SCNuLEAST64 "llu"
+
+#define SCNuMAX "llu"
+#define SCNuPTR "llu"
+
+#define SCNx8 "hhx"
+#define SCNx16 "hx"
+#define SCNx32 "lx"
+#define SCNx64 "llx"
+
+#define SCNxFAST8 "hhx"
+#define SCNxFAST16 "hx"
+#define SCNxFAST32 "lx"
+#define SCNxFAST64 "llx"
+
+#define SCNxLEAST8 "hhx"
+#define SCNxLEAST16 "hx"
+#define SCNxLEAST32 "lx"
+#define SCNxLEAST64 "llx"
+
+#define SCNxMAX "llx"
+#define SCNxPTR "llx"
+
+#define SCNX8 "hhX"
+#define SCNX16 "hX"
+#define SCNX32 "lX"
+#define SCNX64 "llX"
+
+#define SCNXFAST8 "hhX"
+#define SCNXFAST16 "hX"
+#define SCNXFAST32 "lX"
+#define SCNXFAST64 "llX"
+
+#define SCNXLEAST8 "hhX"
+#define SCNXLEAST16 "hX"
+#define SCNXLEAST32 "lX"
+#define SCNXLEAST64 "llX"
+
+#define SCNXMAX "llX"
+#define SCNXPTR "llX" \ No newline at end of file
diff --git a/src/extras/screendroplets.cpp b/src/extras/screendroplets.cpp
new file mode 100644
index 0000000..1412141
--- /dev/null
+++ b/src/extras/screendroplets.cpp
@@ -0,0 +1,817 @@
+#define WITHD3D
+#include "common.h"
+
+#ifdef SCREEN_DROPLETS
+
+#ifndef LIBRW
+#error "Need librw for SCREEN_DROPLETS"
+#endif
+
+#include "General.h"
+#include "main.h"
+#include "RwHelper.h"
+#include "Timer.h"
+#include "Camera.h"
+#include "World.h"
+#include "ZoneCull.h"
+#include "Weather.h"
+#include "ParticleObject.h"
+ #include "Pad.h"
+#include "RenderBuffer.h"
+#include "custompipes.h"
+#include "postfx.h"
+#include "screendroplets.h"
+
+// for 640
+#define MAXSIZE 15
+#define MINSIZE 4
+
+int ScreenDroplets::ms_initialised;
+RwTexture *ScreenDroplets::ms_maskTex;
+RwTexture *ScreenDroplets::ms_screenTex;
+
+bool ScreenDroplets::ms_enabled = true;
+bool ScreenDroplets::ms_movingEnabled = true;
+
+ScreenDroplets::ScreenDrop ScreenDroplets::ms_drops[ScreenDroplets::MAXDROPS];
+int ScreenDroplets::ms_numDrops;
+ScreenDroplets::ScreenDropMoving ScreenDroplets::ms_dropsMoving[ScreenDroplets::MAXDROPSMOVING];
+int ScreenDroplets::ms_numDropsMoving;
+
+CVector ScreenDroplets::ms_prevCamUp;
+CVector ScreenDroplets::ms_prevCamPos;
+CVector ScreenDroplets::ms_camMoveDelta;
+float ScreenDroplets::ms_camMoveDist;
+CVector ScreenDroplets::ms_screenMoveDelta;
+float ScreenDroplets::ms_screenMoveDist;
+float ScreenDroplets::ms_camUpAngle;
+
+int ScreenDroplets::ms_splashDuration;
+CParticleObject *ScreenDroplets::ms_splashObject;
+
+struct Im2DVertexUV2 : rw::RWDEVICE::Im2DVertex
+{
+ rw::float32 u2, v2;
+};
+
+#ifdef RW_D3D9
+static void *screenDroplet_PS;
+#endif
+#ifdef RW_GL3
+static rw::gl3::Shader *screenDroplet;
+#endif
+
+// platform specific
+static void openim2d_uv2(void);
+static void closeim2d_uv2(void);
+static void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices);
+
+static Im2DVertexUV2 VertexBuffer[TEMPBUFFERVERTSIZE];
+
+void
+ScreenDroplets::Initialise(void)
+{
+ Clear();
+ ms_splashDuration = -1;
+ ms_splashObject = nil;
+}
+
+// Create white circle mask for rain drops
+static RwTexture*
+CreateDropMask(int32 size)
+{
+ RwImage *img = RwImageCreate(size, size, 32);
+ RwImageAllocatePixels(img);
+
+ uint8 *pixels = RwImageGetPixels(img);
+ int32 stride = RwImageGetStride(img);
+
+ for(int y = 0; y < size; y++){
+ float yf = ((y + 0.5f)/size - 0.5f)*2.0f;
+ for(int x = 0; x < size; x++){
+ float xf = ((x + 0.5f)/size - 0.5f)*2.0f;
+ memset(&pixels[y*stride + x*4], xf*xf + yf*yf < 1.0f ? 0xFF : 0x00, 4);
+ }
+ }
+
+ int32 width, height, depth, format;
+ RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &width, &height, &depth, &format);
+ RwRaster *ras = RwRasterCreate(width, height, depth, format);
+ RwRasterSetFromImage(ras, img);
+ RwImageDestroy(img);
+ return RwTextureCreate(ras);
+}
+
+void
+ScreenDroplets::InitDraw(void)
+{
+ ms_maskTex = CreateDropMask(64);
+
+ ms_screenTex = RwTextureCreate(nil);
+ RwTextureSetFilterMode(ms_screenTex, rwFILTERLINEAR);
+
+ openim2d_uv2();
+#ifdef RW_D3D9
+#include "shaders/obj/screenDroplet_PS.inc"
+ screenDroplet_PS = rw::d3d::createPixelShader(screenDroplet_PS_cso);
+#endif
+#ifdef RW_GL3
+ using namespace rw::gl3;
+ {
+#include "shaders/obj/im2d_UV2_vert.inc"
+#include "shaders/obj/screenDroplet_frag.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, im2d_UV2_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, screenDroplet_frag_src, nil };
+ screenDroplet = Shader::create(vs, fs);
+ assert(screenDroplet);
+ }
+#endif
+
+ ms_initialised = 1;
+}
+
+void
+ScreenDroplets::Shutdown(void)
+{
+ if(ms_maskTex){
+ RwTextureDestroy(ms_maskTex);
+ ms_maskTex = nil;
+ }
+ if(ms_screenTex){
+ RwTextureSetRaster(ms_screenTex, nil);
+ RwTextureDestroy(ms_screenTex);
+ ms_screenTex = nil;
+ }
+#ifdef RW_D3D9
+ if(screenDroplet_PS){
+ rw::d3d::destroyPixelShader(screenDroplet_PS);
+ screenDroplet_PS = nil;
+ }
+#endif
+#ifdef RW_GL3
+ if(screenDroplet){
+ screenDroplet->destroy();
+ screenDroplet = nil;
+ }
+#endif
+
+ closeim2d_uv2();
+}
+
+void
+ScreenDroplets::Process(void)
+{
+ ProcessCameraMovement();
+ SprayDrops();
+ ProcessMoving();
+ Fade();
+}
+
+static void
+FlushBuffer(void)
+{
+ if(TempBufferIndicesStored){
+ RenderIndexedPrimitive_UV2(rwPRIMTYPETRILIST,
+ VertexBuffer, TempBufferVerticesStored,
+ TempBufferRenderIndexList, TempBufferIndicesStored);
+ TempBufferVerticesStored = 0;
+ TempBufferIndicesStored = 0;
+ }
+}
+
+static int
+StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, Im2DVertexUV2 **vertexStart)
+{
+ if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE ||
+ TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE)
+ FlushBuffer();
+ *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored];
+ *vertexStart = &VertexBuffer[TempBufferVerticesStored];
+ int vertOffset = TempBufferVerticesStored;
+ TempBufferIndicesStored += numIndices;
+ TempBufferVerticesStored += numVertices;
+ return vertOffset;
+}
+
+void
+ScreenDroplets::Render(void)
+{
+ ScreenDrop *drop;
+
+ DefinedState();
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(ms_maskTex));
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+
+ RwTextureSetRaster(ms_screenTex, CPostFX::pBackBuffer);
+#ifdef RW_D3D9
+ rw::d3d::im2dOverridePS = screenDroplet_PS;
+ rw::d3d::setTexture(1, ms_screenTex);
+#endif
+#ifdef RW_GL3
+ rw::gl3::im2dOverrideShader = screenDroplet;
+ rw::gl3::setTexture(1, ms_screenTex);
+#endif
+
+ RenderBuffer::ClearRenderBuffer();
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
+ if(drop->active)
+ AddToRenderList(drop);
+ FlushBuffer();
+
+#ifdef RW_D3D9
+ rw::d3d::im2dOverridePS = nil;
+ rw::d3d::setTexture(1, nil);
+#endif
+#ifdef RW_GL3
+ rw::gl3::im2dOverrideShader = nil;
+ rw::gl3::setTexture(1, nil);
+#endif
+
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, FALSE);
+}
+
+void
+ScreenDroplets::AddToRenderList(ScreenDroplets::ScreenDrop *drop)
+{
+ static float xy[] = {
+ -1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, -1.0f
+ };
+ static float uv[] = {
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f
+ };
+
+ int i;
+ RwImVertexIndex *indices;
+ Im2DVertexUV2 *verts;
+ int first = StartStoring(6, 4, &indices, &verts);
+
+ float scale = 0.5f*SCREEN_SCALE_X(drop->size);
+
+ float screenz = RwIm2DGetNearScreenZ();
+ float z = RwCameraGetNearClipPlane(Scene.camera);
+ float recipz = 1.0f/z;
+
+ float magSize = SCREEN_SCALE_Y(drop->magnification*(300.0f-40.0f) + 40.0f);
+ float ul = drop->x - magSize;
+ float vt = drop->y - magSize;
+ float ur = drop->x + magSize;
+ float vb = drop->y + magSize;
+ ul = Max(ul, 0.0f)/RwRasterGetWidth(CPostFX::pBackBuffer);
+ vt = Max(vt, 0.0f)/RwRasterGetHeight(CPostFX::pBackBuffer);
+ ur = Min(ur, SCREEN_WIDTH)/RwRasterGetWidth(CPostFX::pBackBuffer);
+ vb = Min(vb, SCREEN_HEIGHT)/RwRasterGetHeight(CPostFX::pBackBuffer);
+
+ for(i = 0; i < 4; i++){
+ RwIm2DVertexSetScreenX(&verts[i], drop->x + xy[i*2]*scale);
+ RwIm2DVertexSetScreenY(&verts[i], drop->y + xy[i*2+1]*scale);
+ RwIm2DVertexSetScreenZ(&verts[i], screenz);
+ RwIm2DVertexSetCameraZ(&verts[i], z);
+ RwIm2DVertexSetRecipCameraZ(&verts[i], recipz);
+ RwIm2DVertexSetIntRGBA(&verts[i], drop->color.r, drop->color.g, drop->color.b, drop->color.a);
+ RwIm2DVertexSetU(&verts[i], uv[i*2], recipz);
+ RwIm2DVertexSetV(&verts[i], uv[i*2+1], recipz);
+
+ verts[i].u2 = i < 2 ? ul : ur;
+ verts[i].v2 = i % 3 ? vt : vb;
+ }
+ indices[0] = first + 0;
+ indices[1] = first + 1;
+ indices[2] = first + 2;
+ indices[3] = first + 2;
+ indices[4] = first + 3;
+ indices[5] = first + 0;
+}
+
+void
+ScreenDroplets::Clear(void)
+{
+ ScreenDrop *drop;
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
+ drop->active = false;
+ ms_numDrops = 0;
+}
+
+ScreenDroplets::ScreenDrop*
+ScreenDroplets::NewDrop(float x, float y, float size, float lifetime, bool fades, int r, int g, int b)
+{
+ ScreenDrop *drop;
+ int i;
+
+ for(i = 0, drop = ms_drops; i < MAXDROPS; i++, drop++)
+ if(!ms_drops[i].active)
+ goto found;
+ return nil;
+found:
+ ms_numDrops++;
+ drop->x = x;
+ drop->y = y;
+ drop->size = size;
+ drop->magnification = (MAXSIZE - size + 1.0f) / (MAXSIZE - MINSIZE + 1.0f);
+ drop->fades = fades;
+ drop->active = true;
+ drop->color.r = r;
+ drop->color.g = g;
+ drop->color.b = b;
+ drop->color.a = 255;
+ drop->time = 0.0f;
+ drop->lifetime = lifetime;
+ return drop;
+}
+
+void
+ScreenDroplets::SetMoving(ScreenDroplets::ScreenDrop *drop)
+{
+ ScreenDropMoving *moving;
+ for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++)
+ if(moving->drop == nil)
+ goto found;
+ return;
+found:
+ ms_numDropsMoving++;
+ moving->drop = drop;
+ moving->dist = 0.0f;
+}
+
+void
+ScreenDroplets::FillScreen(int n)
+{
+ float x, y, size;
+ ScreenDrop *drop;
+
+ if(!ms_initialised)
+ return;
+ ms_numDrops = 0;
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++){
+ drop->active = false;
+ if(drop < &ms_drops[n]){
+ x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
+ y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
+ size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
+ NewDrop(x, y, size, 2000.0f, true);
+ }
+ }
+}
+
+void
+ScreenDroplets::FillScreenMoving(float amount, bool isBlood)
+{
+ int n = (ms_screenMoveDelta.z > 5.0f ? 1.5f : 1.0f)*amount*20.0f;
+ float x, y, size;
+ ScreenDrop *drop;
+
+ while(n--)
+ if(ms_numDrops < MAXDROPS && ms_numDropsMoving < MAXDROPSMOVING){
+ x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
+ y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
+ size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
+ drop = nil;
+ if(isBlood)
+ drop = NewDrop(x, y, size, 2000.0f, true, 255, 0, 0);
+ else
+ drop = NewDrop(x, y, size, 2000.0f, true);
+ if(drop)
+ SetMoving(drop);
+ }
+}
+
+void
+ScreenDroplets::RegisterSplash(CParticleObject *pobj)
+{
+ CVector dist = pobj->GetPosition() - ms_prevCamPos;
+ if(dist.MagnitudeSqr() < 50.0f){ // 20 originally
+ ms_splashDuration = 14;
+ ms_splashObject = pobj;
+ }
+}
+
+void
+ScreenDroplets::ProcessCameraMovement(void)
+{
+ RwMatrix *camMat = RwFrameGetMatrix(RwCameraGetFrame(Scene.camera));
+ CVector camPos = camMat->pos;
+ CVector camUp = camMat->at;
+ ms_camMoveDelta = camPos - ms_prevCamPos;
+ ms_camMoveDist = ms_camMoveDelta.Magnitude();
+
+ ms_prevCamUp = camUp;
+ ms_prevCamPos = camPos;
+
+ ms_screenMoveDelta.x = -RwV3dDotProduct(&camMat->right, &ms_camMoveDelta);
+ ms_screenMoveDelta.y = RwV3dDotProduct(&camMat->up, &ms_camMoveDelta);
+ ms_screenMoveDelta.z = RwV3dDotProduct(&camMat->at, &ms_camMoveDelta);
+ ms_screenMoveDelta *= 10.0f;
+ ms_screenMoveDist = ms_screenMoveDelta.Magnitude2D();
+
+ uint16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode;
+ bool isTopDown = mode == CCam::MODE_TOPDOWN || mode == CCam::MODE_GTACLASSIC || mode == CCam::MODE_TOP_DOWN_PED;
+ bool isLookingInDirection = FindPlayerVehicle() && mode == CCam::MODE_1STPERSON &&
+ (CPad::GetPad(0)->GetLookBehindForCar() || CPad::GetPad(0)->GetLookLeft() || CPad::GetPad(0)->GetLookRight());
+ ms_enabled = !isTopDown && !isLookingInDirection;
+ ms_movingEnabled = !isTopDown && !isLookingInDirection;
+
+ // 0 when looking stright up, 180 when looking up or down
+ ms_camUpAngle = RADTODEG(Acos(clamp(camUp.z, -1.0f, 1.0f)));
+}
+
+void
+ScreenDroplets::SprayDrops(void)
+{
+ bool noRain = CCullZones::PlayerNoRain() || CCullZones::CamNoRain();
+ if(!noRain && CWeather::Rain > 0.0f && ms_enabled){
+ // 180 when looking stright up, 0 when looking up or down
+ float angle = 180.0f - ms_camUpAngle;
+ angle = Max(angle, 40.0f); // want at least some rain
+ FillScreenMoving((angle - 40.0f) / 150.0f * CWeather::Rain * 0.5f);
+ }
+
+ int i;
+ for(i = 0; i < MAX_AUDIOHYDRANTS; i++){
+ CAudioHydrant *hyd = CAudioHydrant::Get(i);
+ if (hyd->pParticleObject){
+ CVector dist = hyd->pParticleObject->GetPosition() - ms_prevCamPos;
+ if(dist.MagnitudeSqr() > 40.0f ||
+ DotProduct(dist, ms_prevCamUp) < 0.0f) continue;
+
+ FillScreenMoving(1.0f);
+ }
+ }
+
+ static int ndrops[] = {
+ 125, 250, 500, 1000, 1000,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if(ms_splashDuration >= 0){
+ if(ms_numDrops < MAXDROPS) {
+ float numDropMult = 1.0f;
+ if(ms_splashObject){
+ float dist = (ms_splashObject->GetPosition() - ms_prevCamPos).Magnitude();
+ numDropMult = 1.0f - (dist - 5.0f)/15.0f;
+ if(numDropMult < 0) numDropMult = 0.0f; // fix
+ }
+ int n = ndrops[ms_splashDuration] * numDropMult;
+ while(n--)
+ if(ms_numDrops < MAXDROPS){
+ float x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
+ float y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
+ float size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
+ NewDrop(x, y, size, 10000.0f, false);
+ }
+ }
+ ms_splashDuration--;
+ }
+}
+
+void
+ScreenDroplets::NewTrace(ScreenDroplets::ScreenDropMoving *moving)
+{
+ if(ms_numDrops < MAXDROPS){
+ moving->dist = 0.0f;
+ NewDrop(moving->drop->x, moving->drop->y, MINSIZE, 500.0f, true,
+ moving->drop->color.r, moving->drop->color.g, moving->drop->color.b);
+ }
+}
+
+void
+ScreenDroplets::MoveDrop(ScreenDroplets::ScreenDropMoving *moving)
+{
+ ScreenDrop *drop = moving->drop;
+ if(!ms_movingEnabled)
+ return;
+ if(!drop->active){
+ moving->drop = nil;
+ ms_numDropsMoving--;
+ return;
+ }
+ if(ms_screenMoveDelta.z > 0.0f && ms_camMoveDist > 0.3f){
+ if(ms_screenMoveDist > 0.5f && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){
+ // movement when camera turns
+ moving->dist += ms_screenMoveDist;
+ if(moving->dist > 20.0f && drop->color.a > 100)
+ NewTrace(moving);
+
+ drop->x -= ms_screenMoveDelta.x;
+ drop->y += ms_screenMoveDelta.y;
+ }else{
+ // movement out of center
+ float d = ms_screenMoveDelta.z*0.2f;
+ float dx, dy, sum;
+ dx = drop->x - SCREEN_WIDTH*0.5f + ms_screenMoveDelta.x;
+ if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON)
+ dy = drop->y - SCREEN_HEIGHT*1.2f - ms_screenMoveDelta.y;
+ else
+ dy = drop->y - SCREEN_HEIGHT*0.5f - ms_screenMoveDelta.y;
+ sum = fabs(dx) + fabs(dy);
+ if(sum > 0.001f){
+ dx /= sum;
+ dy /= sum;
+ }
+ moving->dist += d;
+ if(moving->dist > 20.0f && drop->color.a > 100)
+ NewTrace(moving);
+ drop->x += dx * d;
+ drop->y += dy * d;
+ }
+
+ if(drop->x < 0.0f || drop->y < 0.0f ||
+ drop->x > SCREEN_WIDTH || drop->y > SCREEN_HEIGHT){
+ moving->drop = nil;
+ ms_numDropsMoving--;
+ }
+ }
+}
+
+void
+ScreenDroplets::ProcessMoving(void)
+{
+ ScreenDropMoving *moving;
+ if(!ms_movingEnabled)
+ return;
+ for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++)
+ if(moving->drop)
+ MoveDrop(moving);
+}
+
+void
+ScreenDroplets::Fade(void)
+{
+ ScreenDrop *drop;
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
+ if(drop->active)
+ drop->Fade();
+}
+
+void
+ScreenDroplets::ScreenDrop::Fade(void)
+{
+ int delta = CTimer::GetTimeStepInMilliseconds();
+ time += delta;
+ if(time < lifetime){
+ color.a = 255 - time/lifetime*255;
+ }else if(fades){
+ ScreenDroplets::ms_numDrops--;
+ active = false;
+ }
+}
+
+
+/*
+ * Im2D with two uv coors
+ */
+
+#ifdef RW_D3D9
+// stolen from RW, not in a public header
+namespace rw {
+namespace d3d {
+void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf); // NB: don't share this pointer
+void removeDynamicVB(IDirect3DVertexBuffer9 **buf);
+void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf); // NB: don't share this pointer
+void removeDynamicIB(IDirect3DIndexBuffer9 **buf);
+}
+}
+// different than im2d
+#define NUMINDICES 1024
+#define NUMVERTICES 1024
+
+static int primTypeMap[] = {
+ D3DPT_POINTLIST, // invalid
+ D3DPT_LINELIST,
+ D3DPT_LINESTRIP,
+ D3DPT_TRIANGLELIST,
+ D3DPT_TRIANGLESTRIP,
+ D3DPT_TRIANGLEFAN,
+ D3DPT_POINTLIST, // actually not supported!
+};
+// end of stolen stuff
+
+
+static IDirect3DVertexDeclaration9 *im2ddecl_uv2;
+static IDirect3DVertexBuffer9 *im2dvertbuf_uv2;
+static IDirect3DIndexBuffer9 *im2dindbuf_uv2;
+
+void
+openim2d_uv2(void)
+{
+ using namespace rw;
+ using namespace d3d;
+ D3DVERTEXELEMENT9 elements[5] = {
+ { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },
+ { 0, offsetof(Im2DVertexUV2, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
+ { 0, offsetof(Im2DVertexUV2, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, offsetof(Im2DVertexUV2, u2), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ D3DDECL_END()
+ };
+ assert(im2ddecl_uv2 == nil);
+ im2ddecl_uv2 = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements);
+ assert(im2ddecl_uv2);
+
+ assert(im2dvertbuf_uv2 == nil);
+ im2dvertbuf_uv2 = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im2DVertexUV2), 0, true);
+ assert(im2dvertbuf_uv2);
+ addDynamicVB(NUMVERTICES*sizeof(Im2DVertexUV2), 0, &im2dvertbuf_uv2);
+
+ assert(im2dindbuf_uv2 == nil);
+ im2dindbuf_uv2 = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(rw::uint16), true);
+ assert(im2dindbuf_uv2);
+ addDynamicIB(NUMINDICES*sizeof(rw::uint16), &im2dindbuf_uv2);
+}
+
+void
+closeim2d_uv2(void)
+{
+ using namespace rw;
+ using namespace d3d;
+
+ d3d9::destroyVertexDeclaration(im2ddecl_uv2);
+ im2ddecl_uv2 = nil;
+
+ removeDynamicVB(&im2dvertbuf_uv2);
+ destroyVertexBuffer(im2dvertbuf_uv2);
+ im2dvertbuf_uv2 = nil;
+
+ removeDynamicIB(&im2dindbuf_uv2);
+ destroyIndexBuffer(im2dindbuf_uv2);
+ im2dindbuf_uv2 = nil;
+}
+
+void
+RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices)
+{
+ using namespace rw;
+ using namespace d3d;
+
+ if(numVertices > NUMVERTICES ||
+ numIndices > NUMINDICES){
+ // TODO: error
+ return;
+ }
+ rw::uint16 *lockedindices = lockIndices(im2dindbuf_uv2, 0, numIndices*sizeof(rw::uint16), D3DLOCK_DISCARD);
+ memcpy(lockedindices, indices, numIndices*sizeof(rw::uint16));
+ unlockIndices(im2dindbuf_uv2);
+
+ rw::uint8 *lockedvertices = lockVertices(im2dvertbuf_uv2, 0, numVertices*sizeof(Im2DVertexUV2), D3DLOCK_DISCARD);
+ memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertexUV2));
+ unlockVertices(im2dvertbuf_uv2);
+
+ setStreamSource(0, im2dvertbuf_uv2, 0, sizeof(Im2DVertexUV2));
+ setIndices(im2dindbuf_uv2);
+ setVertexDeclaration(im2ddecl_uv2);
+
+ if(im2dOverridePS)
+ setPixelShader(im2dOverridePS);
+ else if(engine->device.getRenderState(TEXTURERASTER))
+ setPixelShader(im2d_tex_PS);
+ else
+ setPixelShader(im2d_PS);
+
+ d3d::flushCache();
+
+ rw::uint32 primCount = 0;
+ switch(primType){
+ case PRIMTYPELINELIST:
+ primCount = numIndices/2;
+ break;
+ case PRIMTYPEPOLYLINE:
+ primCount = numIndices-1;
+ break;
+ case PRIMTYPETRILIST:
+ primCount = numIndices/3;
+ break;
+ case PRIMTYPETRISTRIP:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPETRIFAN:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPEPOINTLIST:
+ primCount = numIndices;
+ break;
+ }
+ d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0,
+ 0, numVertices,
+ 0, primCount);
+}
+#endif
+
+#ifdef RW_GL3
+// different than im2d
+#define NUMINDICES 1024
+#define NUMVERTICES 1024
+
+static rw::gl3::AttribDesc im2d_UV2_attribDesc[4] = {
+ { rw::gl3::ATTRIB_POS, GL_FLOAT, GL_FALSE, 4,
+ sizeof(Im2DVertexUV2), 0 },
+ { rw::gl3::ATTRIB_COLOR, GL_UNSIGNED_BYTE, GL_TRUE, 4,
+ sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, r) },
+ { rw::gl3::ATTRIB_TEXCOORDS0, GL_FLOAT, GL_FALSE, 2,
+ sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u) },
+ { rw::gl3::ATTRIB_TEXCOORDS1, GL_FLOAT, GL_FALSE, 2,
+ sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u2) }
+};
+
+static int primTypeMap[] = {
+ GL_POINTS, // invalid
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLE_FAN,
+ GL_POINTS
+};
+
+static int32 u_xform;
+
+uint32 im2D_UV2_Vbo, im2D_UV2_Ibo;
+#ifdef RW_GL_USE_VAOS
+uint32 im2D_UV2_Vao;
+#endif
+
+void
+openim2d_uv2(void)
+{
+ u_xform = rw::gl3::registerUniform("u_xform"); // this doesn't add a new one, so it's safe
+
+ glGenBuffers(1, &im2D_UV2_Ibo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW);
+
+ glGenBuffers(1, &im2D_UV2_Vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo);
+ glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW);
+
+#ifdef RW_GL_USE_VAOS
+ glGenVertexArrays(1, &im2D_UV2_Vao);
+ glBindVertexArray(im2D_UV2_Vao);
+ setAttribPointers(im2d_UV2_attribDesc, 4);
+#endif
+}
+
+void
+closeim2d_uv2(void)
+{
+ glDeleteBuffers(1, &im2D_UV2_Ibo);
+ glDeleteBuffers(1, &im2D_UV2_Vbo);
+#ifdef RW_GL_USE_VAOS
+ glDeleteVertexArrays(1, &im2D_UV2_Vao);
+#endif
+}
+
+void
+RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices)
+{
+ using namespace rw;
+ using namespace gl3;
+
+ GLfloat xform[4];
+ Camera *cam;
+ cam = (Camera*)engine->currentCamera;
+
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(im2D_UV2_Vao);
+#endif
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices);
+
+ glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo);
+ glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertexUV2), vertices);
+
+ xform[0] = 2.0f/cam->frameBuffer->width;
+ xform[1] = -2.0f/cam->frameBuffer->height;
+ xform[2] = -1.0f;
+ xform[3] = 1.0f;
+
+ if(im2dOverrideShader)
+ im2dOverrideShader->use();
+ else
+ assert(0);//im2dShader->use();
+#ifndef RW_GL_USE_VAOS
+ setAttribPointers(im2d_UV2_attribDesc, 4);
+#endif
+
+ glUniform4fv(currentShader->uniformLocations[u_xform], 1, xform);
+
+ flushCache();
+ glDrawElements(primTypeMap[primType], numIndices,
+ GL_UNSIGNED_SHORT, nil);
+#ifndef RW_GL_USE_VAOS
+ disableAttribPointers(im2d_UV2_attribDesc, 4);
+#endif
+}
+#endif
+
+#endif
diff --git a/src/extras/screendroplets.h b/src/extras/screendroplets.h
new file mode 100644
index 0000000..090b192
--- /dev/null
+++ b/src/extras/screendroplets.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#ifdef SCREEN_DROPLETS
+
+class CParticleObject;
+
+class ScreenDroplets
+{
+public:
+ enum {
+ MAXDROPS = 2000,
+ MAXDROPSMOVING = 700
+ };
+
+ class ScreenDrop
+ {
+ public:
+ float x, y, time; // shorts on xbox (short float?)
+ float size, magnification, lifetime; // "
+ CRGBA color;
+ bool active;
+ bool fades;
+
+ void Fade(void);
+ };
+
+ struct ScreenDropMoving
+ {
+ ScreenDrop *drop;
+ float dist;
+ };
+
+ static int ms_initialised;
+ static RwTexture *ms_maskTex;
+ static RwTexture *ms_screenTex;
+
+ static bool ms_enabled;
+ static bool ms_movingEnabled;
+
+ static ScreenDrop ms_drops[MAXDROPS];
+ static int ms_numDrops;
+ static ScreenDropMoving ms_dropsMoving[MAXDROPSMOVING];
+ static int ms_numDropsMoving;
+
+ static CVector ms_prevCamUp;
+ static CVector ms_prevCamPos;
+ static CVector ms_camMoveDelta;
+ static float ms_camMoveDist;
+ static CVector ms_screenMoveDelta;
+ static float ms_screenMoveDist;
+ static float ms_camUpAngle;
+
+ static int ms_splashDuration;
+ static CParticleObject *ms_splashObject;
+
+ static void Initialise(void);
+ static void InitDraw(void);
+ static void Shutdown(void);
+ static void Process(void);
+ static void Render(void);
+ static void AddToRenderList(ScreenDrop *drop);
+
+ static void Clear(void);
+ static ScreenDrop *NewDrop(float x, float y, float size, float lifetime, bool fades, int r = 255, int g = 255, int b = 255);
+ static void SetMoving(ScreenDroplets::ScreenDrop *drop);
+ static void FillScreen(int n);
+ static void FillScreenMoving(float amount, bool isBlood = false);
+ static void RegisterSplash(CParticleObject *pobj);
+
+ static void ProcessCameraMovement(void);
+ static void SprayDrops(void);
+ static void NewTrace(ScreenDroplets::ScreenDropMoving *moving);
+ static void MoveDrop(ScreenDropMoving *moving);
+ static void ProcessMoving(void);
+ static void Fade(void);
+};
+
+#endif
diff --git a/src/extras/shaders/colourfilterVC.frag b/src/extras/shaders/colourfilterVC.frag
new file mode 100644
index 0000000..283aa81
--- /dev/null
+++ b/src/extras/shaders/colourfilterVC.frag
@@ -0,0 +1,27 @@
+uniform sampler2D tex0;
+uniform vec4 u_blurcolor;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ float a = u_blurcolor.a;
+ vec4 doublec = clamp(u_blurcolor*2.0, 0.0, 1.0);
+ vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ vec4 prev = dst;
+ for(int i = 0; i < 5; i++){
+ vec4 tmp = dst*(1.0-a) + prev*doublec*a;
+ tmp += prev*u_blurcolor;
+ tmp += prev*u_blurcolor;
+ prev = clamp(tmp, 0.0, 1.0);
+ }
+ vec4 color;
+ color.rgb = prev.rgb;
+ color.a = 1.0;
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/colourfilterVC_PS.hlsl b/src/extras/shaders/colourfilterVC_PS.hlsl
new file mode 100644
index 0000000..90d3b50
--- /dev/null
+++ b/src/extras/shaders/colourfilterVC_PS.hlsl
@@ -0,0 +1,23 @@
+sampler2D tex : register(s0);
+float4 blurcol : register(c10);
+
+//float4 blurcols[10] : register(c15);
+
+
+float4 main(in float2 texcoord : TEXCOORD0) : COLOR0
+{
+ float a = blurcol.a;
+
+ float4 doublec = saturate(blurcol*2);
+ float4 dst = tex2D(tex, texcoord.xy);
+ float4 prev = dst;
+ for(int i = 0; i < 5; i++){
+// float4 doublec = saturate(blurcol*2);
+ float4 tmp = dst*(1-a) + prev*doublec*a;
+ tmp += prev*blurcol;
+ tmp += prev*blurcol;
+ prev = saturate(tmp);
+ }
+ prev.a = 1.0;
+ return prev;
+}
diff --git a/src/extras/shaders/contrast.frag b/src/extras/shaders/contrast.frag
new file mode 100644
index 0000000..2d394f6
--- /dev/null
+++ b/src/extras/shaders/contrast.frag
@@ -0,0 +1,19 @@
+uniform sampler2D tex0;
+uniform vec3 u_contrastAdd;
+uniform vec3 u_contrastMult;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ vec4 color;
+ color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;
+ color.a = 1.0;
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/contrastPS.hlsl b/src/extras/shaders/contrastPS.hlsl
new file mode 100644
index 0000000..a1de1d8
--- /dev/null
+++ b/src/extras/shaders/contrastPS.hlsl
@@ -0,0 +1,21 @@
+struct PS_INPUT
+{
+ float4 position : POSITION;
+ float3 texcoord0 : TEXCOORD0;
+ float4 color : COLOR0;
+};
+
+uniform float3 contrastMult : register(c10);
+uniform float3 contrastAdd : register(c11);
+
+sampler2D tex : register(s0);
+
+float4
+main(PS_INPUT In) : COLOR
+{
+ float4 dst = tex2D(tex, In.texcoord0.xy);
+
+ dst.rgb = dst.rgb*contrastMult + contrastAdd;
+ dst.a = 1.0;
+ return dst;
+}
diff --git a/src/extras/shaders/default_UV2.vert b/src/extras/shaders/default_UV2.vert
new file mode 100644
index 0000000..694c012
--- /dev/null
+++ b/src/extras/shaders/default_UV2.vert
@@ -0,0 +1,25 @@
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT vec2 v_tex1;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * in_normal;
+
+ v_tex0 = in_tex0;
+ v_tex1 = in_tex1;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_color *= u_matColor;
+
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/extras/shaders/default_UV2_VS.hlsl b/src/extras/shaders/default_UV2_VS.hlsl
new file mode 100644
index 0000000..e78a990
--- /dev/null
+++ b/src/extras/shaders/default_UV2_VS.hlsl
@@ -0,0 +1,54 @@
+#include "standardConstants.h"
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Prelight : COLOR0;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+};
+
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = mul(combinedMat, input.Position);
+ float3 Vertex = mul(worldMat, input.Position).xyz;
+ float3 Normal = mul(normalMat, input.Normal);
+
+ output.TexCoord0.xy = input.TexCoord;
+ output.TexCoord1.xy = input.TexCoord1;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient;
+
+ int i;
+#ifdef DIRECTIONALS
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
+#endif
+#ifdef POINTLIGHTS
+ for(i = 0; i < numPointLights; i++)
+ output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
+#endif
+#ifdef SPOTLIGHTS
+ for(i = 0; i < numSpotLights; i++)
+ output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
+#endif
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.Color *= matCol;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/extras/shaders/im2d.vert b/src/extras/shaders/im2d.vert
new file mode 100644
index 0000000..fcd81c2
--- /dev/null
+++ b/src/extras/shaders/im2d.vert
@@ -0,0 +1,19 @@
+uniform vec4 u_xform;
+
+VSIN(ATTRIB_POS) vec4 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ gl_Position = in_pos;
+ gl_Position.w = 1.0;
+ gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
+ v_fog = DoFog(gl_Position.z);
+ gl_Position.xyz *= gl_Position.w;
+ v_color = in_color;
+ v_tex0 = in_tex0;
+}
diff --git a/src/extras/shaders/im2d_UV2.vert b/src/extras/shaders/im2d_UV2.vert
new file mode 100644
index 0000000..e5fd4d0
--- /dev/null
+++ b/src/extras/shaders/im2d_UV2.vert
@@ -0,0 +1,21 @@
+uniform vec4 u_xform;
+
+VSIN(ATTRIB_POS) vec4 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT vec2 v_tex1;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ gl_Position = in_pos;
+ gl_Position.w = 1.0;
+ gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
+ v_fog = DoFog(gl_Position.z);
+ gl_Position.xyz *= gl_Position.w;
+ v_color = in_color;
+ v_tex0 = in_tex0;
+ v_tex1 = in_tex1;
+}
diff --git a/src/extras/shaders/lighting.h b/src/extras/shaders/lighting.h
new file mode 100644
index 0000000..4b08196
--- /dev/null
+++ b/src/extras/shaders/lighting.h
@@ -0,0 +1,44 @@
+struct Light
+{
+ float4 color; // and radius
+ float4 position; // and -cos(angle)
+ float4 direction; // and falloff clamp
+};
+
+float3 DoDirLight(Light L, float3 N)
+{
+ float l = max(0.0, dot(N, -L.direction.xyz));
+ return l*L.color.xyz;
+}
+
+float3 DoDirLightSpec(Light L, float3 N, float3 V, float power)
+{
+ return pow(saturate(dot(N, normalize(V + -L.direction.xyz))), power)*L.color.xyz;
+}
+
+float3 DoPointLight(Light L, float3 V, float3 N)
+{
+ // As on PS2
+ float3 dir = V - L.position.xyz;
+ float dist = length(dir);
+ float atten = max(0.0, (1.0 - dist/L.color.w));
+ float l = max(0.0, dot(N, -normalize(dir)));
+ return l*L.color.xyz*atten;
+}
+
+float3 DoSpotLight(Light L, float3 V, float3 N)
+{
+ // As on PS2
+ float3 dir = V - L.position.xyz;
+ float dist = length(dir);
+ float atten = max(0.0, (1.0 - dist/L.color.w));
+ dir /= dist;
+ float l = max(0.0, dot(N, -dir));
+ float pcos = dot(dir, L.direction.xyz); // cos to point
+ float ccos = -L.position.w; // cos of cone
+ float falloff = (pcos-ccos)/(1.0-ccos);
+ if(falloff < 0) // outside of cone
+ l = 0;
+ l *= max(falloff, L.direction.w); // falloff clamp
+ return l*L.color.xyz*atten;
+}
diff --git a/src/extras/shaders/make_glsl.sh b/src/extras/shaders/make_glsl.sh
new file mode 100644
index 0000000..0af9896
--- /dev/null
+++ b/src/extras/shaders/make_glsl.sh
@@ -0,0 +1,9 @@
+#!sh
+for i in *.vert; do
+ echo $i
+ ./makeinc_glsl.sh $i
+done
+for i in *.frag; do
+ echo $i
+ ./makeinc_glsl.sh $i
+done
diff --git a/src/extras/shaders/make_hlsl.cmd b/src/extras/shaders/make_hlsl.cmd
new file mode 100644
index 0000000..dee9528
--- /dev/null
+++ b/src/extras/shaders/make_hlsl.cmd
@@ -0,0 +1,3 @@
+@echo off
+for %%f in (*PS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T ps_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f
+for %%f in (*VS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T vs_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f
diff --git a/src/extras/shaders/makeinc_glsl.sh b/src/extras/shaders/makeinc_glsl.sh
new file mode 100644
index 0000000..2bc6a38
--- /dev/null
+++ b/src/extras/shaders/makeinc_glsl.sh
@@ -0,0 +1,6 @@
+#!sh
+ext=${1##*.}
+name=${1%.*}
+(echo "const char *${name}_${ext}_src =";\
+sed 's/..*/"&\\n"/' $1;\
+echo ';') > obj/${name}_${ext}.inc
diff --git a/src/extras/shaders/makeinc_hlsl.sh b/src/extras/shaders/makeinc_hlsl.sh
new file mode 100644
index 0000000..a5b1286
--- /dev/null
+++ b/src/extras/shaders/makeinc_hlsl.sh
@@ -0,0 +1,6 @@
+#!sh
+cd obj
+for i in *cso; do
+ (echo -n 'static '
+ xxd -i $i | grep -v '_len = ') > ${i%cso}inc
+done
diff --git a/src/extras/shaders/neoGloss.frag b/src/extras/shaders/neoGloss.frag
new file mode 100644
index 0000000..4f097b0
--- /dev/null
+++ b/src/extras/shaders/neoGloss.frag
@@ -0,0 +1,26 @@
+uniform sampler2D tex0;
+
+uniform vec4 u_reflProps;
+
+#define glossMult (u_reflProps.x)
+
+FSIN vec3 v_normal;
+FSIN vec3 v_light;
+FSIN vec2 v_tex0;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 color = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ vec3 n = 2.0*v_normal-1.0; // unpack
+ vec3 v = 2.0*v_light-1.0; //
+
+ float s = dot(n, v);
+ color = s*s*s*s*s*s*s*s*color*v_fog*glossMult;
+
+ DoAlphaTest(color.a);
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/neoGloss.vert b/src/extras/shaders/neoGloss.vert
new file mode 100644
index 0000000..41102f3
--- /dev/null
+++ b/src/extras/shaders/neoGloss.vert
@@ -0,0 +1,25 @@
+uniform vec3 u_eye;
+
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec3 v_normal;
+VSOUT vec3 v_light;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * in_normal;
+
+ v_tex0 = in_tex0;
+
+ vec3 viewVec = normalize(u_eye - Vertex.xyz);
+ vec3 Light = normalize(viewVec - u_lightDirection[0].xyz);
+ v_normal = 0.5*(1.0 + vec3(0.0, 0.0, 1.0)); // compress
+ v_light = 0.5*(1.0 + Light); //
+
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/extras/shaders/neoGloss_PS.hlsl b/src/extras/shaders/neoGloss_PS.hlsl
new file mode 100644
index 0000000..b3c9763
--- /dev/null
+++ b/src/extras/shaders/neoGloss_PS.hlsl
@@ -0,0 +1,20 @@
+sampler2D tex0 : register(s0);
+float glossMult : register(c1);
+
+struct VS_out
+{
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float3 Normal : COLOR0;
+ float3 Light : COLOR1;
+};
+
+float4 main(VS_out input) : COLOR
+{
+ float4 color = tex2D(tex0, input.TexCoord0.xy);
+ float3 n = 2.0*input.Normal-1.0; // unpack
+ float3 v = 2.0*input.Light-1.0; //
+
+ float s = dot(n, v);
+ return s*s*s*s*s*s*s*s*color*input.TexCoord0.z*glossMult;
+}
diff --git a/src/extras/shaders/neoGloss_VS.hlsl b/src/extras/shaders/neoGloss_VS.hlsl
new file mode 100644
index 0000000..d166171
--- /dev/null
+++ b/src/extras/shaders/neoGloss_VS.hlsl
@@ -0,0 +1,35 @@
+#include "standardConstants.h"
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float2 TexCoord : TEXCOORD0;
+};
+
+struct VS_out
+{
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float3 Normal : COLOR0;
+ float3 Light : COLOR1;
+};
+
+float3 eye : register(c41);
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = mul(combinedMat, input.Position);
+ float3 Vertex = mul(worldMat, input.Position).xyz;
+ output.TexCoord0.xy = input.TexCoord;
+
+ float3 viewVec = normalize(eye - Vertex);
+ float3 Light = normalize(viewVec - lights[0].direction.xyz);
+ output.Normal = 0.5*(1.0 + float3(0.0, 0.0, 1.0)); // compress
+ output.Light = 0.5*(1.0 + Light); //
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/extras/shaders/neoRim.vert b/src/extras/shaders/neoRim.vert
new file mode 100644
index 0000000..81ee109
--- /dev/null
+++ b/src/extras/shaders/neoRim.vert
@@ -0,0 +1,34 @@
+uniform vec3 u_viewVec;
+uniform vec4 u_rampStart;
+uniform vec4 u_rampEnd;
+uniform vec3 u_rimData;
+
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * in_normal;
+
+ v_tex0 = in_tex0;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
+
+ // rim light
+ float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);
+ vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);
+ v_color.rgb += rimlight.rgb;
+
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_color *= u_matColor;
+
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/extras/shaders/neoRimSkin.vert b/src/extras/shaders/neoRimSkin.vert
new file mode 100644
index 0000000..1515ad7
--- /dev/null
+++ b/src/extras/shaders/neoRimSkin.vert
@@ -0,0 +1,43 @@
+uniform mat4 u_boneMatrices[64];
+
+uniform vec3 u_viewVec;
+uniform vec4 u_rampStart;
+uniform vec4 u_rampEnd;
+uniform vec3 u_rimData;
+
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec3 SkinVertex = vec3(0.0, 0.0, 0.0);
+ vec3 SkinNormal = vec3(0.0, 0.0, 0.0);
+ for(int i = 0; i < 4; i++){
+ SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i];
+ SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i];
+ }
+
+ vec4 Vertex = u_world * vec4(SkinVertex, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * SkinNormal;
+
+ v_tex0 = in_tex0;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
+
+ // rim light
+ float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);
+ vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);
+ v_color.rgb += rimlight.rgb;
+
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_color *= u_matColor;
+
+ v_fog = DoFog(gl_Position.z);
+}
diff --git a/src/extras/shaders/neoRimSkin_VS.hlsl b/src/extras/shaders/neoRimSkin_VS.hlsl
new file mode 100644
index 0000000..87cc093
--- /dev/null
+++ b/src/extras/shaders/neoRimSkin_VS.hlsl
@@ -0,0 +1,73 @@
+#include "standardConstants.h"
+
+float4x3 boneMatrices[64] : register(c41);
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float4 Prelight : COLOR0;
+ float4 Weights : BLENDWEIGHT;
+ int4 Indices : BLENDINDICES;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float4 Color : COLOR0;
+};
+
+float3 viewVec : register(c233);
+float4 rampStart : register(c234);
+float4 rampEnd : register(c235);
+float3 rimData : register(c236);
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ int j;
+ float3 SkinVertex = float3(0.0, 0.0, 0.0);
+ float3 SkinNormal = float3(0.0, 0.0, 0.0);
+ for(j = 0; j < 4; j++){
+ SkinVertex += mul(input.Position, boneMatrices[input.Indices[j]]).xyz * input.Weights[j];
+ SkinNormal += mul(input.Normal, (float3x3)boneMatrices[input.Indices[j]]).xyz * input.Weights[j];
+ }
+
+ output.Position = mul(combinedMat, float4(SkinVertex, 1.0));
+ float3 Vertex = mul(worldMat, float4(SkinVertex, 1.0)).xyz;
+ float3 Normal = mul(normalMat, SkinNormal);
+
+ output.TexCoord0.xy = input.TexCoord;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient;
+
+ int i;
+//#ifdef DIRECTIONALS
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
+//#endif
+//#ifdef POINTLIGHTS
+// for(i = 0; i < numPointLights; i++)
+// output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
+//#endif
+//#ifdef SPOTLIGHTS
+// for(i = 0; i < numSpotLights; i++)
+// output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
+//#endif
+
+ // rim light
+ float f = rimData.x - rimData.y*dot(Normal, viewVec);
+ float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z);
+ output.Color.xyz += rimlight.xyz;
+
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.Color *= matCol;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/extras/shaders/neoRim_VS.hlsl b/src/extras/shaders/neoRim_VS.hlsl
new file mode 100644
index 0000000..7f95166
--- /dev/null
+++ b/src/extras/shaders/neoRim_VS.hlsl
@@ -0,0 +1,61 @@
+#include "standardConstants.h"
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float4 Prelight : COLOR0;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float4 Color : COLOR0;
+};
+
+float3 viewVec : register(c233);
+float4 rampStart : register(c234);
+float4 rampEnd : register(c235);
+float3 rimData : register(c236);
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = mul(combinedMat, input.Position);
+ float3 Vertex = mul(worldMat, input.Position).xyz;
+ float3 Normal = mul(normalMat, input.Normal);
+
+ output.TexCoord0.xy = input.TexCoord;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient;
+
+ int i;
+//#ifdef DIRECTIONALS
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
+//#endif
+//#ifdef POINTLIGHTS
+// for(i = 0; i < numPointLights; i++)
+// output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
+//#endif
+//#ifdef SPOTLIGHTS
+// for(i = 0; i < numSpotLights; i++)
+// output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
+//#endif
+
+ // rim light
+ float f = rimData.x - rimData.y*dot(Normal, viewVec);
+ float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z);
+ output.Color.xyz += rimlight.xyz;
+
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.Color *= matCol;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/extras/shaders/neoVehicle.frag b/src/extras/shaders/neoVehicle.frag
new file mode 100644
index 0000000..2ac24f7
--- /dev/null
+++ b/src/extras/shaders/neoVehicle.frag
@@ -0,0 +1,29 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+FSIN vec4 v_color;
+FSIN vec4 v_reflcolor;
+FSIN vec2 v_tex0;
+FSIN vec2 v_tex1;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 pass1 = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ vec3 envmap = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)).rgb;
+ pass1.rgb = mix(pass1.rgb, envmap, v_reflcolor.a);
+ pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog);
+// pass1.rgb += v_reflcolor.rgb * v_fog;
+
+ vec3 pass2 = v_reflcolor.rgb * v_fog;
+
+ vec4 color;
+ color.rgb = pass1.rgb*pass1.a + pass2;
+ color.a = pass1.a;
+
+// color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
+ DoAlphaTest(color.a);
+
+ FRAGCOLOR(color);
+}
diff --git a/src/extras/shaders/neoVehicle.vert b/src/extras/shaders/neoVehicle.vert
new file mode 100644
index 0000000..6985a68
--- /dev/null
+++ b/src/extras/shaders/neoVehicle.vert
@@ -0,0 +1,51 @@
+uniform vec3 u_eye;
+uniform vec4 u_reflProps;
+uniform vec4 u_specDir[5];
+uniform vec4 u_specColor[5];
+
+#define fresnel (u_reflProps.x)
+#define lightStrength (u_reflProps.y) // speclight alpha
+#define shininess (u_reflProps.z)
+#define specularity (u_reflProps.w)
+
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec4 v_reflcolor;
+VSOUT vec2 v_tex0;
+VSOUT vec2 v_tex1;
+VSOUT float v_fog;
+
+vec3 DoDirLightSpec(vec3 Ldir, vec3 Lcol, vec3 N, vec3 V, float power)
+{
+ return pow(clamp(dot(N, normalize(V + -Ldir)), 0.0, 1.0), power)*Lcol;
+}
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * in_normal;
+ vec3 viewVec = normalize(u_eye - Vertex.xyz);
+
+ v_tex0 = in_tex0;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse*lightStrength;
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_color *= u_matColor;
+
+ // reflect V along Normal
+ vec3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec;
+ v_tex1 = uv2.xy*0.5 + 0.5;
+ float b = 1.0 - clamp(dot(viewVec, Normal), 0.0, 1.0);
+ v_reflcolor = vec4(0.0, 0.0, 0.0, 1.0);
+ v_reflcolor.a = mix(b*b*b*b*b, 1.0, fresnel)*shininess;
+
+ for(int i = 0; i < 5; i++)
+ v_reflcolor.rgb += DoDirLightSpec(u_specDir[i].xyz, u_specColor[i].rgb, Normal, viewVec, u_specDir[i].w)*specularity*lightStrength;
+
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/extras/shaders/neoVehicle_PS.hlsl b/src/extras/shaders/neoVehicle_PS.hlsl
new file mode 100644
index 0000000..fa030dd
--- /dev/null
+++ b/src/extras/shaders/neoVehicle_PS.hlsl
@@ -0,0 +1,34 @@
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+ float4 ReflColor : COLOR1;
+};
+
+sampler2D tex0 : register(s0);
+sampler2D tex1 : register(s1);
+
+float4 fogColor : register(c0);
+
+float4 main(VS_out input) : COLOR
+{
+ float4 pass1 = input.Color;
+//#ifdef TEX
+ pass1 *= tex2D(tex0, input.TexCoord0.xy);
+//#endif
+ float3 envmap = tex2D(tex1, input.TexCoord1).rgb;
+ pass1.rgb = lerp(pass1.rgb, envmap, input.ReflColor.a);
+// pass1.rgb = envmap;
+// pass1.rgb *= input.ReflColor.a;
+ pass1.rgb = lerp(fogColor.rgb, pass1.rgb, input.TexCoord0.z);
+// pass1.rgb += input.ReflColor.rgb * input.TexCoord0.z;
+
+ float3 pass2 = input.ReflColor.rgb*input.TexCoord0.z;
+
+ float4 color;
+ color.rgb = pass1.rgb*pass1.a + pass2;
+ color.a = pass1.a;
+
+ return color;
+}
diff --git a/src/extras/shaders/neoVehicle_VS.hlsl b/src/extras/shaders/neoVehicle_VS.hlsl
new file mode 100644
index 0000000..fb73009
--- /dev/null
+++ b/src/extras/shaders/neoVehicle_VS.hlsl
@@ -0,0 +1,64 @@
+#include "standardConstants.h"
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float4 Prelight : COLOR0;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+ float4 ReflColor : COLOR1;
+};
+
+float3 eye : register(c41);
+float4 reflProps : register(c42);
+Light specLights[5] : register(c43);
+
+
+#define fresnel (reflProps.x)
+#define lightStrength (reflProps.y) // speclight alpha
+#define shininess (reflProps.z)
+#define specularity (reflProps.w)
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = mul(combinedMat, input.Position);
+ float3 Vertex = mul(worldMat, input.Position).xyz;
+ float3 Normal = mul(normalMat, input.Normal);
+ float3 viewVec = normalize(eye - Vertex);
+
+ output.TexCoord0.xy = input.TexCoord;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient*lightStrength;
+
+ int i;
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse*lightStrength;
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.Color *= matCol;
+
+ // reflect V along Normal
+ float3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec;
+ output.TexCoord1 = uv2.xy*0.5 + 0.5;
+ float b = 1.0 - saturate(dot(viewVec, Normal));
+ output.ReflColor = float4(0.0, 0.0, 0.0, 1.0);
+ output.ReflColor.a = lerp(b*b*b*b*b, 1.0, fresnel)*shininess;
+
+ //Light mainLight = lights[0];
+ for(i = 0; i < 5; i++)
+ output.ReflColor.xyz += DoDirLightSpec(specLights[i], Normal, viewVec, specLights[i].direction.w)*specularity*lightStrength;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/extras/shaders/neoWorldVC.frag b/src/extras/shaders/neoWorldVC.frag
new file mode 100644
index 0000000..08cae74
--- /dev/null
+++ b/src/extras/shaders/neoWorldVC.frag
@@ -0,0 +1,26 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+uniform vec4 u_lightMap;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN vec2 v_tex1;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 t0 = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ vec4 t1 = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));
+
+ vec4 color;
+ color = t0*v_color*(1.0 + u_lightMap*(t1-1.0));
+ color.a = v_color.a*t0.a*u_lightMap.a;
+
+ color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
+ DoAlphaTest(color.a);
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/neoWorldVC_PS.hlsl b/src/extras/shaders/neoWorldVC_PS.hlsl
new file mode 100644
index 0000000..fc4f1de
--- /dev/null
+++ b/src/extras/shaders/neoWorldVC_PS.hlsl
@@ -0,0 +1,25 @@
+sampler2D Diffuse : register(s0);
+sampler2D Light : register(s1);
+float4 fogColor : register(c0);
+float4 lm : register(c1);
+
+struct PS_INPUT
+{
+ float4 Color : COLOR0;
+ float3 Tex0 : TEXCOORD0;
+ float2 Tex1 : TEXCOORD1;
+};
+
+float4
+main(PS_INPUT IN) : COLOR
+{
+ float4 t0 = tex2D(Diffuse, IN.Tex0.xy);
+ float4 t1 = tex2D(Light, IN.Tex1);
+
+ float4 col = t0*IN.Color*(1 + lm*(t1-1));
+ col.a = IN.Color.a*t0.a*lm.a;
+
+ col.rgb = lerp(fogColor.rgb, col.rgb, IN.Tex0.z);
+
+ return col;
+}
diff --git a/src/extras/shaders/obj/contrastPS.cso b/src/extras/shaders/obj/contrastPS.cso
new file mode 100644
index 0000000..a87c48d
--- /dev/null
+++ b/src/extras/shaders/obj/contrastPS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/contrastPS.inc b/src/extras/shaders/obj/contrastPS.inc
new file mode 100644
index 0000000..5386792
--- /dev/null
+++ b/src/extras/shaders/obj/contrastPS.inc
@@ -0,0 +1,31 @@
+static unsigned char contrastPS_cso[] = {
+ 0x00, 0x02, 0xff, 0xff, 0xfe, 0xff, 0x35, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff,
+ 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x98, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0b, 0x00,
+ 0x01, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x2a, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x73, 0x74,
+ 0x41, 0x64, 0x64, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x74,
+ 0x72, 0x61, 0x73, 0x74, 0x4d, 0x75, 0x6c, 0x74, 0x00, 0x74, 0x65, 0x78,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, 0x5f, 0x32,
+ 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68,
+ 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+ 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e, 0x33,
+ 0x31, 0x31, 0x31, 0x00, 0x51, 0x00, 0x00, 0x05, 0x00, 0x00, 0x0f, 0xa0,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x03, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x00, 0x08, 0x0f, 0xa0, 0x42, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0xb0, 0x00, 0x08, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x07, 0x80, 0x0a, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x0b, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x80,
+ 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x08, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/contrast_frag.inc b/src/extras/shaders/obj/contrast_frag.inc
new file mode 100644
index 0000000..a1ad479
--- /dev/null
+++ b/src/extras/shaders/obj/contrast_frag.inc
@@ -0,0 +1,21 @@
+const char *contrast_frag_src =
+"uniform sampler2D tex0;\n"
+"uniform vec3 u_contrastAdd;\n"
+"uniform vec3 u_contrastMult;\n"
+
+"FSIN vec4 v_color;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+" vec4 color;\n"
+" color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;\n"
+" color.a = 1.0;\n"
+
+" FRAGCOLOR(color);\n"
+"}\n"
+
+;
diff --git a/src/extras/shaders/obj/default_UV2_VS.cso b/src/extras/shaders/obj/default_UV2_VS.cso
new file mode 100644
index 0000000..5a48c66
--- /dev/null
+++ b/src/extras/shaders/obj/default_UV2_VS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/default_UV2_VS.inc b/src/extras/shaders/obj/default_UV2_VS.inc
new file mode 100644
index 0000000..de83210
--- /dev/null
+++ b/src/extras/shaders/obj/default_UV2_VS.inc
@@ -0,0 +1,55 @@
+static unsigned char default_UV2_VS_cso[] = {
+ 0x00, 0x02, 0xfe, 0xff, 0xfe, 0xff, 0x45, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff,
+ 0x05, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xd5, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0x3e, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+ 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x3a, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00,
+ 0x01, 0x00, 0x32, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xcb, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x36, 0x00,
+ 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6d, 0x62, 0x69,
+ 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x00, 0xab, 0xab, 0xab,
+ 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64,
+ 0x4d, 0x61, 0x74, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x67, 0x44,
+ 0x61, 0x74, 0x61, 0x00, 0x6d, 0x61, 0x74, 0x43, 0x6f, 0x6c, 0x00, 0x73,
+ 0x75, 0x72, 0x66, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x00, 0x76, 0x73, 0x5f,
+ 0x32, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+ 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53,
+ 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c,
+ 0x65, 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e,
+ 0x33, 0x31, 0x31, 0x31, 0x00, 0xab, 0xab, 0xab, 0x51, 0x00, 0x00, 0x05,
+ 0x04, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x05, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x05, 0x00, 0x01, 0x80, 0x02, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x0a, 0x00, 0x00, 0x80, 0x03, 0x00, 0x0f, 0x90, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x07, 0x80, 0x0f, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x0d, 0x00, 0x00, 0xa0,
+ 0x03, 0x00, 0xe4, 0x90, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x80,
+ 0x03, 0x00, 0xff, 0x90, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x55, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0xd0, 0x00, 0x00, 0xe4, 0x80,
+ 0x0c, 0x00, 0xe4, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0x55, 0x90, 0x01, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0x00, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80,
+ 0x02, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xaa, 0x90, 0x00, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80, 0x03, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x01, 0x80, 0x00, 0x00, 0xff, 0x80, 0x0e, 0x00, 0x55, 0xa1,
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0xe4, 0x80,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x0e, 0x00, 0xaa, 0xa0, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0xff, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x04, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x55, 0xa0,
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe0, 0x01, 0x00, 0xe4, 0x90,
+ 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x03, 0xe0, 0x02, 0x00, 0xe4, 0x90,
+ 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/default_UV2_vert.inc b/src/extras/shaders/obj/default_UV2_vert.inc
new file mode 100644
index 0000000..450f3d9
--- /dev/null
+++ b/src/extras/shaders/obj/default_UV2_vert.inc
@@ -0,0 +1,27 @@
+const char *default_UV2_vert_src =
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT vec2 v_tex1;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * in_normal;\n"
+
+" v_tex0 = in_tex0;\n"
+" v_tex1 = in_tex1;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_color *= u_matColor;\n"
+
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/im2d_UV2_vert.inc b/src/extras/shaders/obj/im2d_UV2_vert.inc
new file mode 100644
index 0000000..3feb2bc
--- /dev/null
+++ b/src/extras/shaders/obj/im2d_UV2_vert.inc
@@ -0,0 +1,23 @@
+const char *im2d_UV2_vert_src =
+"uniform vec4 u_xform;\n"
+
+"VSIN(ATTRIB_POS) vec4 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT vec2 v_tex1;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" gl_Position = in_pos;\n"
+" gl_Position.w = 1.0;\n"
+" gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n"
+" v_fog = DoFog(gl_Position.z);\n"
+" gl_Position.xyz *= gl_Position.w;\n"
+" v_color = in_color;\n"
+" v_tex0 = in_tex0;\n"
+" v_tex1 = in_tex1;\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/im2d_vert.inc b/src/extras/shaders/obj/im2d_vert.inc
new file mode 100644
index 0000000..d11f5d3
--- /dev/null
+++ b/src/extras/shaders/obj/im2d_vert.inc
@@ -0,0 +1,21 @@
+const char *im2d_vert_src =
+"uniform vec4 u_xform;\n"
+
+"VSIN(ATTRIB_POS) vec4 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" gl_Position = in_pos;\n"
+" gl_Position.w = 1.0;\n"
+" gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n"
+" v_fog = DoFog(gl_Position.z);\n"
+" gl_Position.xyz *= gl_Position.w;\n"
+" v_color = in_color;\n"
+" v_tex0 = in_tex0;\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/neoGloss_PS.cso b/src/extras/shaders/obj/neoGloss_PS.cso
new file mode 100644
index 0000000..aa88e45
--- /dev/null
+++ b/src/extras/shaders/obj/neoGloss_PS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/neoGloss_PS.inc b/src/extras/shaders/obj/neoGloss_PS.inc
new file mode 100644
index 0000000..97e5641
--- /dev/null
+++ b/src/extras/shaders/obj/neoGloss_PS.inc
@@ -0,0 +1,39 @@
+static unsigned char neoGloss_PS_cso[] = {
+ 0x00, 0x02, 0xff, 0xff, 0xfe, 0xff, 0x2d, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff,
+ 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x6f, 0x73,
+ 0x73, 0x4d, 0x75, 0x6c, 0x74, 0x00, 0xab, 0xab, 0x00, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x74, 0x65, 0x78, 0x30, 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x73, 0x5f, 0x32, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f,
+ 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53,
+ 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d,
+ 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39,
+ 0x35, 0x32, 0x2e, 0x33, 0x31, 0x31, 0x31, 0x00, 0x51, 0x00, 0x00, 0x05,
+ 0x00, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0xbf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x07, 0xb0, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x07, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x07, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x08, 0x0f, 0xa0, 0x42, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xb0, 0x00, 0x08, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x55, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x00, 0x00, 0x00, 0xa0,
+ 0x00, 0x00, 0x55, 0xa0, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x02, 0x00, 0xe4, 0x80, 0x05, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80,
+ 0x00, 0x00, 0xaa, 0xb0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x08, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/neoGloss_VS.cso b/src/extras/shaders/obj/neoGloss_VS.cso
new file mode 100644
index 0000000..9635b8b
--- /dev/null
+++ b/src/extras/shaders/obj/neoGloss_VS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/neoGloss_VS.inc b/src/extras/shaders/obj/neoGloss_VS.inc
new file mode 100644
index 0000000..1ec0376
--- /dev/null
+++ b/src/extras/shaders/obj/neoGloss_VS.inc
@@ -0,0 +1,66 @@
+static unsigned char neoGloss_VS_cso[] = {
+ 0x00, 0x02, 0xfe, 0xff, 0xfe, 0xff, 0x5b, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff,
+ 0x05, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x2d, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x29, 0x00, 0x01, 0x00, 0xa6, 0x00,
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x3a, 0x00, 0xb8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x11, 0x00,
+ 0x03, 0x00, 0x4e, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x01, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x12, 0x00,
+ 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x61, 0x74, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x65, 0x79, 0x65, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x67, 0x44,
+ 0x61, 0x74, 0x61, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x67, 0x68,
+ 0x74, 0x73, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0xab, 0xab, 0xab,
+ 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+ 0x00, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xab,
+ 0xcf, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00,
+ 0xd8, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x03, 0x00,
+ 0xfc, 0x00, 0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74,
+ 0x00, 0x76, 0x73, 0x5f, 0x32, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c,
+ 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e,
+ 0x39, 0x35, 0x32, 0x2e, 0x33, 0x31, 0x31, 0x31, 0x00, 0xab, 0xab, 0xab,
+ 0x51, 0x00, 0x00, 0x05, 0x08, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x80, 0x3f,
+ 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90,
+ 0x1f, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0f, 0x90,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x55, 0x90,
+ 0x05, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80,
+ 0x04, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80, 0x06, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0xaa, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x07, 0x80, 0x07, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80,
+ 0x00, 0x00, 0xe4, 0x81, 0x29, 0x00, 0xe4, 0xa0, 0x24, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x13, 0x00, 0xe4, 0xa1,
+ 0x24, 0x00, 0x00, 0x02, 0x01, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x80,
+ 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x08, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0xd0,
+ 0x00, 0x00, 0xe4, 0x80, 0x08, 0x00, 0x55, 0xa0, 0x05, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x55, 0x90, 0x01, 0x00, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x0f, 0x80, 0x02, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xaa, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80,
+ 0x03, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0x90, 0x00, 0x00, 0xe4, 0x80,
+ 0x02, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80, 0x00, 0x00, 0xff, 0x80,
+ 0x0e, 0x00, 0x55, 0xa1, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0f, 0xc0,
+ 0x00, 0x00, 0xe4, 0x80, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x0e, 0x00, 0xaa, 0xa0, 0x0b, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0xff, 0xa0,
+ 0x0a, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0xe0, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe0,
+ 0x01, 0x00, 0xe4, 0x90, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x07, 0xd0,
+ 0x08, 0x00, 0xc5, 0xa0, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/neoGloss_frag.inc b/src/extras/shaders/obj/neoGloss_frag.inc
new file mode 100644
index 0000000..67e9724
--- /dev/null
+++ b/src/extras/shaders/obj/neoGloss_frag.inc
@@ -0,0 +1,28 @@
+const char *neoGloss_frag_src =
+"uniform sampler2D tex0;\n"
+
+"uniform vec4 u_reflProps;\n"
+
+"#define glossMult (u_reflProps.x)\n"
+
+"FSIN vec3 v_normal;\n"
+"FSIN vec3 v_light;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 color = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+" vec3 n = 2.0*v_normal-1.0; // unpack\n"
+" vec3 v = 2.0*v_light-1.0; //\n"
+
+" float s = dot(n, v);\n"
+" color = s*s*s*s*s*s*s*s*color*v_fog*glossMult;\n"
+
+" DoAlphaTest(color.a);\n"
+
+" FRAGCOLOR(color);\n"
+"}\n"
+
+;
diff --git a/src/extras/shaders/obj/neoGloss_vert.inc b/src/extras/shaders/obj/neoGloss_vert.inc
new file mode 100644
index 0000000..dffb423
--- /dev/null
+++ b/src/extras/shaders/obj/neoGloss_vert.inc
@@ -0,0 +1,27 @@
+const char *neoGloss_vert_src =
+"uniform vec3 u_eye;\n"
+
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec3 v_normal;\n"
+"VSOUT vec3 v_light;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * in_normal;\n"
+
+" v_tex0 = in_tex0;\n"
+
+" vec3 viewVec = normalize(u_eye - Vertex.xyz);\n"
+" vec3 Light = normalize(viewVec - u_lightDirection[0].xyz);\n"
+" v_normal = 0.5*(1.0 + vec3(0.0, 0.0, 1.0)); // compress\n"
+" v_light = 0.5*(1.0 + Light); //\n"
+
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/neoRimSkin_VS.cso b/src/extras/shaders/obj/neoRimSkin_VS.cso
new file mode 100644
index 0000000..8410504
--- /dev/null
+++ b/src/extras/shaders/obj/neoRimSkin_VS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/neoRimSkin_VS.inc b/src/extras/shaders/obj/neoRimSkin_VS.inc
new file mode 100644
index 0000000..ac18295
--- /dev/null
+++ b/src/extras/shaders/obj/neoRimSkin_VS.inc
@@ -0,0 +1,203 @@
+static unsigned char neoRimSkin_VS_cso[] = {
+ 0x00, 0x02, 0xfe, 0xff, 0xfe, 0xff, 0xb4, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff,
+ 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x92, 0x02, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0x3e, 0x00, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x54, 0x01, 0x00, 0x00, 0x02, 0x00, 0x29, 0x00, 0xc0, 0x00, 0xa6, 0x00,
+ 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x80, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00,
+ 0x01, 0x00, 0x42, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xac, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x3a, 0x00,
+ 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x11, 0x00, 0x18, 0x00, 0x46, 0x00, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00,
+ 0x01, 0x00, 0x32, 0x00, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x02, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x03, 0x00, 0x22, 0x00,
+ 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x02, 0x00, 0xeb, 0x00,
+ 0x01, 0x00, 0xae, 0x03, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5c, 0x02, 0x00, 0x00, 0x02, 0x00, 0xea, 0x00, 0x01, 0x00, 0xaa, 0x03,
+ 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00,
+ 0x02, 0x00, 0xec, 0x00, 0x01, 0x00, 0xb2, 0x03, 0x70, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x00,
+ 0x01, 0x00, 0x36, 0x00, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8a, 0x02, 0x00, 0x00, 0x02, 0x00, 0xe9, 0x00, 0x01, 0x00, 0xa6, 0x03,
+ 0x70, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6d, 0x62, 0x69,
+ 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x00, 0xab, 0xab, 0xab,
+ 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x6e, 0x65, 0x4d, 0x61, 0x74, 0x72,
+ 0x69, 0x63, 0x65, 0x73, 0x00, 0xab, 0xab, 0xab, 0x03, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x61, 0x74, 0x00,
+ 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x69, 0x67,
+ 0x68, 0x74, 0x00, 0xab, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x67, 0x44,
+ 0x61, 0x74, 0x61, 0x00, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x63,
+ 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0xab, 0xab, 0xab, 0x01, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x64, 0x69, 0x72,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xab, 0xbb, 0x01, 0x00, 0x00,
+ 0xc4, 0x01, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00,
+ 0xdd, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x03, 0x00, 0xe8, 0x01, 0x00, 0x00,
+ 0x6d, 0x61, 0x74, 0x43, 0x6f, 0x6c, 0x00, 0x6e, 0x6f, 0x72, 0x6d, 0x61,
+ 0x6c, 0x4d, 0x61, 0x74, 0x00, 0xab, 0xab, 0xab, 0x03, 0x00, 0x03, 0x00,
+ 0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6e, 0x75, 0x6d, 0x44, 0x69, 0x72, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x73,
+ 0x00, 0xab, 0xab, 0xab, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x6d, 0x70,
+ 0x45, 0x6e, 0x64, 0x00, 0x72, 0x61, 0x6d, 0x70, 0x53, 0x74, 0x61, 0x72,
+ 0x74, 0x00, 0x72, 0x69, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x00, 0xab, 0xab,
+ 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x73, 0x75, 0x72, 0x66, 0x50, 0x72, 0x6f, 0x70,
+ 0x73, 0x00, 0x76, 0x69, 0x65, 0x77, 0x56, 0x65, 0x63, 0x00, 0x76, 0x73,
+ 0x5f, 0x32, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f,
+ 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20,
+ 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69,
+ 0x6c, 0x65, 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32,
+ 0x2e, 0x33, 0x31, 0x31, 0x31, 0x00, 0xab, 0xab, 0x51, 0x00, 0x00, 0x05,
+ 0x04, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x05, 0x00, 0x00, 0x80, 0x02, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x0a, 0x00, 0x00, 0x80, 0x03, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x80, 0x04, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x80, 0x05, 0x00, 0x0f, 0x90, 0x05, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0f, 0x80, 0x05, 0x00, 0xe4, 0x90, 0x04, 0x00, 0x00, 0xa0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x00, 0x04, 0x01, 0x00, 0x04, 0x80, 0x01, 0x00, 0xe4, 0x90,
+ 0x2b, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x00, 0x04, 0x01, 0x00, 0x02, 0x80,
+ 0x01, 0x00, 0xe4, 0x90, 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x55, 0x80,
+ 0x08, 0x00, 0x00, 0x04, 0x02, 0x00, 0x04, 0x80, 0x01, 0x00, 0xe4, 0x90,
+ 0x2b, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x55, 0x80, 0x08, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x01, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0x55, 0x80, 0x08, 0x00, 0x00, 0x04, 0x02, 0x00, 0x02, 0x80,
+ 0x01, 0x00, 0xe4, 0x90, 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x05, 0x00, 0x00, 0x03, 0x02, 0x00, 0x07, 0x80, 0x02, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0x55, 0x90, 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x90, 0x02, 0x00, 0xe4, 0x80,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xaa, 0x80,
+ 0x08, 0x00, 0x00, 0x04, 0x02, 0x00, 0x04, 0x80, 0x01, 0x00, 0xe4, 0x90,
+ 0x2b, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xaa, 0x80, 0x08, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x01, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0xaa, 0x80, 0x08, 0x00, 0x00, 0x04, 0x02, 0x00, 0x02, 0x80,
+ 0x01, 0x00, 0xe4, 0x90, 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80, 0x02, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0xaa, 0x90, 0x01, 0x00, 0xe4, 0x80, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xff, 0x80, 0x08, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x04, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x2b, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0xff, 0x80, 0x08, 0x00, 0x00, 0x04, 0x02, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xff, 0x80,
+ 0x08, 0x00, 0x00, 0x04, 0x02, 0x00, 0x02, 0x80, 0x01, 0x00, 0xe4, 0x90,
+ 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x07, 0x80, 0x02, 0x00, 0xe4, 0x80, 0x04, 0x00, 0xff, 0x90,
+ 0x01, 0x00, 0xe4, 0x80, 0x05, 0x00, 0x00, 0x03, 0x02, 0x00, 0x07, 0x80,
+ 0x01, 0x00, 0x55, 0x80, 0x09, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x0b, 0x80, 0x08, 0x00, 0xa4, 0xa0, 0x01, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0xa4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80,
+ 0x0a, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0xaa, 0x80, 0x01, 0x00, 0xf4, 0x80,
+ 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x08, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0xe9, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x08, 0x80,
+ 0x03, 0x00, 0xff, 0x90, 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x01, 0x80,
+ 0x0d, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x00, 0x04, 0x03, 0x00, 0x07, 0x80,
+ 0x0f, 0x00, 0xe4, 0xa0, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0xe4, 0x90,
+ 0x01, 0x00, 0x00, 0x02, 0x04, 0x00, 0x07, 0x80, 0x03, 0x00, 0xe4, 0x80,
+ 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x08, 0x80, 0x04, 0x00, 0x55, 0xa0,
+ 0x26, 0x00, 0x00, 0x01, 0x00, 0x00, 0xe4, 0xf0, 0x02, 0x00, 0x00, 0x03,
+ 0x04, 0x00, 0x08, 0x80, 0x03, 0x00, 0xff, 0x80, 0x10, 0x00, 0x00, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x80, 0x04, 0x00, 0xff, 0x80,
+ 0x04, 0x00, 0x00, 0xa0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x04, 0x00, 0xff, 0x80, 0x08, 0x00, 0x00, 0x04, 0x05, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x13, 0x20, 0xe4, 0xa1, 0x00, 0x00, 0xff, 0xb0,
+ 0x0b, 0x00, 0x00, 0x03, 0x05, 0x00, 0x01, 0x80, 0x05, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x55, 0xa0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x04, 0x00, 0xff, 0x80, 0x05, 0x00, 0x00, 0x04, 0x05, 0x00, 0x07, 0x80,
+ 0x05, 0x00, 0x00, 0x80, 0x11, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x04, 0x00, 0x00, 0x04, 0x04, 0x00, 0x07, 0x80, 0x05, 0x00, 0xe4, 0x80,
+ 0x0d, 0x00, 0xaa, 0xa0, 0x04, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x08, 0x80, 0x03, 0x00, 0xff, 0x80, 0x04, 0x00, 0xaa, 0xa0,
+ 0x27, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x01, 0x80,
+ 0xec, 0x00, 0x55, 0xa0, 0x01, 0x00, 0xff, 0x81, 0xec, 0x00, 0x00, 0xa0,
+ 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x07, 0x80, 0xeb, 0x00, 0xe4, 0xa0,
+ 0x02, 0x00, 0x00, 0x03, 0x01, 0x00, 0x0e, 0x80, 0x03, 0x00, 0x90, 0x81,
+ 0xea, 0x00, 0x90, 0xa0, 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0xf9, 0x80, 0xeb, 0x00, 0xe4, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0xec, 0x00, 0xaa, 0xa0, 0x0b, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x55, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0xaa, 0xa0,
+ 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0xe4, 0x80, 0x0b, 0x00, 0x00, 0x03, 0x01, 0x00, 0x0f, 0x80,
+ 0x02, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x55, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0xaa, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0xd0, 0x01, 0x00, 0xe4, 0x80,
+ 0x0c, 0x00, 0xe4, 0xa0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0x55, 0x80, 0x09, 0x00, 0x00, 0x04, 0x01, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x55, 0x80,
+ 0x09, 0x00, 0x00, 0x04, 0x01, 0x00, 0x02, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x55, 0x80, 0x09, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x04, 0x80, 0x00, 0x00, 0xe4, 0x90, 0x2b, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x55, 0x90, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00, 0x04, 0x02, 0x00, 0x02, 0x80,
+ 0x00, 0x00, 0xe4, 0x90, 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x00, 0x80,
+ 0x09, 0x00, 0x00, 0x04, 0x02, 0x00, 0x04, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x2b, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x07, 0x80, 0x02, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x90,
+ 0x01, 0x00, 0xe4, 0x80, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0xaa, 0x80, 0x09, 0x00, 0x00, 0x04, 0x02, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0xe4, 0x90, 0x29, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xaa, 0x80,
+ 0x09, 0x00, 0x00, 0x04, 0x02, 0x00, 0x02, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x2a, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xaa, 0x80, 0x09, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x04, 0x80, 0x00, 0x00, 0xe4, 0x90, 0x2b, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80,
+ 0x02, 0x00, 0xe4, 0x80, 0x04, 0x00, 0xaa, 0x90, 0x01, 0x00, 0xe4, 0x80,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xff, 0x80,
+ 0x09, 0x00, 0x00, 0x04, 0x01, 0x00, 0x01, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x29, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0xff, 0x80, 0x09, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x02, 0x80, 0x00, 0x00, 0xe4, 0x90, 0x2a, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0xb0, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0xb0,
+ 0x00, 0x00, 0xff, 0x80, 0x09, 0x00, 0x00, 0x04, 0x01, 0x00, 0x04, 0x80,
+ 0x00, 0x00, 0xe4, 0x90, 0x2b, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0xb0,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0xff, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x05, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x55, 0x80, 0x01, 0x00, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x0f, 0x80, 0x02, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xaa, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x03, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xff, 0x80, 0x0e, 0x00, 0x55, 0xa1,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x0e, 0x00, 0xaa, 0xa0, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0xff, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x04, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0xaa, 0xa0,
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe0, 0x02, 0x00, 0xe4, 0x90,
+ 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/neoRimSkin_vert.inc b/src/extras/shaders/obj/neoRimSkin_vert.inc
new file mode 100644
index 0000000..01b739b
--- /dev/null
+++ b/src/extras/shaders/obj/neoRimSkin_vert.inc
@@ -0,0 +1,45 @@
+const char *neoRimSkin_vert_src =
+"uniform mat4 u_boneMatrices[64];\n"
+
+"uniform vec3 u_viewVec;\n"
+"uniform vec4 u_rampStart;\n"
+"uniform vec4 u_rampEnd;\n"
+"uniform vec3 u_rimData;\n"
+
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec3 SkinVertex = vec3(0.0, 0.0, 0.0);\n"
+" vec3 SkinNormal = vec3(0.0, 0.0, 0.0);\n"
+" for(int i = 0; i < 4; i++){\n"
+" SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i];\n"
+" SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i];\n"
+" }\n"
+
+" vec4 Vertex = u_world * vec4(SkinVertex, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * SkinNormal;\n"
+
+" v_tex0 = in_tex0;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
+
+" // rim light\n"
+" float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);\n"
+" vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);\n"
+" v_color.rgb += rimlight.rgb;\n"
+
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_color *= u_matColor;\n"
+
+" v_fog = DoFog(gl_Position.z);\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/neoRim_VS.cso b/src/extras/shaders/obj/neoRim_VS.cso
new file mode 100644
index 0000000..4af538b
--- /dev/null
+++ b/src/extras/shaders/obj/neoRim_VS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/neoRim_VS.inc b/src/extras/shaders/obj/neoRim_VS.inc
new file mode 100644
index 0000000..03b044a
--- /dev/null
+++ b/src/extras/shaders/obj/neoRim_VS.inc
@@ -0,0 +1,118 @@
+static unsigned char neoRim_VS_cso[] = {
+ 0x00, 0x02, 0xfe, 0xff, 0xfe, 0xff, 0xa7, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x65, 0x02, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff,
+ 0x0d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x5e, 0x02, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0x3e, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+ 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x10, 0x00, 0x01, 0x00, 0x42, 0x00, 0x68, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0e, 0x00,
+ 0x01, 0x00, 0x3a, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x02, 0x00, 0x11, 0x00, 0x18, 0x00, 0x46, 0x00,
+ 0xcc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x32, 0x00, 0x30, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe3, 0x01, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00,
+ 0x03, 0x00, 0x22, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
+ 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00,
+ 0x02, 0x00, 0xeb, 0x00, 0x01, 0x00, 0xae, 0x03, 0x30, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x02, 0x00, 0xea, 0x00,
+ 0x01, 0x00, 0xaa, 0x03, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x32, 0x02, 0x00, 0x00, 0x02, 0x00, 0xec, 0x00, 0x01, 0x00, 0xb2, 0x03,
+ 0x3c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00,
+ 0x02, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x36, 0x00, 0x30, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0x02, 0x00, 0xe9, 0x00,
+ 0x01, 0x00, 0xa6, 0x03, 0x3c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74,
+ 0x00, 0xab, 0xab, 0xab, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x61, 0x74, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x69, 0x72, 0x73, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x00, 0xab,
+ 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x67, 0x44, 0x61, 0x74, 0x61, 0x00,
+ 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+ 0x00, 0xab, 0xab, 0xab, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69,
+ 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x00, 0xab, 0x87, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00,
+ 0xa0, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0xa9, 0x01, 0x00, 0x00,
+ 0x90, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00,
+ 0x08, 0x00, 0x03, 0x00, 0xb4, 0x01, 0x00, 0x00, 0x6d, 0x61, 0x74, 0x43,
+ 0x6f, 0x6c, 0x00, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74,
+ 0x00, 0xab, 0xab, 0xab, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6d, 0x44,
+ 0x69, 0x72, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0xab, 0xab, 0xab,
+ 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x6d, 0x70, 0x45, 0x6e, 0x64, 0x00,
+ 0x72, 0x61, 0x6d, 0x70, 0x53, 0x74, 0x61, 0x72, 0x74, 0x00, 0x72, 0x69,
+ 0x6d, 0x44, 0x61, 0x74, 0x61, 0x00, 0xab, 0xab, 0x01, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x73, 0x75, 0x72, 0x66, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x00, 0x76, 0x69,
+ 0x65, 0x77, 0x56, 0x65, 0x63, 0x00, 0x76, 0x73, 0x5f, 0x32, 0x5f, 0x30,
+ 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28,
+ 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64,
+ 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20,
+ 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e, 0x33, 0x31, 0x31,
+ 0x31, 0x00, 0xab, 0xab, 0x51, 0x00, 0x00, 0x05, 0x04, 0x00, 0x0f, 0xa0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x0f, 0x90, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0x55, 0x90, 0x01, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0x00, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80,
+ 0x02, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xaa, 0x90, 0x00, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80, 0x03, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x08, 0x80, 0x03, 0x00, 0xff, 0x90, 0x05, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0x55, 0x90, 0x09, 0x00, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80, 0x08, 0x00, 0xe4, 0xa0,
+ 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x07, 0x80, 0x0a, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0xaa, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0xe9, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02,
+ 0x03, 0x00, 0x01, 0x80, 0x0d, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x0e, 0x80, 0x0f, 0x00, 0x90, 0xa0, 0x03, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x90, 0x90, 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x07, 0x80,
+ 0x02, 0x00, 0xf9, 0x80, 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x08, 0x80,
+ 0x04, 0x00, 0x00, 0xa0, 0x26, 0x00, 0x00, 0x01, 0x00, 0x00, 0xe4, 0xf0,
+ 0x02, 0x00, 0x00, 0x03, 0x04, 0x00, 0x01, 0x80, 0x03, 0x00, 0xff, 0x80,
+ 0x10, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x04, 0x00, 0x01, 0x80,
+ 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x55, 0xa0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x01, 0xb0, 0x04, 0x00, 0x00, 0x80, 0x08, 0x00, 0x00, 0x04,
+ 0x04, 0x00, 0x01, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x13, 0x20, 0xe4, 0xa1,
+ 0x00, 0x00, 0x00, 0xb0, 0x0b, 0x00, 0x00, 0x03, 0x04, 0x00, 0x01, 0x80,
+ 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x04,
+ 0x04, 0x00, 0x07, 0x80, 0x04, 0x00, 0x00, 0x80, 0x11, 0x20, 0xe4, 0xa0,
+ 0x00, 0x00, 0x00, 0xb0, 0x04, 0x00, 0x00, 0x04, 0x03, 0x00, 0x07, 0x80,
+ 0x04, 0x00, 0xe4, 0x80, 0x0d, 0x00, 0xaa, 0xa0, 0x03, 0x00, 0xe4, 0x80,
+ 0x02, 0x00, 0x00, 0x03, 0x03, 0x00, 0x08, 0x80, 0x03, 0x00, 0xff, 0x80,
+ 0x04, 0x00, 0xaa, 0xa0, 0x27, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x01, 0x80, 0xec, 0x00, 0x55, 0xa0, 0x02, 0x00, 0x00, 0x81,
+ 0xec, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x07, 0x80,
+ 0xeb, 0x00, 0xe4, 0xa0, 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x07, 0x80,
+ 0x02, 0x00, 0xe4, 0x81, 0xea, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0xe4, 0x80,
+ 0xeb, 0x00, 0xe4, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0xec, 0x00, 0xaa, 0xa0, 0x0b, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0xa0,
+ 0x0a, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0xaa, 0xa0, 0x02, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x03, 0x00, 0xe4, 0x80, 0x0b, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0xa0,
+ 0x0a, 0x00, 0x00, 0x03, 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0xaa, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0xd0,
+ 0x01, 0x00, 0xe4, 0x80, 0x0c, 0x00, 0xe4, 0xa0, 0x02, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xff, 0x80, 0x0e, 0x00, 0x55, 0xa1,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x0e, 0x00, 0xaa, 0xa0, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0xff, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x04, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0xaa, 0xa0,
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe0, 0x02, 0x00, 0xe4, 0x90,
+ 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/neoRim_vert.inc b/src/extras/shaders/obj/neoRim_vert.inc
new file mode 100644
index 0000000..7cd199d
--- /dev/null
+++ b/src/extras/shaders/obj/neoRim_vert.inc
@@ -0,0 +1,36 @@
+const char *neoRim_vert_src =
+"uniform vec3 u_viewVec;\n"
+"uniform vec4 u_rampStart;\n"
+"uniform vec4 u_rampEnd;\n"
+"uniform vec3 u_rimData;\n"
+
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * in_normal;\n"
+
+" v_tex0 = in_tex0;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
+
+" // rim light\n"
+" float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec);\n"
+" vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0);\n"
+" v_color.rgb += rimlight.rgb;\n"
+
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_color *= u_matColor;\n"
+
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/neoVehicle_PS.cso b/src/extras/shaders/obj/neoVehicle_PS.cso
new file mode 100644
index 0000000..ded01bf
--- /dev/null
+++ b/src/extras/shaders/obj/neoVehicle_PS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/neoVehicle_PS.inc b/src/extras/shaders/obj/neoVehicle_PS.inc
new file mode 100644
index 0000000..8b77cec
--- /dev/null
+++ b/src/extras/shaders/obj/neoVehicle_PS.inc
@@ -0,0 +1,42 @@
+static unsigned char neoVehicle_PS_cso[] = {
+ 0x00, 0x02, 0xff, 0xff, 0xfe, 0xff, 0x38, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff,
+ 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x94, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
+ 0x00, 0xab, 0xab, 0xab, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x30,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x31,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, 0x5f, 0x32,
+ 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68,
+ 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+ 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e, 0x33,
+ 0x31, 0x31, 0x31, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x07, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x03, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x00, 0x08, 0x0f, 0xa0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x01, 0x08, 0x0f, 0xa0, 0x42, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x01, 0x00, 0xe4, 0xb0, 0x01, 0x08, 0xe4, 0xa0, 0x42, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xb0, 0x00, 0x08, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x01, 0x00, 0xe4, 0x81, 0x00, 0x00, 0xe4, 0x80, 0x05, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0x90,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0xff, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x12, 0x00, 0x00, 0x04,
+ 0x02, 0x00, 0x07, 0x80, 0x00, 0x00, 0xaa, 0xb0, 0x00, 0x00, 0xe4, 0x80,
+ 0x00, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x08, 0x80,
+ 0x00, 0x00, 0xaa, 0xb0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80,
+ 0x02, 0x00, 0xff, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x07, 0x80, 0x02, 0x00, 0xe4, 0x80, 0x01, 0x00, 0xff, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02, 0x00, 0x08, 0x0f, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/neoVehicle_VS.cso b/src/extras/shaders/obj/neoVehicle_VS.cso
new file mode 100644
index 0000000..6ea4798
--- /dev/null
+++ b/src/extras/shaders/obj/neoVehicle_VS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/neoVehicle_VS.inc b/src/extras/shaders/obj/neoVehicle_VS.inc
new file mode 100644
index 0000000..37c5858
--- /dev/null
+++ b/src/extras/shaders/obj/neoVehicle_VS.inc
@@ -0,0 +1,160 @@
+static unsigned char neoVehicle_VS_cso[] = {
+ 0x00, 0x02, 0xfe, 0xff, 0xfe, 0xff, 0xab, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x76, 0x02, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff,
+ 0x0d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x6f, 0x02, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00,
+ 0x01, 0x00, 0x3e, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+ 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x29, 0x00, 0x01, 0x00, 0xa6, 0x00, 0x60, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00,
+ 0x01, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8c, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x3a, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x11, 0x00, 0x18, 0x00, 0x46, 0x00, 0xe0, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00,
+ 0x01, 0x00, 0x32, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xf7, 0x01, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x03, 0x00, 0x22, 0x00,
+ 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00,
+ 0x01, 0x00, 0xaa, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3e, 0x02, 0x00, 0x00, 0x02, 0x00, 0x2b, 0x00, 0x0f, 0x00, 0xae, 0x00,
+ 0x4c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x02, 0x00, 0x00,
+ 0x02, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x36, 0x00, 0x30, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00,
+ 0x04, 0x00, 0x12, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74,
+ 0x00, 0xab, 0xab, 0xab, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x61, 0x74, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x65, 0x79, 0x65, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x69, 0x72, 0x73,
+ 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x00, 0xab, 0x01, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x6f, 0x67, 0x44, 0x61, 0x74, 0x61, 0x00, 0x6c, 0x69, 0x67, 0x68,
+ 0x74, 0x73, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0xab, 0xab, 0xab,
+ 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+ 0x00, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xab,
+ 0x9b, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00,
+ 0xa4, 0x01, 0x00, 0x00, 0xbd, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x03, 0x00,
+ 0xc8, 0x01, 0x00, 0x00, 0x6d, 0x61, 0x74, 0x43, 0x6f, 0x6c, 0x00, 0x6e,
+ 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x00, 0xab, 0xab, 0xab,
+ 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6d, 0x44, 0x69, 0x72, 0x4c, 0x69,
+ 0x67, 0x68, 0x74, 0x73, 0x00, 0xab, 0xab, 0xab, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x65, 0x66, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x00, 0x73, 0x70,
+ 0x65, 0x63, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0xab, 0xab, 0xab,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x03, 0x00,
+ 0xc8, 0x01, 0x00, 0x00, 0x73, 0x75, 0x72, 0x66, 0x50, 0x72, 0x6f, 0x70,
+ 0x73, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x00, 0x76,
+ 0x73, 0x5f, 0x32, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+ 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c,
+ 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70,
+ 0x69, 0x6c, 0x65, 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35,
+ 0x32, 0x2e, 0x33, 0x31, 0x31, 0x31, 0x00, 0xab, 0x51, 0x00, 0x00, 0x05,
+ 0x0b, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x51, 0x00, 0x00, 0x05,
+ 0x3a, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x05,
+ 0x01, 0x00, 0x0f, 0xf0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x05, 0x00, 0x00, 0x80, 0x02, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02,
+ 0x0a, 0x00, 0x00, 0x80, 0x03, 0x00, 0x0f, 0x90, 0x05, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x55, 0x90, 0x01, 0x00, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x0f, 0x80, 0x02, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xaa, 0x90,
+ 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80,
+ 0x03, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xff, 0x90, 0x00, 0x00, 0xe4, 0x80,
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0xe4, 0x80,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0x55, 0x90,
+ 0x09, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80,
+ 0x08, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80, 0x0a, 0x00, 0xe4, 0xa0,
+ 0x01, 0x00, 0xaa, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x01, 0x80, 0x0d, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x07, 0x80, 0x01, 0x00, 0x00, 0x80, 0x0f, 0x00, 0xe4, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x2a, 0x00, 0x55, 0xa0, 0x03, 0x00, 0xe4, 0x90, 0x01, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x08, 0x80, 0x0b, 0x00, 0x00, 0xa0, 0x26, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0xe4, 0xf0, 0x02, 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0xff, 0x80, 0x10, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x01, 0x80, 0x03, 0x00, 0x00, 0x80, 0x0b, 0x00, 0x55, 0xa0,
+ 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xb0, 0x03, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x00, 0x04, 0x03, 0x00, 0x01, 0x80, 0x00, 0x00, 0xe4, 0x80,
+ 0x13, 0x20, 0xe4, 0xa1, 0x00, 0x00, 0x00, 0xb0, 0x0b, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x01, 0x80, 0x03, 0x00, 0x00, 0x80, 0x0b, 0x00, 0x00, 0xa0,
+ 0x05, 0x00, 0x00, 0x04, 0x03, 0x00, 0x07, 0x80, 0x03, 0x00, 0x00, 0x80,
+ 0x11, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0x00, 0xb0, 0x05, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x07, 0x80, 0x03, 0x00, 0xe4, 0x80, 0x0d, 0x00, 0xaa, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x02, 0x00, 0x07, 0x80, 0x03, 0x00, 0xe4, 0x80,
+ 0x2a, 0x00, 0x55, 0xa0, 0x02, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x08, 0x80, 0x01, 0x00, 0xff, 0x80, 0x0b, 0x00, 0xaa, 0xa0,
+ 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x08, 0x80,
+ 0x03, 0x00, 0xff, 0x90, 0x0b, 0x00, 0x00, 0x03, 0x01, 0x00, 0x0f, 0x80,
+ 0x02, 0x00, 0xe4, 0x80, 0x0b, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x0b, 0x00, 0xaa, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0xd0, 0x01, 0x00, 0xe4, 0x80,
+ 0x0c, 0x00, 0xe4, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0x80,
+ 0x00, 0x00, 0x55, 0x90, 0x05, 0x00, 0xe4, 0xa0, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x07, 0x80, 0x04, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0x00, 0x90,
+ 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80,
+ 0x06, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xaa, 0x90, 0x01, 0x00, 0xe4, 0x80,
+ 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80, 0x07, 0x00, 0xe4, 0xa0,
+ 0x00, 0x00, 0xff, 0x90, 0x01, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x81, 0x29, 0x00, 0xe4, 0xa0,
+ 0x24, 0x00, 0x00, 0x02, 0x02, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0x80,
+ 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80, 0x02, 0x00, 0xe4, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x06, 0x80,
+ 0x00, 0x00, 0xd0, 0x80, 0x01, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x06, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x0b, 0x00, 0xff, 0xa0,
+ 0x02, 0x00, 0xd0, 0x81, 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x03, 0xe0,
+ 0x01, 0x00, 0xe9, 0x80, 0x3a, 0x00, 0x00, 0xa0, 0x3a, 0x00, 0x00, 0xa0,
+ 0x0b, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x0b, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x0b, 0x00, 0xaa, 0xa0, 0x02, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x81, 0x0b, 0x00, 0xaa, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x02, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x02, 0x80,
+ 0x01, 0x00, 0x55, 0x80, 0x01, 0x00, 0x55, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x04, 0x80, 0x01, 0x00, 0x55, 0x80, 0x01, 0x00, 0x00, 0x81,
+ 0x0b, 0x00, 0xaa, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x55, 0x80, 0x04, 0x00, 0x00, 0x04,
+ 0x01, 0x00, 0x01, 0x80, 0x2a, 0x00, 0x00, 0xa0, 0x01, 0x00, 0xaa, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x08, 0xd0,
+ 0x01, 0x00, 0x00, 0x80, 0x2a, 0x00, 0xaa, 0xa0, 0x01, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x0f, 0x80, 0x0b, 0x00, 0x00, 0xa0, 0x26, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0xe4, 0xf0, 0x05, 0x00, 0x00, 0x03, 0x02, 0x00, 0x08, 0x80,
+ 0x01, 0x00, 0xff, 0x80, 0x0b, 0x00, 0x55, 0xa0, 0x2e, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x01, 0xb0, 0x02, 0x00, 0xff, 0x80, 0x02, 0x00, 0x00, 0x04,
+ 0x03, 0x00, 0x07, 0x80, 0x02, 0x00, 0xe4, 0x80, 0x2d, 0x20, 0xe4, 0xa1,
+ 0x00, 0x00, 0x00, 0xb0, 0x24, 0x00, 0x00, 0x02, 0x04, 0x00, 0x07, 0x80,
+ 0x03, 0x00, 0xe4, 0x80, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x08, 0x80,
+ 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0xe4, 0x80, 0x0b, 0x00, 0x00, 0x03,
+ 0x02, 0x00, 0x08, 0x80, 0x02, 0x00, 0xff, 0x80, 0x0b, 0x00, 0x00, 0xa0,
+ 0x0a, 0x00, 0x00, 0x03, 0x02, 0x00, 0x08, 0x80, 0x02, 0x00, 0xff, 0x80,
+ 0x0b, 0x00, 0xaa, 0xa0, 0x20, 0x00, 0x00, 0x04, 0x03, 0x00, 0x01, 0x80,
+ 0x02, 0x00, 0xff, 0x80, 0x2d, 0x20, 0xff, 0xa0, 0x00, 0x00, 0x00, 0xb0,
+ 0x05, 0x00, 0x00, 0x04, 0x03, 0x00, 0x07, 0x80, 0x03, 0x00, 0x00, 0x80,
+ 0x2b, 0x20, 0xe4, 0xa0, 0x00, 0x00, 0x00, 0xb0, 0x05, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x07, 0x80, 0x03, 0x00, 0xe4, 0x80, 0x2a, 0x00, 0xff, 0xa0,
+ 0x04, 0x00, 0x00, 0x04, 0x01, 0x00, 0x07, 0x80, 0x03, 0x00, 0xe4, 0x80,
+ 0x2a, 0x00, 0x55, 0xa0, 0x01, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x08, 0x80, 0x01, 0x00, 0xff, 0x80, 0x0b, 0x00, 0xaa, 0xa0,
+ 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x07, 0xd0,
+ 0x01, 0x00, 0xe4, 0x80, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80,
+ 0x00, 0x00, 0xff, 0x80, 0x0e, 0x00, 0x55, 0xa1, 0x05, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0xaa, 0xa0,
+ 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x0e, 0x00, 0xff, 0xa0, 0x0a, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0xe0,
+ 0x00, 0x00, 0x00, 0x80, 0x0b, 0x00, 0xaa, 0xa0, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x03, 0xe0, 0x02, 0x00, 0xe4, 0x90, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/neoVehicle_frag.inc b/src/extras/shaders/obj/neoVehicle_frag.inc
new file mode 100644
index 0000000..2053744
--- /dev/null
+++ b/src/extras/shaders/obj/neoVehicle_frag.inc
@@ -0,0 +1,31 @@
+const char *neoVehicle_frag_src =
+"uniform sampler2D tex0;\n"
+"uniform sampler2D tex1;\n"
+
+"FSIN vec4 v_color;\n"
+"FSIN vec4 v_reflcolor;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN vec2 v_tex1;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 pass1 = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+" vec3 envmap = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)).rgb;\n"
+" pass1.rgb = mix(pass1.rgb, envmap, v_reflcolor.a);\n"
+" pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog);\n"
+"// pass1.rgb += v_reflcolor.rgb * v_fog;\n"
+
+" vec3 pass2 = v_reflcolor.rgb * v_fog;\n"
+
+" vec4 color;\n"
+" color.rgb = pass1.rgb*pass1.a + pass2;\n"
+" color.a = pass1.a;\n"
+
+"// color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
+" DoAlphaTest(color.a);\n"
+
+" FRAGCOLOR(color);\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/neoVehicle_vert.inc b/src/extras/shaders/obj/neoVehicle_vert.inc
new file mode 100644
index 0000000..ebd0ea1
--- /dev/null
+++ b/src/extras/shaders/obj/neoVehicle_vert.inc
@@ -0,0 +1,53 @@
+const char *neoVehicle_vert_src =
+"uniform vec3 u_eye;\n"
+"uniform vec4 u_reflProps;\n"
+"uniform vec4 u_specDir[5];\n"
+"uniform vec4 u_specColor[5];\n"
+
+"#define fresnel (u_reflProps.x)\n"
+"#define lightStrength (u_reflProps.y) // speclight alpha\n"
+"#define shininess (u_reflProps.z)\n"
+"#define specularity (u_reflProps.w)\n"
+
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec4 v_reflcolor;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT vec2 v_tex1;\n"
+"VSOUT float v_fog;\n"
+
+"vec3 DoDirLightSpec(vec3 Ldir, vec3 Lcol, vec3 N, vec3 V, float power)\n"
+"{\n"
+" return pow(clamp(dot(N, normalize(V + -Ldir)), 0.0, 1.0), power)*Lcol;\n"
+"}\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * in_normal;\n"
+" vec3 viewVec = normalize(u_eye - Vertex.xyz);\n"
+
+" v_tex0 = in_tex0;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse*lightStrength;\n"
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_color *= u_matColor;\n"
+
+" // reflect V along Normal\n"
+" vec3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec;\n"
+" v_tex1 = uv2.xy*0.5 + 0.5;\n"
+" float b = 1.0 - clamp(dot(viewVec, Normal), 0.0, 1.0);\n"
+" v_reflcolor = vec4(0.0, 0.0, 0.0, 1.0);\n"
+" v_reflcolor.a = mix(b*b*b*b*b, 1.0, fresnel)*shininess;\n"
+
+" for(int i = 0; i < 5; i++)\n"
+" v_reflcolor.rgb += DoDirLightSpec(u_specDir[i].xyz, u_specColor[i].rgb, Normal, viewVec, u_specDir[i].w)*specularity*lightStrength;\n"
+
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
diff --git a/src/extras/shaders/obj/screenDroplet_PS.cso b/src/extras/shaders/obj/screenDroplet_PS.cso
new file mode 100644
index 0000000..5508096
--- /dev/null
+++ b/src/extras/shaders/obj/screenDroplet_PS.cso
Binary files differ
diff --git a/src/extras/shaders/obj/screenDroplet_PS.inc b/src/extras/shaders/obj/screenDroplet_PS.inc
new file mode 100644
index 0000000..c205518
--- /dev/null
+++ b/src/extras/shaders/obj/screenDroplet_PS.inc
@@ -0,0 +1,29 @@
+static unsigned char screenDroplet_PS_cso[] = {
+ 0x00, 0x02, 0xff, 0xff, 0xfe, 0xff, 0x2c, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff,
+ 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x30,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x31,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, 0x5f, 0x32,
+ 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68,
+ 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+ 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e, 0x33,
+ 0x31, 0x31, 0x31, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x03, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x03, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x00, 0x08, 0x0f, 0xa0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x01, 0x08, 0x0f, 0xa0, 0x42, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0xb0, 0x00, 0x08, 0xe4, 0xa0, 0x42, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0xb0, 0x01, 0x08, 0xe4, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80,
+ 0x00, 0x00, 0xe4, 0x90, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x08, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/obj/screenDroplet_frag.inc b/src/extras/shaders/obj/screenDroplet_frag.inc
new file mode 100644
index 0000000..dd393b0
--- /dev/null
+++ b/src/extras/shaders/obj/screenDroplet_frag.inc
@@ -0,0 +1,20 @@
+const char *screenDroplet_frag_src =
+"uniform sampler2D tex0;\n"
+"uniform sampler2D tex1;\n"
+
+"FSIN vec4 v_color;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN vec2 v_tex1;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 color;\n"
+" color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+" color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));\n"
+
+" FRAGCOLOR(color);\n"
+"}\n"
+
+;
diff --git a/src/extras/shaders/obj/simple_frag.inc b/src/extras/shaders/obj/simple_frag.inc
new file mode 100644
index 0000000..614d79a
--- /dev/null
+++ b/src/extras/shaders/obj/simple_frag.inc
@@ -0,0 +1,19 @@
+const char *simple_frag_src =
+"uniform sampler2D tex0;\n"
+
+"FSIN vec4 v_color;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 color;\n"
+" color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+" color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
+" DoAlphaTest(color.a);\n"
+
+" FRAGCOLOR(color);\n"
+"}\n"
+
+;
diff --git a/src/extras/shaders/screenDroplet.frag b/src/extras/shaders/screenDroplet.frag
new file mode 100644
index 0000000..84d30bd
--- /dev/null
+++ b/src/extras/shaders/screenDroplet.frag
@@ -0,0 +1,18 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN vec2 v_tex1;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 color;
+ color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/screenDroplet_PS.hlsl b/src/extras/shaders/screenDroplet_PS.hlsl
new file mode 100644
index 0000000..4d41da6
--- /dev/null
+++ b/src/extras/shaders/screenDroplet_PS.hlsl
@@ -0,0 +1,17 @@
+struct VS_out {
+ float4 Position : POSITION;
+ float2 TexCoord0 : TEXCOORD0;
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+};
+
+sampler2D tex0 : register(s0);
+sampler2D tex1 : register(s1);
+
+float4 main(VS_out input) : COLOR
+{
+ float4 color = input.Color;
+ color *= tex2D(tex0, input.TexCoord0.xy);
+ color *= tex2D(tex1, input.TexCoord1.xy);
+ return color;
+}
diff --git a/src/extras/shaders/simple.frag b/src/extras/shaders/simple.frag
new file mode 100644
index 0000000..c85bf08
--- /dev/null
+++ b/src/extras/shaders/simple.frag
@@ -0,0 +1,17 @@
+uniform sampler2D tex0;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 color;
+ color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
+ DoAlphaTest(color.a);
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/standardConstants.h b/src/extras/shaders/standardConstants.h
new file mode 100644
index 0000000..088df7d
--- /dev/null
+++ b/src/extras/shaders/standardConstants.h
@@ -0,0 +1,28 @@
+float4x4 combinedMat : register(c0);
+float4x4 worldMat : register(c4);
+float3x3 normalMat : register(c8);
+float4 matCol : register(c12);
+float4 surfProps : register(c13);
+float4 fogData : register(c14);
+float4 ambientLight : register(c15);
+
+#define surfAmbient (surfProps.x)
+#define surfSpecular (surfProps.y)
+#define surfDiffuse (surfProps.z)
+
+#define fogStart (fogData.x)
+#define fogEnd (fogData.y)
+#define fogRange (fogData.z)
+#define fogDisable (fogData.w)
+
+#include "lighting.h"
+
+int numDirLights : register(i0);
+int numPointLights : register(i1);
+int numSpotLights : register(i2);
+int4 firstLight : register(c16);
+Light lights[8] : register(c17);
+
+#define firstDirLight (firstLight.x)
+#define firstPointLight (firstLight.y)
+#define firstSpotLight (firstLight.z)