From c556cbbbe016858537ebc2950e2803bcb85f93d5 Mon Sep 17 00:00:00 2001
From: aap <aap@papnet.eu>
Date: Tue, 18 Aug 2020 10:58:15 +0200
Subject: [PATCH] neo pipelines

---
 src/core/Game.cpp                             |   5 +
 src/core/config.h                             |   4 +-
 src/core/main.cpp                             |  27 +-
 src/core/re3.cpp                              |  12 +
 src/extras/custompipes.cpp                    | 535 +++++++++++++++
 src/extras/custompipes.h                      | 133 ++++
 src/extras/custompipes_d3d9.cpp               | 527 +++++++++++++++
 src/extras/custompipes_gl.cpp                 | 623 ++++++++++++++++++
 src/extras/shaders/Makefile                   |  54 +-
 src/extras/shaders/colourfilterIII.frag       |   7 +-
 src/extras/shaders/colourfilterIII_fs_gl3.inc |   7 +-
 src/extras/shaders/default_UV2.vert           |  29 +
 src/extras/shaders/default_UV2_VS.cso         | Bin 0 -> 628 bytes
 src/extras/shaders/default_UV2_VS.hlsl        |  54 ++
 src/extras/shaders/default_UV2_VS.inc         |  55 ++
 src/extras/shaders/default_UV2_gl3.inc        |  31 +
 src/extras/shaders/lighting.h                 |  44 ++
 src/extras/shaders/neoGloss.frag              |  26 +
 src/extras/shaders/neoGloss.vert              |  29 +
 src/extras/shaders/neoGloss_PS.cso            | Bin 0 -> 444 bytes
 src/extras/shaders/neoGloss_PS.hlsl           |  20 +
 src/extras/shaders/neoGloss_PS.inc            |  39 ++
 src/extras/shaders/neoGloss_VS.cso            | Bin 0 -> 764 bytes
 src/extras/shaders/neoGloss_VS.hlsl           |  35 +
 src/extras/shaders/neoGloss_VS.inc            |  66 ++
 src/extras/shaders/neoGloss_fs_gl3.inc        |  28 +
 src/extras/shaders/neoGloss_vs_gl3.inc        |  31 +
 src/extras/shaders/neoRim.vert                |  37 ++
 src/extras/shaders/neoRimSkin.vert            |  48 ++
 src/extras/shaders/neoRimSkin_VS.cso          | Bin 0 -> 2404 bytes
 src/extras/shaders/neoRimSkin_VS.hlsl         |  73 ++
 src/extras/shaders/neoRimSkin_VS.inc          | 203 ++++++
 src/extras/shaders/neoRimSkin_gl3.inc         |  50 ++
 src/extras/shaders/neoRim_VS.cso              | Bin 0 -> 1384 bytes
 src/extras/shaders/neoRim_VS.hlsl             |  61 ++
 src/extras/shaders/neoRim_VS.inc              | 118 ++++
 src/extras/shaders/neoRim_gl3.inc             |  39 ++
 src/extras/shaders/neoVehicle.frag            |  28 +
 src/extras/shaders/neoVehicle.vert            |  54 ++
 src/extras/shaders/neoVehicle_PS.cso          | Bin 0 -> 476 bytes
 src/extras/shaders/neoVehicle_PS.hlsl         |  34 +
 src/extras/shaders/neoVehicle_PS.inc          |  42 ++
 src/extras/shaders/neoVehicle_VS.cso          | Bin 0 -> 1896 bytes
 src/extras/shaders/neoVehicle_VS.hlsl         |  64 ++
 src/extras/shaders/neoVehicle_VS.inc          | 160 +++++
 src/extras/shaders/neoVehicle_fs_gl3.inc      |  30 +
 src/extras/shaders/neoVehicle_vs_gl3.inc      |  56 ++
 src/extras/shaders/neoWorldIII.frag           |  25 +
 src/extras/shaders/neoWorldIII_PS.cso         | Bin 0 -> 528 bytes
 src/extras/shaders/neoWorldIII_PS.hlsl        |  25 +
 src/extras/shaders/neoWorldIII_PS.inc         |  46 ++
 src/extras/shaders/neoWorldIII_fs_gl3.inc     |  27 +
 src/extras/shaders/simple.frag                |  16 +
 src/extras/shaders/simple_fs_gl3.inc          |  18 +
 src/extras/shaders/standardConstants.h        |  28 +
 src/modelinfo/PedModelInfo.cpp                |   5 +-
 src/modelinfo/SimpleModelInfo.cpp             |   5 +
 src/modelinfo/VehicleModelInfo.cpp            |   5 +
 src/render/Renderer.cpp                       |  12 +-
 src/rw/Lights.h                               |   6 +
 src/rw/VisibilityPlugins.cpp                  |   5 +
 61 files changed, 3723 insertions(+), 18 deletions(-)
 create mode 100644 src/extras/custompipes.cpp
 create mode 100644 src/extras/custompipes.h
 create mode 100644 src/extras/custompipes_d3d9.cpp
 create mode 100644 src/extras/custompipes_gl.cpp
 create mode 100644 src/extras/shaders/default_UV2.vert
 create mode 100644 src/extras/shaders/default_UV2_VS.cso
 create mode 100644 src/extras/shaders/default_UV2_VS.hlsl
 create mode 100644 src/extras/shaders/default_UV2_VS.inc
 create mode 100644 src/extras/shaders/default_UV2_gl3.inc
 create mode 100644 src/extras/shaders/lighting.h
 create mode 100644 src/extras/shaders/neoGloss.frag
 create mode 100644 src/extras/shaders/neoGloss.vert
 create mode 100644 src/extras/shaders/neoGloss_PS.cso
 create mode 100644 src/extras/shaders/neoGloss_PS.hlsl
 create mode 100644 src/extras/shaders/neoGloss_PS.inc
 create mode 100644 src/extras/shaders/neoGloss_VS.cso
 create mode 100644 src/extras/shaders/neoGloss_VS.hlsl
 create mode 100644 src/extras/shaders/neoGloss_VS.inc
 create mode 100644 src/extras/shaders/neoGloss_fs_gl3.inc
 create mode 100644 src/extras/shaders/neoGloss_vs_gl3.inc
 create mode 100644 src/extras/shaders/neoRim.vert
 create mode 100644 src/extras/shaders/neoRimSkin.vert
 create mode 100644 src/extras/shaders/neoRimSkin_VS.cso
 create mode 100644 src/extras/shaders/neoRimSkin_VS.hlsl
 create mode 100644 src/extras/shaders/neoRimSkin_VS.inc
 create mode 100644 src/extras/shaders/neoRimSkin_gl3.inc
 create mode 100644 src/extras/shaders/neoRim_VS.cso
 create mode 100644 src/extras/shaders/neoRim_VS.hlsl
 create mode 100644 src/extras/shaders/neoRim_VS.inc
 create mode 100644 src/extras/shaders/neoRim_gl3.inc
 create mode 100644 src/extras/shaders/neoVehicle.frag
 create mode 100644 src/extras/shaders/neoVehicle.vert
 create mode 100644 src/extras/shaders/neoVehicle_PS.cso
 create mode 100644 src/extras/shaders/neoVehicle_PS.hlsl
 create mode 100644 src/extras/shaders/neoVehicle_PS.inc
 create mode 100644 src/extras/shaders/neoVehicle_VS.cso
 create mode 100644 src/extras/shaders/neoVehicle_VS.hlsl
 create mode 100644 src/extras/shaders/neoVehicle_VS.inc
 create mode 100644 src/extras/shaders/neoVehicle_fs_gl3.inc
 create mode 100644 src/extras/shaders/neoVehicle_vs_gl3.inc
 create mode 100644 src/extras/shaders/neoWorldIII.frag
 create mode 100644 src/extras/shaders/neoWorldIII_PS.cso
 create mode 100644 src/extras/shaders/neoWorldIII_PS.hlsl
 create mode 100644 src/extras/shaders/neoWorldIII_PS.inc
 create mode 100644 src/extras/shaders/neoWorldIII_fs_gl3.inc
 create mode 100644 src/extras/shaders/simple.frag
 create mode 100644 src/extras/shaders/simple_fs_gl3.inc
 create mode 100644 src/extras/shaders/standardConstants.h

diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index d5b376f6..c0530709 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -88,6 +88,7 @@
 #include "debugmenu.h"
 #include "frontendoption.h"
 #include "postfx.h"
+#include "custompipes.h"
 
 eLevelName CGame::currLevel;
 bool CGame::bDemoMode = true;
@@ -360,6 +361,10 @@ bool CGame::Initialise(const char* datFile)
 	CdStreamAddImage("MODELS\\GTA3.IMG");
 	CFileLoader::LoadLevel("DATA\\DEFAULT.DAT");
 	CFileLoader::LoadLevel(datFile);
+#ifdef EXTENDED_PIPELINES
+	// for generic fallback
+	CustomPipes::SetTxdFindCallback();
+#endif
 	CWorld::AddParticles();
 	CVehicleModelInfo::LoadVehicleColours();
 	CVehicleModelInfo::LoadEnvironmentMaps();
diff --git a/src/core/config.h b/src/core/config.h
index bb6adce6..055fcd00 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -8,7 +8,8 @@ enum Config {
 	MAX_CDCHANNELS = 5,
 
 	MODELINFOSIZE = 5500,
-	TXDSTORESIZE = 850,
+//	TXDSTORESIZE = 850,
+	TXDSTORESIZE = 1024,	// for Xbox map
 	EXTRADIRSIZE = 128,
 	CUTSCENEDIRSIZE = 512,
 
@@ -209,6 +210,7 @@ enum Config {
 //#define USE_TEXTURE_POOL
 #define CUTSCENE_BORDERS_SWITCH
 //#define EXTENDED_COLOURFILTER		// more options for colour filter (replaces mblur)
+//#define EXTENDED_PIPELINES		// custom render pipelines (includes Neo)
 
 // Particle
 //#define PC_PARTICLE
diff --git a/src/core/main.cpp b/src/core/main.cpp
index b63688ec..7cde9c4d 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -61,6 +61,7 @@
 #include "MemoryCard.h"
 #include "SceneEdit.h"
 #include "debugmenu.h"
+#include "custompipes.h"
 
 GlobalScene Scene;
 
@@ -349,6 +350,9 @@ PluginAttach(void)
 		
 		return FALSE;
 	}
+#ifdef EXTENDED_PIPELINES
+	CustomPipes::CustomPipeRegister();
+#endif
 
 	return TRUE;
 }
@@ -362,7 +366,11 @@ Initialise3D(void *param)
 		DebugMenuInit();
 		DebugMenuPopulate();
 #endif // !DEBUGMENU
-		return CGame::InitialiseRenderWare();
+		bool ret = CGame::InitialiseRenderWare();
+#ifdef EXTENDED_PIPELINES
+		CustomPipes::CustomPipeInit();	// need Scene.world for this
+#endif
+		return ret;
 	}
 
 	return (FALSE);
@@ -371,6 +379,9 @@ Initialise3D(void *param)
 static void 
 Terminate3D(void)
 {
+#ifdef EXTENDED_PIPELINES
+	CustomPipes::CustomPipeShutdown();
+#endif
 	CGame::ShutdownRenderWare();
 #ifdef DEBUGMENU
 	DebugMenuShutdown();
@@ -1061,6 +1072,12 @@ Idle(void *arg)
 		tbEndTimer("PreRender");
 #endif
 
+#ifdef FIX_BUGS
+		// This has to be done BEFORE RwCameraBeginUpdate
+		RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip());
+		RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart());
+#endif
+
 		if(CWeather::LightningFlash && !CCullZones::CamNoRain()){
 			if(!DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255))
 				return;
@@ -1073,9 +1090,10 @@ Idle(void *arg)
 
 		DefinedState();
 
-		// BUG. This has to be done BEFORE RwCameraBeginUpdate
+#ifndef FIX_BUGS
 		RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip());
 		RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart());
+#endif
 
 #ifdef TIMEBARS
 		tbStartTimer(0, "RenderScene");
@@ -1084,6 +1102,11 @@ Idle(void *arg)
 #ifdef TIMEBARS
 		tbEndTimer("RenderScene");
 #endif
+
+#ifdef EXTENDED_PIPELINES
+		CustomPipes::EnvMapRender();
+#endif
+
 		RenderDebugShit();
 		RenderEffects();
 
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 9edd9497..e8d14435 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -31,6 +31,7 @@
 #include "main.h"
 #include "MBlur.h"
 #include "postfx.h"
+#include "custompipes.h"
 
 #ifndef _WIN32
 #include "assert.h"
@@ -469,6 +470,17 @@ DebugMenuPopulate(void)
 		DebugMenuEntrySetWrap(e, true);
 		DebugMenuAddVar("Render", "Intensity", &CPostFX::Intensity, nil, 0.05f, 0, 10.0f);
 		DebugMenuAddVarBool8("Render", "Motion Blur", &CPostFX::MotionBlurOn, nil);
+#endif
+#ifdef EXTENDED_PIPELINES
+		static const char *vehpipenames[] = { "MatFX", "Neo" };
+		e = DebugMenuAddVar("Render", "Vehicle Pipeline", &CustomPipes::VehiclePipeSwitch, nil,
+			1, CustomPipes::VEHICLEPIPE_MATFX, CustomPipes::VEHICLEPIPE_NEO, vehpipenames);
+		DebugMenuEntrySetWrap(e, true);
+		DebugMenuAddVar("Render", "Neo Vehicle Shininess", &CustomPipes::VehicleShininess, nil, 0.1f, 0, 1.0f);
+		DebugMenuAddVar("Render", "Neo Vehicle Specularity", &CustomPipes::VehicleSpecularity, nil, 0.1f, 0, 1.0f);
+		DebugMenuAddVar("Render", "Neo Ped Rim light", &CustomPipes::RimlightMult, nil, 0.1f, 0, 1.0f);
+		DebugMenuAddVar("Render", "Neo World Lightmaps", &CustomPipes::LightmapMult, nil, 0.1f, 0, 1.0f);
+		DebugMenuAddVar("Render", "Neo Road Gloss", &CustomPipes::GlossMult, nil, 0.1f, 0, 1.0f);
 #endif
 		DebugMenuAddVarBool8("Render", "Show Ped Paths", &gbShowPedPaths, nil);
 		DebugMenuAddVarBool8("Render", "Show Car Paths", &gbShowCarPaths, nil);
diff --git a/src/extras/custompipes.cpp b/src/extras/custompipes.cpp
new file mode 100644
index 00000000..fb529b47
--- /dev/null
+++ b/src/extras/custompipes.cpp
@@ -0,0 +1,535 @@
+#define WITH_D3D
+#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;
+}
+
+
+
+static 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 = { CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), 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_NEO;
+float VehicleShininess = 0.7f;	// the default is a bit extreme
+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
+ */
+
+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
+ */
+
+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
+ */
+
+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;
+	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 00000000..4ebe586f
--- /dev/null
+++ b/src/extras/custompipes.h
@@ -0,0 +1,133 @@
+#pragma once
+
+#ifdef EXTENDED_PIPELINES
+#ifdef LIBRW
+
+namespace CustomPipes {
+
+
+
+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 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 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 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
+#endif
diff --git a/src/extras/custompipes_d3d9.cpp b/src/extras/custompipes_d3d9.cpp
new file mode 100644
index 00000000..bfc744a3
--- /dev/null
+++ b/src/extras/custompipes_d3d9.cpp
@@ -0,0 +1,527 @@
+#define WITH_D3D
+#include "common.h"
+
+#ifdef RW_D3D9
+#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
+
+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;
+	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(m->color, m->surfaceProps);
+
+		if(m->texture)
+			d3d::setTexture(0, m->texture);
+		else
+			d3d::setTexture(0, gpWhiteTexture);
+		setPixelShader(neoVehicle_PS);
+
+		drawInst(header, inst);
+		inst++;
+	}
+
+	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/neoVehicle_VS.inc"
+	neoVehicle_VS = rw::d3d::createVertexShader(neoVehicle_VS_cso);
+	assert(neoVehicle_VS);
+
+#include "shaders/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::d3d9::ObjPipeline*)vehiclePipe)->destroy();
+	vehiclePipe = nil;
+}
+
+
+
+/*
+ * Neo World pipe
+ */
+
+static void *neoWorld_VS;
+static void *neoWorldIII_PS;
+
+static void
+worldRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
+{
+	using namespace rw;
+	using namespace rw::d3d;
+	using namespace rw::d3d9;
+
+	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(neoWorldIII_PS);
+
+		drawInst(header, inst);
+		inst++;
+	}
+}
+
+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/default_UV2_VS.inc"
+	neoWorld_VS = rw::d3d::createVertexShader(default_UV2_VS_cso);
+	assert(neoWorld_VS);
+
+#include "shaders/neoWorldIII_PS.inc"
+	neoWorldIII_PS = rw::d3d::createPixelShader(neoWorldIII_PS_cso);
+	assert(neoWorldIII_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(neoWorldIII_PS);
+	neoWorldIII_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;
+
+	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/neoGloss_VS.inc"
+	neoGloss_VS = rw::d3d::createVertexShader(neoGloss_VS_cso);
+	assert(neoGloss_VS);
+
+#include "shaders/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::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;
+
+	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());
+
+	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(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;
+
+	int vsBits;
+
+	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(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/neoRim_VS.inc"
+	neoRim_VS = rw::d3d::createVertexShader(neoRim_VS_cso);
+	assert(neoRim_VS);
+
+#include "shaders/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
+#endif
diff --git a/src/extras/custompipes_gl.cpp b/src/extras/custompipes_gl.cpp
new file mode 100644
index 00000000..5717c83b
--- /dev/null
+++ b/src/extras/custompipes_gl.cpp
@@ -0,0 +1,623 @@
+#include "common.h"
+
+#ifdef RW_OPENGL
+#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 {
+
+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;
+
+	setWorldMatrix(atomic->getFrame()->getLTM());
+	lightingCB(atomic);
+
+#ifdef RW_GL_USE_VAOS
+	glBindVertexArray(header->vao);
+#else
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+	glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+	setAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+
+	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(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++;
+	}
+
+	SetRenderState(SRCBLEND, BLENDSRCALPHA);
+
+#ifndef RW_GL_USE_VAOS
+	disableAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+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);
+	}
+
+
+	{
+#ifdef RW_GLES2
+#include "gl2_shaders/neoVehicle_fs_gl2.inc"
+#include "gl2_shaders/neoVehicle_vs_gl2.inc"
+#else
+#include "shaders/neoVehicle_fs_gl3.inc"
+#include "shaders/neoVehicle_vs_gl3.inc"
+#endif
+	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;
+
+	Material *m;
+
+	setWorldMatrix(atomic->getFrame()->getLTM());
+	lightingCB(atomic);
+
+#ifdef RW_GL_USE_VAOS
+	glBindVertexArray(header->vao);
+#else
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+	glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+	setAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+
+	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++;
+	}
+#ifndef RW_GL_USE_VAOS
+	disableAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+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);
+
+	{
+#ifdef RW_GLES2
+#include "gl2_shaders/neoWorldIII_fs_gl2.inc"
+#include "gl2_shaders/default_UV2_gl2.inc"
+#else
+#include "shaders/neoWorldIII_fs_gl3.inc"
+#include "shaders/default_UV2_gl3.inc"
+#endif
+	const char *vs[] = { shaderDecl, header_vert_src, default_UV2_vert_src, nil };
+	const char *fs[] = { shaderDecl, header_frag_src, neoWorldIII_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);
+
+	Material *m;
+
+#ifdef RW_GL_USE_VAOS
+	glBindVertexArray(header->vao);
+#else
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+	glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+	setAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+
+	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);
+	glUniform4fv(U(u_reflProps), 1, (float*)&GlossMult);
+
+	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);
+
+#ifndef RW_GL_USE_VAOS
+	disableAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+void
+CreateGlossPipe(void)
+{
+	using namespace rw;
+	using namespace rw::gl3;
+
+	{
+#ifdef RW_GLES2
+#include "gl2_shaders/neoGloss_fs_gl2.inc"
+#include "gl2_shaders/neoGloss_vs_gl2.inc"
+#else
+#include "shaders/neoGloss_fs_gl3.inc"
+#include "shaders/neoGloss_vs_gl3.inc"
+#endif
+	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;
+
+	Material *m;
+
+	setWorldMatrix(atomic->getFrame()->getLTM());
+	lightingCB(atomic);
+
+#ifdef RW_GL_USE_VAOS
+	glBindVertexArray(header->vao);
+#else
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+	glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+	setAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+
+	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(m->color, m->surfaceProps);
+
+		setTexture(0, m->texture);
+
+		rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+		drawInst(header, inst);
+		inst++;
+	}
+#ifndef RW_GL_USE_VAOS
+	disableAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+static void
+rimRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
+{
+	using namespace rw;
+	using namespace rw::gl3;
+
+	Material *m;
+
+	setWorldMatrix(atomic->getFrame()->getLTM());
+	lightingCB(atomic);
+
+#ifdef RW_GL_USE_VAOS
+	glBindVertexArray(header->vao);
+#else
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+	glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+	setAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+
+	InstanceData *inst = header->inst;
+	rw::int32 n = header->numMeshes;
+
+	neoRimShader->use();
+
+	uploadRimData(atomic->geometry->flags & Geometry::LIGHT);
+
+	while(n--){
+		m = inst->material;
+
+		setMaterial(m->color, m->surfaceProps);
+
+		setTexture(0, m->texture);
+
+		rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+		drawInst(header, inst);
+		inst++;
+	}
+#ifndef RW_GL_USE_VAOS
+	disableAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+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);
+	}
+
+	{
+#ifdef RW_GLES2
+#include "gl2_shaders/simple_fs_gl2.inc"
+#include "gl2_shaders/neoRimSkin_gl2.inc"
+#else
+#include "shaders/simple_fs_gl3.inc"
+#include "shaders/neoRimSkin_gl3.inc"
+#endif
+	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);
+	}
+
+	{
+#ifdef RW_GLES2
+#include "gl2_shaders/simple_fs_gl2.inc"
+#include "gl2_shaders/neoRim_gl2.inc"
+#else
+#include "shaders/simple_fs_gl3.inc"
+#include "shaders/neoRim_gl3.inc"
+#endif
+	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
+#endif
diff --git a/src/extras/shaders/Makefile b/src/extras/shaders/Makefile
index 87be011e..6cbbf237 100644
--- a/src/extras/shaders/Makefile
+++ b/src/extras/shaders/Makefile
@@ -1,10 +1,26 @@
-all: im2d_gl3.inc colourfilterIII_fs_gl3.inc contrast_fs_gl3.inc
+all: im2d_gl3.inc simple_fs_gl3.inc default_UV2_gl3.inc \
+	colourfilterIII_fs_gl3.inc contrast_fs_gl3.inc \
+	neoRim_gl3.inc neoRimSkin_gl3.inc \
+	neoWorldIII_fs_gl3.inc neoGloss_vs_gl3.inc neoGloss_fs_gl3.inc \
+	neoVehicle_vs_gl3.inc neoVehicle_fs_gl3.inc
 
 im2d_gl3.inc: im2d.vert
 	(echo 'const char *im2d_vert_src =';\
 	 sed 's/..*/"&\\n"/' im2d.vert;\
 	 echo ';') >im2d_gl3.inc
 
+simple_fs_gl3.inc: simple.frag
+	(echo 'const char *simple_frag_src =';\
+	 sed 's/..*/"&\\n"/' simple.frag;\
+	 echo ';') >simple_fs_gl3.inc
+
+default_UV2_gl3.inc: default_UV2.vert
+	(echo 'const char *default_UV2_vert_src =';\
+	 sed 's/..*/"&\\n"/' default_UV2.vert;\
+	 echo ';') >default_UV2_gl3.inc
+
+
+
 colourfilterIII_fs_gl3.inc: colourfilterIII.frag
 	(echo 'const char *colourfilterIII_frag_src =';\
 	 sed 's/..*/"&\\n"/' colourfilterIII.frag;\
@@ -14,3 +30,39 @@ contrast_fs_gl3.inc: contrast.frag
 	(echo 'const char *contrast_frag_src =';\
 	 sed 's/..*/"&\\n"/' contrast.frag;\
 	 echo ';') >contrast_fs_gl3.inc
+
+
+neoRim_gl3.inc: neoRim.vert
+	(echo 'const char *neoRim_vert_src =';\
+	 sed 's/..*/"&\\n"/' neoRim.vert;\
+	 echo ';') >neoRim_gl3.inc
+
+neoRimSkin_gl3.inc: neoRimSkin.vert
+	(echo 'const char *neoRimSkin_vert_src =';\
+	 sed 's/..*/"&\\n"/' neoRimSkin.vert;\
+	 echo ';') >neoRimSkin_gl3.inc
+
+neoWorldIII_fs_gl3.inc: neoWorldIII.frag
+	(echo 'const char *neoWorldIII_frag_src =';\
+	 sed 's/..*/"&\\n"/' neoWorldIII.frag;\
+	 echo ';') >neoWorldIII_fs_gl3.inc
+
+neoGloss_fs_gl3.inc: neoGloss.frag
+	(echo 'const char *neoGloss_frag_src =';\
+	 sed 's/..*/"&\\n"/' neoGloss.frag;\
+	 echo ';') >neoGloss_fs_gl3.inc
+
+neoGloss_vs_gl3.inc: neoGloss.vert
+	(echo 'const char *neoGloss_vert_src =';\
+	 sed 's/..*/"&\\n"/' neoGloss.vert;\
+	 echo ';') >neoGloss_vs_gl3.inc
+
+neoVehicle_vs_gl3.inc: neoVehicle.vert
+	(echo 'const char *neoVehicle_vert_src =';\
+	 sed 's/..*/"&\\n"/' neoVehicle.vert;\
+	 echo ';') >neoVehicle_vs_gl3.inc
+
+neoVehicle_fs_gl3.inc: neoVehicle.frag
+	(echo 'const char *neoVehicle_frag_src =';\
+	 sed 's/..*/"&\\n"/' neoVehicle.frag;\
+	 echo ';') >neoVehicle_fs_gl3.inc
diff --git a/src/extras/shaders/colourfilterIII.frag b/src/extras/shaders/colourfilterIII.frag
index e19a8600..4c9a8400 100644
--- a/src/extras/shaders/colourfilterIII.frag
+++ b/src/extras/shaders/colourfilterIII.frag
@@ -11,15 +11,10 @@ void
 main(void)
 {
 	float a = u_blurcolor.a;
-
-	vec4 doublec = clamp(u_blurcolor*2, 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 doublec = clamp(u_blurcolor*2, 0.0, 1.0);
-		vec4 tmp = dst*(1.0-a) + prev*doublec*a;
-		tmp += prev*u_blurcolor;
-		tmp += prev*u_blurcolor;
+		vec4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;
 		prev = clamp(tmp, 0.0, 1.0);
 	}
 	color.rgb = prev.rgb;
diff --git a/src/extras/shaders/colourfilterIII_fs_gl3.inc b/src/extras/shaders/colourfilterIII_fs_gl3.inc
index f57b9cdd..5530a4fa 100644
--- a/src/extras/shaders/colourfilterIII_fs_gl3.inc
+++ b/src/extras/shaders/colourfilterIII_fs_gl3.inc
@@ -12,15 +12,10 @@ const char *colourfilterIII_frag_src =
 "main(void)\n"
 "{\n"
 "	float a = u_blurcolor.a;\n"
-
-"	vec4 doublec = clamp(u_blurcolor*2, 0.0, 1.0);\n"
 "	vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
 "	vec4 prev = dst;\n"
 "	for(int i = 0; i < 5; i++){\n"
-"//		vec4 doublec = clamp(u_blurcolor*2, 0.0, 1.0);\n"
-"		vec4 tmp = dst*(1.0-a) + prev*doublec*a;\n"
-"		tmp += prev*u_blurcolor;\n"
-"		tmp += prev*u_blurcolor;\n"
+"		vec4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;\n"
 "		prev = clamp(tmp, 0.0, 1.0);\n"
 "	}\n"
 "	color.rgb = prev.rgb;\n"
diff --git a/src/extras/shaders/default_UV2.vert b/src/extras/shaders/default_UV2.vert
new file mode 100644
index 00000000..3dbad20f
--- /dev/null
+++ b/src/extras/shaders/default_UV2.vert
@@ -0,0 +1,29 @@
+layout(location = 0) in vec3 in_pos;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec4 in_color;
+layout(location = 3) in vec2 in_tex0;
+layout(location = 4) in vec2 in_tex1;
+
+out vec4 v_color;
+out vec2 v_tex0;
+out vec2 v_tex1;
+out 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.cso b/src/extras/shaders/default_UV2_VS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..5a48c663dc34a8ea60781a6c045d0e5e710a87c4
GIT binary patch
literal 628
zcmY*WyH3ME5F9@u5G3&rAmI|4hy;NY3J?S&ibMi}>&WCSB{8xM^?iar;b;>+fr5gL
zhK7zJg})<a&JF=iTJO&6-tOEQ`XX5*7pUEyo^1j6qJdnFPGM-kC!L__un3P4F;+*?
zbehTUGrc-5G#y3O9j)r#QZ7%fvf|!Tu0ZZ^#ku3VVRsVNhy6i_*=(l4VBVx<r|q#|
z((N?dkQsxS-(1h@pSz)pu^ZOB5rPN5cjbHc0Um=^rL~8~u<d(+*9+~Pn_c^|-mKfr
zf!pc&cFh~#4@V5DrAoC_J*brS%jL3~e+}eJSTdU~PL?vTojqFsR-|I?N@%NC6WmkE
zZ;&?_Q$N?VCM?9#RO@04#G=3z8i<$qb=D@)CSb5{nrES8k3ZOvoTc!Gx}RsiC1$<D
zK0p1_`w~#AjYT%oqOO?S^dHCh<dT%5NJE1rh(!_3Q>NQ`RXwsf8s7=#rZUs1Y86hF
ZI?cJlCLEdFHRjZmzK{QM&iPT20N+GebZr0t

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/default_UV2_VS.hlsl b/src/extras/shaders/default_UV2_VS.hlsl
new file mode 100644
index 00000000..e78a9907
--- /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/default_UV2_VS.inc b/src/extras/shaders/default_UV2_VS.inc
new file mode 100644
index 00000000..de832107
--- /dev/null
+++ b/src/extras/shaders/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/default_UV2_gl3.inc b/src/extras/shaders/default_UV2_gl3.inc
new file mode 100644
index 00000000..14106b29
--- /dev/null
+++ b/src/extras/shaders/default_UV2_gl3.inc
@@ -0,0 +1,31 @@
+const char *default_UV2_vert_src =
+"layout(location = 0) in vec3 in_pos;\n"
+"layout(location = 1) in vec3 in_normal;\n"
+"layout(location = 2) in vec4 in_color;\n"
+"layout(location = 3) in vec2 in_tex0;\n"
+"layout(location = 4) in vec2 in_tex1;\n"
+
+"out vec4 v_color;\n"
+"out vec2 v_tex0;\n"
+"out vec2 v_tex1;\n"
+"out 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/lighting.h b/src/extras/shaders/lighting.h
new file mode 100644
index 00000000..4b081962
--- /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/neoGloss.frag b/src/extras/shaders/neoGloss.frag
new file mode 100644
index 00000000..14ef0e15
--- /dev/null
+++ b/src/extras/shaders/neoGloss.frag
@@ -0,0 +1,26 @@
+uniform sampler2D tex0;
+
+uniform vec4 u_reflProps;
+
+#define glossMult (u_reflProps.x)
+
+in vec3 v_normal;
+in vec3 v_light;
+in vec2 v_tex0;
+in float v_fog;
+
+out vec4 color;
+
+void
+main(void)
+{
+	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);
+}
+
diff --git a/src/extras/shaders/neoGloss.vert b/src/extras/shaders/neoGloss.vert
new file mode 100644
index 00000000..78dd1b33
--- /dev/null
+++ b/src/extras/shaders/neoGloss.vert
@@ -0,0 +1,29 @@
+uniform vec3 u_eye;
+
+
+layout(location = 0) in vec3 in_pos;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec4 in_color;
+layout(location = 3) in vec2 in_tex0;
+
+out vec3 v_normal;
+out vec3 v_light;
+out vec2 v_tex0;
+out 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.cso b/src/extras/shaders/neoGloss_PS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..aa88e45013a1c77b32447b0caf4be7905f004e50
GIT binary patch
literal 444
zcma)1F;2rk5S%*<ae?FyAmI`Utk{Uc9V8SKi39}dfFO`aMvh`5r6W)96RyUMe~{1Q
z9UQYKj6%msbGtLUH@63E?Uy}4|L&r90N{fFHO(X(3Rp5-5}HI|57(kI9*7-6u`^|s
zc+K*n7`|sE>RP&TgBC8z$+Ani_OSm)pBLj~+{JMAw8)G6r3{a5kHf3MXb_I3kI$1u
z*w5eQvy4R=C25qNCDCad$G8D@fxyuG99XU0BRmdeRSH0-*{Z9yR_L^K1wSx7_l<z_
zn+E>Ic!0+l;T3%haPKrtO>JpD@9}q{tu(=9emJP%U!42PJHPWS2R<9|YdQT4p}DV{
Hw-)#YPXbCc

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoGloss_PS.hlsl b/src/extras/shaders/neoGloss_PS.hlsl
new file mode 100644
index 00000000..b3c97639
--- /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_PS.inc b/src/extras/shaders/neoGloss_PS.inc
new file mode 100644
index 00000000..97e5641d
--- /dev/null
+++ b/src/extras/shaders/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/neoGloss_VS.cso b/src/extras/shaders/neoGloss_VS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..9635b8b7e20db1928f385e8189118a68f431a573
GIT binary patch
literal 764
zcmZ`$Jx?1!5PkPyd?JVwA!Q_7P{ajR>?DQLAOb}Sh!nt83S{iFeUf3(`IIQ#(Np*b
zP#g^fZc?VCiIgssloVH_rAJ)e>|Vl$l#%A<&6}N>ce}7}^V|FrR<~E)JqPgG;)h&`
zL2@u(q9=ql-R1X)QS6v*qsk+vNQIz%quZDz_ZHtZ1N_w792|VWGnr3YWVAWW#nXTD
zI%;n>+z+}N?U-jBT`i6y_!@AgQ@EMYQ~kAe+%7nm(bzET58@FzYN#SiCKJ<lS3gjr
zFb>r|x?vP_G@ATk>m~hn5wD9V_h4ZL9(Ugme^Jp;3;aCVY3#hjM%aneNcG~%i_L1~
z{rc8=Woyvx22o{I?Hz<ef@ZDJtTo>>YOm_`y6E``lsxz;kSuegxV8mz#R=e#1#@>|
z5i>eKnS6^lOI>*e<}NWC$3RAGxGBw@J>bfk%QeS0EM{egRq~76W9ivh^0#Jj-aN9C
z|J6Jo6;15Bklh|yI;U=}k^QOBBl%;EKKBTBU!$Kmn!D3z!D4To%%XL|ccnD{Ty~ke
ouy`+E?JRww|3}VBo~P3~cdsNDou=2hqNcII<qHo#(mV(L0G#EFZ2$lO

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoGloss_VS.hlsl b/src/extras/shaders/neoGloss_VS.hlsl
new file mode 100644
index 00000000..d166171c
--- /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/neoGloss_VS.inc b/src/extras/shaders/neoGloss_VS.inc
new file mode 100644
index 00000000..1ec03761
--- /dev/null
+++ b/src/extras/shaders/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/neoGloss_fs_gl3.inc b/src/extras/shaders/neoGloss_fs_gl3.inc
new file mode 100644
index 00000000..736b0c5d
--- /dev/null
+++ b/src/extras/shaders/neoGloss_fs_gl3.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"
+
+"in vec3 v_normal;\n"
+"in vec3 v_light;\n"
+"in vec2 v_tex0;\n"
+"in float v_fog;\n"
+
+"out vec4 color;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+"	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"
+"}\n"
+
+;
diff --git a/src/extras/shaders/neoGloss_vs_gl3.inc b/src/extras/shaders/neoGloss_vs_gl3.inc
new file mode 100644
index 00000000..4adc9cb2
--- /dev/null
+++ b/src/extras/shaders/neoGloss_vs_gl3.inc
@@ -0,0 +1,31 @@
+const char *neoGloss_vert_src =
+"uniform vec3 u_eye;\n"
+
+
+"layout(location = 0) in vec3 in_pos;\n"
+"layout(location = 1) in vec3 in_normal;\n"
+"layout(location = 2) in vec4 in_color;\n"
+"layout(location = 3) in vec2 in_tex0;\n"
+
+"out vec3 v_normal;\n"
+"out vec3 v_light;\n"
+"out vec2 v_tex0;\n"
+"out 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/neoRim.vert b/src/extras/shaders/neoRim.vert
new file mode 100644
index 00000000..4a2b545f
--- /dev/null
+++ b/src/extras/shaders/neoRim.vert
@@ -0,0 +1,37 @@
+uniform vec3 u_viewVec;
+uniform vec4 u_rampStart;
+uniform vec4 u_rampEnd;
+uniform vec3 u_rimData;
+
+layout(location = 0) in vec3 in_pos;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec4 in_color;
+layout(location = 3) in vec2 in_tex0;
+
+out vec4 v_color;
+out vec2 v_tex0;
+out 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 00000000..f16f2310
--- /dev/null
+++ b/src/extras/shaders/neoRimSkin.vert
@@ -0,0 +1,48 @@
+uniform mat4 u_boneMatrices[64];
+
+uniform vec3 u_viewVec;
+uniform vec4 u_rampStart;
+uniform vec4 u_rampEnd;
+uniform vec3 u_rimData;
+
+layout(location = 0) in vec3 in_pos;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec4 in_color;
+layout(location = 3) in vec2 in_tex0;
+layout(location = 11) in vec4 in_weights;
+layout(location = 12) in vec4 in_indices;
+
+out vec4 v_color;
+out vec2 v_tex0;
+out 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.cso b/src/extras/shaders/neoRimSkin_VS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..8410504e3efc71e81bc7be64baac7da19c1d0ee4
GIT binary patch
literal 2404
zcmah}J8v9S7(Mf_yd;j-Q3@!KSRn)$Bnvwa$rQoF22nT?U@sL?G}%Wo((cY`b`si2
zOSMXeL@|+~6l;Vcl#V6^KY)T3Da8~daX~>tli_^dox5vmJ$St1d+#~l{a*Lnf%~d>
zRXoA={-@X81n`5)f5etK$QllCpV%diF6vxf#SZ84>?^j4r+9>Ri6x2wKCROd8!NWL
zz1Q$#iT$M5GO>@R{+=jy9&g}t38v2qM;A-9+nCzDu2>Bo-o`tn-6f9V+M)GMiS4W1
zOJYBJHs@~@dqHe6#kz_e68ps)mU~mh&JnvZjr+Y~&xt*n+6DcaVP}x+hPQ4fI5;?P
zn3coo%QA5@8g#A)NgTF1BjG*rKHn`<9$FC{3_5Mv5EX8=w;RSI>sjhL>bD!++6j^X
zJqbLbn_d*36n_|v!X%6aXoqp9rL%)yncQ!D|0u^l%W*;bw*=|zU&Q)BvK{p>h~j?G
zlLX8BB~Q}&4DR%I!dT1K`sB16{WE+V^oO4f+K_QC3E~8C*w@T$uV%hRcjE4saWov^
zZrJ(mn@$UNM>jTZe1z*^D~?7{H)*{8^=jjb-M!t$-tC~>i5uHde;D>S*<9P$T-&_7
zv37BNeVvJZ1uXceXTa8$$t3+m#<z^JQlLJTxd)M{nX`T#yP2Qi+$-m-HKi}Lrv%ak
z&S}f7b<Xp#4`gfVxAvGJ(i-K@v!dU8{9z-{fa38y?<l`o$-BybFvmC5zgy|w@>TvV
zf84*+<6@EZ=d86UHL-?8$$@K(1)tS0IVA_nSLI;&<2l%vCWi?>8%JXnr^K{;RZPqO
zU(7;d+Fm$lrmrE-OkQUh|6wLtaPd%jqWw8Tp5GGo=^x3@b5PTsJy#sA%e4pbD_V2X
zbJU9L_1N1wZA5<_bt1e^jC1Z6+Ztmuvv>8gmj6xNdA{+LuF!|%C~F@D-<keoE&5x4
z_94B{$RCLQ&eC2m?o+?^EXAG}?H83h-^f+3lB489eKY4t=5v%BBrn!OACv4o(1<_Y
z_w<mtIw<ZP0vsj(#q=fXlRXZWxo3Ru1;+GabtU)iU$v21tkw?cS8}y$=>hNJk@ZA<
zidKy_^VHbanP1MltbM)YCz<%?UHx6`mza~S9eTbdfA9?E@1mT)@NO#nNq5okzVi02
zAFW63(kvd-AHR@lKOfKXRq-r;Jf7{TsUNE0S<J~PF)d#e)AI8%!8543tEMkibrz@e
z)$%9DH0NH7c_yjpb)Efe%6XUilC|hqG$xt=f2t2+a#xL(1rr~<bD3zC>!-37&GOoj
kE7D~&A4m+d2G>TPvvr~RWQb>AJh>Ofm-mdl85afcA0B<-r~m)}

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoRimSkin_VS.hlsl b/src/extras/shaders/neoRimSkin_VS.hlsl
new file mode 100644
index 00000000..87cc0931
--- /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/neoRimSkin_VS.inc b/src/extras/shaders/neoRimSkin_VS.inc
new file mode 100644
index 00000000..ac182956
--- /dev/null
+++ b/src/extras/shaders/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/neoRimSkin_gl3.inc b/src/extras/shaders/neoRimSkin_gl3.inc
new file mode 100644
index 00000000..70948e1f
--- /dev/null
+++ b/src/extras/shaders/neoRimSkin_gl3.inc
@@ -0,0 +1,50 @@
+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"
+
+"layout(location = 0) in vec3 in_pos;\n"
+"layout(location = 1) in vec3 in_normal;\n"
+"layout(location = 2) in vec4 in_color;\n"
+"layout(location = 3) in vec2 in_tex0;\n"
+"layout(location = 11) in vec4 in_weights;\n"
+"layout(location = 12) in vec4 in_indices;\n"
+
+"out vec4 v_color;\n"
+"out vec2 v_tex0;\n"
+"out 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/neoRim_VS.cso b/src/extras/shaders/neoRim_VS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..4af538b1f4f210e1fef2f8b667e24ad3b279591b
GIT binary patch
literal 1384
zcmZuwJ#W)c6g{?6C6u;l#lnDu#Q<fA+8~gM5R|qchExJdIw2~PCat9;j%>Fux)W=D
zg1dl7Bvc`O06&0%AHYzFEd3a|vNW7~?IcCQk@N07_uO~Sz3&<3dH=ls3Cph@FI@vr
zHTg$ul8g8-fH%Z!t|szyA7YNSJd26}8AeJe_FA!N@|LijV(%53B6fe68!0w}t9XhZ
zDesqJ6XX?#yx)rDVBrSNQXb2ZBd>{R(aXo?YT|_07i*wzR<UDZogr3K?3CD7>p_}Z
zQf!jgy<y#T#g2%59P*sTTesQ_OYYWoh;FxQP?f>%2eK+23C-%}suMC|F*1tPTt66O
zitdYfHE(Oh2_4jB;{X+}?)m@QzvTsP=z2|Tx_-5yR`&yoxB15D`Z-<4I1>XCkjZ@`
z)^NgQua2hYH=Mc%q<+yTKZf;b?le|hU)u{LE)zBMm&ARi(R$L{gsf|!<A?CwhAJP-
zVzgJV<JX@1UMs+^TYa})tzb9UC~nMQ)vfqm;MGEV_T_E+S!u0guWdV<Ro`Cr8ZEa@
zW1&!7C@kD97VgZ?&r`??U@U`toTyk_1foY`Ul)ONfP7nQi?)&4>_6gtReYSbMVlBY
zdGgB~Suf7W)m6V2bDuWQiw2m<IMDeAvlQzq#+k8p{=4LeZ6L=Q{j^4wSh;;^SR)(d
zXp?tgzic$FSW+WLZJc%_`yC}*_G3i+4ppa#sVI+jEInFi%aJplWsiTfzQy{~9^ceY
zqjyGe7H^+uil&K6@8Xlgw;UH#zi}WwYiA@TwXfK{eE<iS|KV#sIa$r0v3s#_WLBaf
z!@Nm!V$O$pV2Ku~n;~|KMB!0ZbjrGn@0~)gt7$U#guP0R*qr&^G5v&}jhHH!a+0$X
ztjB#ivFHPH#3w)I8DH=_x~GxGMVIh-H#n;if8=-KWdHn6iBF`OL^IgO6w3QaTzt?U
Z)F1f$w0`DL;?lRA4@(|Wf4koY{sQ+<_r?GK

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoRim_VS.hlsl b/src/extras/shaders/neoRim_VS.hlsl
new file mode 100644
index 00000000..7f95166d
--- /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/neoRim_VS.inc b/src/extras/shaders/neoRim_VS.inc
new file mode 100644
index 00000000..03b044a6
--- /dev/null
+++ b/src/extras/shaders/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/neoRim_gl3.inc b/src/extras/shaders/neoRim_gl3.inc
new file mode 100644
index 00000000..7e36e95a
--- /dev/null
+++ b/src/extras/shaders/neoRim_gl3.inc
@@ -0,0 +1,39 @@
+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"
+
+"layout(location = 0) in vec3 in_pos;\n"
+"layout(location = 1) in vec3 in_normal;\n"
+"layout(location = 2) in vec4 in_color;\n"
+"layout(location = 3) in vec2 in_tex0;\n"
+
+"out vec4 v_color;\n"
+"out vec2 v_tex0;\n"
+"out 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/neoVehicle.frag b/src/extras/shaders/neoVehicle.frag
new file mode 100644
index 00000000..96d4a632
--- /dev/null
+++ b/src/extras/shaders/neoVehicle.frag
@@ -0,0 +1,28 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+in vec4 v_color;
+in vec4 v_reflcolor;
+in vec2 v_tex0;
+in vec2 v_tex1;
+in float v_fog;
+
+out vec4 color;
+
+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;
+
+	color.rgb = pass1.rgb*pass1.a + pass2;
+	color.a = pass1.a;
+
+//	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
+	DoAlphaTest(color.a);
+}
diff --git a/src/extras/shaders/neoVehicle.vert b/src/extras/shaders/neoVehicle.vert
new file mode 100644
index 00000000..f2f54d6d
--- /dev/null
+++ b/src/extras/shaders/neoVehicle.vert
@@ -0,0 +1,54 @@
+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)
+
+layout(location = 0) in vec3 in_pos;
+layout(location = 1) in vec3 in_normal;
+layout(location = 2) in vec4 in_color;
+layout(location = 3) in vec2 in_tex0;
+
+out vec4 v_color;
+out vec4 v_reflcolor;
+out vec2 v_tex0;
+out vec2 v_tex1;
+out 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.0f, 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.cso b/src/extras/shaders/neoVehicle_PS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..ded01bfba7152da2fa509445354ab5c1d403864a
GIT binary patch
literal 476
zcmZuuF;2rk5S+Uh;sVJMH4-kNv0@`6ToN}Fp&=A>5h4jh3cAQ~K|x`93Ku*e1y$Vm
zQGN%<?1cmhR$lGQ?CkpNgSPg|ULd=@>>mNBnV{BT5e@}>vfMGXqZ(sYiGnFU#m=h;
zjCW>-io!lVoB~YEV`k<iM_t#-bv;?@qL{q&ME7vurvB*hfBIrMN=7|g&mMBKG}9tH
zxj7B52E#!(e7YY`@-Q>ci`kqYjgmA<&y(mZj$<5ej$Z=ZW{Y{29R`BxSKr|Zez5w{
zD@ELQpB$75ZKM3wafN_;+Xmzwz^5y`qUVjZ+~bUR$eq6RDA-E+q91ZjySa5E8gs>e
gp*4Ksbt5}uuZ9(WB|DVkGH=Dt%8T$f@39v62A{-Ow*UYD

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoVehicle_PS.hlsl b/src/extras/shaders/neoVehicle_PS.hlsl
new file mode 100644
index 00000000..fa030dd6
--- /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_PS.inc b/src/extras/shaders/neoVehicle_PS.inc
new file mode 100644
index 00000000..8b77cec2
--- /dev/null
+++ b/src/extras/shaders/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/neoVehicle_VS.cso b/src/extras/shaders/neoVehicle_VS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..6ea47987781830e57e2b921d98b1beed97b07841
GIT binary patch
literal 1896
zcmZuxzi-r75dQ4lWS1o1@S~tWI_rdvOCq8q{E(6YLV`|LEYcm^c1YY_vKuVzu2=Q~
zLZWbbiY@49jD#q>5=wC`66KZo6H>UjF5PwbzL{rx7lBvKd-J_-X1<wu4(u=4mu!sP
zKkn^Z0r1@BA7W>@hz$$yiMd>DI5h8IjeB{v6$7d~Y2?^r#g@qXfIo9=px7d@9lXl1
z*NUAZwpGS^qu6;|#xFR@d8djkkhf9t{!^@u3VuXY^zymHRa`7;xte1)x!PDJmXyAC
z6kA~>AJNEpj}<#h-tCgtQLM_^E|j&kdVhKC{;=U4JRM>@9$Qpkk&#Qqo=-x*z5k#!
z<Vl4mle6~AHhIFA*E}6B2#aW<dpcIfKe*o-w$PPiAwzfl;Cnd-e&`K7zmI({Xz%H6
z{FVvc^Zh$tKjrI;_GbaoxqpcDTEktxi@qQ9T3r#%nnbt!c&tzVS?|6VXvv|ROGhpJ
zne(9C>HZe@gAn1My*J?%xn}ZQ;j^G)crW~*t1WsSKG}G(h6mnW;D>%^=>Gh0$^EtQ
zsNp_(+S+di?ylb(cwKHbS2s3SH*amM-dJB>$M|<(W*$y#6l`w;(OvGh1>Z8b*yXcE
z+=6o|nx>y0q!>B_&PetZ?#a_Vxe@QX?&r9#a8HaBhy3QqLK;`)>Z_f`j57k#sK9KV
z1IbrdMX^jV_LsSx&*Ed^)|k6FI#b52MRo4Un~G73<`gq=B?e~^@mq~0h6Ra%$jM_E
zJB^mnjG3F7VcW_{g-73dG*923^wUXCDX7B9^DK)N^O#~wU*;KORW(~jV%J%_oQeL6
zZu*Y(A2F8t8@_5Hr>6er-IV>nk?1s7O5I5;{Sr;or#jg;_FZbJtB!ecuF=tGualRJ
zBoF6AUaI;zAGJ9X+DUBs=^Tr_z+M!+k-5!G^`^=_L(429(INesRV?`mt0|T#X0%D4
z)59g=SEqW)o=}ISx{A44n2msA@{#A56<_MiVjIVYCU51$!oSfL5X)=olQ~G=5u5r7
zJE5P+!}G(UUjF}3zWA7W#ct|ra#VAB+Q-Bcp5a)W0b>5YB)5oDXCFa4Aue~uQB87Z
zSciO56}z0(<c`?9L)5ABmpNU}?}+rPn$KJMCBEjn(OIf3zD3<~ZtNs>HJ<c0W4tVe
zxliRCTXE9@EAQAc`)q2P+%f6D>EX9`jCX+bnsb&Ifw|71`A+IIHfSfY%xGd~IwQ(E
Rp(biKy&~m=wU4q4_#bVzM_~W}

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoVehicle_VS.hlsl b/src/extras/shaders/neoVehicle_VS.hlsl
new file mode 100644
index 00000000..de75e745
--- /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.0f, 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/neoVehicle_VS.inc b/src/extras/shaders/neoVehicle_VS.inc
new file mode 100644
index 00000000..37c5858d
--- /dev/null
+++ b/src/extras/shaders/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/neoVehicle_fs_gl3.inc b/src/extras/shaders/neoVehicle_fs_gl3.inc
new file mode 100644
index 00000000..c75ba717
--- /dev/null
+++ b/src/extras/shaders/neoVehicle_fs_gl3.inc
@@ -0,0 +1,30 @@
+const char *neoVehicle_frag_src =
+"uniform sampler2D tex0;\n"
+"uniform sampler2D tex1;\n"
+
+"in vec4 v_color;\n"
+"in vec4 v_reflcolor;\n"
+"in vec2 v_tex0;\n"
+"in vec2 v_tex1;\n"
+"in float v_fog;\n"
+
+"out vec4 color;\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"
+
+"	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"
+"}\n"
+;
diff --git a/src/extras/shaders/neoVehicle_vs_gl3.inc b/src/extras/shaders/neoVehicle_vs_gl3.inc
new file mode 100644
index 00000000..268180e1
--- /dev/null
+++ b/src/extras/shaders/neoVehicle_vs_gl3.inc
@@ -0,0 +1,56 @@
+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"
+
+"layout(location = 0) in vec3 in_pos;\n"
+"layout(location = 1) in vec3 in_normal;\n"
+"layout(location = 2) in vec4 in_color;\n"
+"layout(location = 3) in vec2 in_tex0;\n"
+
+"out vec4 v_color;\n"
+"out vec4 v_reflcolor;\n"
+"out vec2 v_tex0;\n"
+"out vec2 v_tex1;\n"
+"out 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.0f, 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/neoWorldIII.frag b/src/extras/shaders/neoWorldIII.frag
new file mode 100644
index 00000000..4c5571ee
--- /dev/null
+++ b/src/extras/shaders/neoWorldIII.frag
@@ -0,0 +1,25 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+uniform vec4 u_lightMap;
+
+in vec4 v_color;
+in vec2 v_tex0;
+in vec2 v_tex1;
+in float v_fog;
+
+out vec4 color;
+
+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));
+
+	color = t0*v_color*(1 + u_lightMap*(2*t1-1));
+	color.a = v_color.a*t0.a*u_lightMap.a;
+
+	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
+	DoAlphaTest(color.a);
+}
+
diff --git a/src/extras/shaders/neoWorldIII_PS.cso b/src/extras/shaders/neoWorldIII_PS.cso
new file mode 100644
index 0000000000000000000000000000000000000000..817888ef460d38d2ce15f62cf9ccacb0a6255d51
GIT binary patch
literal 528
zcmZ8dyH3ME5S+U>#08Rn0121SL^z1T4UdF^A`}p)BLs0`$&sVj^e!kV`~eiELviCW
z5(N_9;_txBogF}&ytg~EyR*K9tg5f-7=xR${tkdo8ps)R3PS+zbOEiUp+rot&J(T0
z0)Z_&YX*3smo&lqt>M<1tLrx0x$k+8(F6vzwF1ZJGxVpk7<vAm_rmEQ48kQ-xzJM$
z_SGktV-bye;{#m!_scK}z1ZHn-nTD?qoF;T-91c}_8^=u{D6Vm?YUj|sMkGo90&Oo
z(3Y?=DxCt!2kk_u?$*yp04x2CJH;mEKWyR@rj<2(Dj4_ms;oqyEQDEVEMQTqEI`cN
z^6My^a95ecZN|4SG>KO~dG=?j2B?xYH3fUf5tpgLs$MY1yvCAP)sp+Gq3V^rs<~3n
RT@?*i&owR1Et9GOz5zfMU(5gi

literal 0
HcmV?d00001

diff --git a/src/extras/shaders/neoWorldIII_PS.hlsl b/src/extras/shaders/neoWorldIII_PS.hlsl
new file mode 100644
index 00000000..8a3d5d86
--- /dev/null
+++ b/src/extras/shaders/neoWorldIII_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*(2*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/neoWorldIII_PS.inc b/src/extras/shaders/neoWorldIII_PS.inc
new file mode 100644
index 00000000..a4631efb
--- /dev/null
+++ b/src/extras/shaders/neoWorldIII_PS.inc
@@ -0,0 +1,46 @@
+static unsigned char neoWorldIII_PS_cso[] = {
+  0x00, 0x02, 0xff, 0xff, 0xfe, 0xff, 0x3e, 0x00, 0x43, 0x54, 0x41, 0x42,
+  0x1c, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff,
+  0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+  0xbb, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x84, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00,
+  0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa8, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
+  0x01, 0x00, 0x06, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x44, 0x69, 0x66, 0x66, 0x75, 0x73, 0x65, 0x00, 0x04, 0x00, 0x0c, 0x00,
+  0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x4c, 0x69, 0x67, 0x68, 0x74, 0x00, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00,
+  0x01, 0x00, 0x01, 0x00, 0x01, 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, 0x6c, 0x6d, 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, 0xab, 0x51, 0x00, 0x00, 0x05, 0x02, 0x00, 0x0f, 0xa0,
+  0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x80, 0x3f,
+  0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+  0x00, 0x00, 0x0f, 0x90, 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, 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, 0x80,
+  0x02, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x55, 0xa0, 0x01, 0x00, 0x00, 0x02,
+  0x00, 0x00, 0x08, 0x80, 0x02, 0x00, 0xaa, 0xa0, 0x04, 0x00, 0x00, 0x04,
+  0x00, 0x00, 0x07, 0x80, 0x01, 0x00, 0xe4, 0xa0, 0x00, 0x00, 0xe4, 0x80,
+  0x00, 0x00, 0xff, 0x80, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, 0x07, 0x80,
+  0x01, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0x90, 0x05, 0x00, 0x00, 0x03,
+  0x00, 0x00, 0x08, 0x80, 0x01, 0x00, 0xff, 0x80, 0x00, 0x00, 0xff, 0x90,
+  0x05, 0x00, 0x00, 0x03, 0x02, 0x00, 0x08, 0x80, 0x00, 0x00, 0xff, 0x80,
+  0x01, 0x00, 0xff, 0xa0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x80,
+  0x01, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0xa1,
+  0x04, 0x00, 0x00, 0x04, 0x02, 0x00, 0x07, 0x80, 0x00, 0x00, 0xaa, 0xb0,
+  0x00, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x02,
+  0x00, 0x08, 0x0f, 0x80, 0x02, 0x00, 0xe4, 0x80, 0xff, 0xff, 0x00, 0x00
+};
diff --git a/src/extras/shaders/neoWorldIII_fs_gl3.inc b/src/extras/shaders/neoWorldIII_fs_gl3.inc
new file mode 100644
index 00000000..5145f9cd
--- /dev/null
+++ b/src/extras/shaders/neoWorldIII_fs_gl3.inc
@@ -0,0 +1,27 @@
+const char *neoWorldIII_frag_src =
+"uniform sampler2D tex0;\n"
+"uniform sampler2D tex1;\n"
+
+"uniform vec4 u_lightMap;\n"
+
+"in vec4 v_color;\n"
+"in vec2 v_tex0;\n"
+"in vec2 v_tex1;\n"
+"in float v_fog;\n"
+
+"out vec4 color;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+"	vec4 t0 = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+"	vec4 t1 = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));\n"
+
+"	color = t0*v_color*(1 + u_lightMap*(2*t1-1));\n"
+"	color.a = v_color.a*t0.a*u_lightMap.a;\n"
+
+"	color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
+"	DoAlphaTest(color.a);\n"
+"}\n"
+
+;
diff --git a/src/extras/shaders/simple.frag b/src/extras/shaders/simple.frag
new file mode 100644
index 00000000..87157beb
--- /dev/null
+++ b/src/extras/shaders/simple.frag
@@ -0,0 +1,16 @@
+uniform sampler2D tex0;
+
+in vec4 v_color;
+in vec2 v_tex0;
+in float v_fog;
+
+out vec4 color;
+
+void
+main(void)
+{
+	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);
+}
+
diff --git a/src/extras/shaders/simple_fs_gl3.inc b/src/extras/shaders/simple_fs_gl3.inc
new file mode 100644
index 00000000..47d89971
--- /dev/null
+++ b/src/extras/shaders/simple_fs_gl3.inc
@@ -0,0 +1,18 @@
+const char *simple_frag_src =
+"uniform sampler2D tex0;\n"
+
+"in vec4 v_color;\n"
+"in vec2 v_tex0;\n"
+"in float v_fog;\n"
+
+"out vec4 color;\n"
+
+"void\n"
+"main(void)\n"
+"{\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"
+"}\n"
+
+;
diff --git a/src/extras/shaders/standardConstants.h b/src/extras/shaders/standardConstants.h
new file mode 100644
index 00000000..088df7dd
--- /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)
diff --git a/src/modelinfo/PedModelInfo.cpp b/src/modelinfo/PedModelInfo.cpp
index 60c63114..117fd378 100644
--- a/src/modelinfo/PedModelInfo.cpp
+++ b/src/modelinfo/PedModelInfo.cpp
@@ -8,6 +8,7 @@
 #include "NodeName.h"
 #include "VisibilityPlugins.h"
 #include "ModelInfo.h"
+#include "custompipes.h"
 
 void
 CPedModelInfo::DeleteRwObject(void)
@@ -91,8 +92,10 @@ CPedModelInfo::findLimbsCb(RpAtomic *atomic, void *data)
 void
 CPedModelInfo::SetClump(RpClump *clump)
 {
+#ifdef EXTENDED_PIPELINES
+	CustomPipes::AttachRimPipe(clump);
+#endif
 #ifdef PED_SKIN
-
 	// CB has to be set here before atomics are detached from clump
 	if(strncmp(GetName(), "player", 7) == 0)
 		RpClumpForAllAtomics(clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB);
diff --git a/src/modelinfo/SimpleModelInfo.cpp b/src/modelinfo/SimpleModelInfo.cpp
index 32204500..416bdad5 100644
--- a/src/modelinfo/SimpleModelInfo.cpp
+++ b/src/modelinfo/SimpleModelInfo.cpp
@@ -3,6 +3,7 @@
 #include "General.h"
 #include "Camera.h"
 #include "ModelInfo.h"
+#include "custompipes.h"
 
 #define LOD_DISTANCE (300.0f)
 
@@ -75,6 +76,10 @@ CSimpleModelInfo::SetAtomic(int n, RpAtomic *atomic)
 		RpGeometry *geo = RpAtomicGetGeometry(atomic);
 		RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) & ~rpGEOMETRYLIGHT);
 	}
+
+#ifdef EXTENDED_PIPELINES
+	CustomPipes::AttachWorldPipe(atomic);
+#endif
 }
 
 void
diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp
index d8b388d5..a024bb40 100644
--- a/src/modelinfo/VehicleModelInfo.cpp
+++ b/src/modelinfo/VehicleModelInfo.cpp
@@ -19,6 +19,7 @@
 #include "Bike.h"
 #include "ModelIndices.h"
 #include "ModelInfo.h"
+#include "custompipes.h"
 
 int8 CVehicleModelInfo::ms_compsToUse[2] = { -2, -2 };
 int8 CVehicleModelInfo::ms_compsUsed[2];
@@ -1037,6 +1038,10 @@ CVehicleModelInfo::SetEnvironmentMap(void)
 				SetEnvironmentMapCB(wheelmi->m_atomics[i], m_envMap);
 		}
 	}
+
+#ifdef EXTENDED_PIPELINES
+	CustomPipes::AttachVehiclePipe(m_clump);
+#endif
 }
 
 void
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 7ae29a2f..76d8af2b 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -19,6 +19,7 @@
 #include "Shadows.h"
 #include "PointLights.h"
 #include "Renderer.h"
+#include "custompipes.h"
 
 bool gbShowPedRoadGroups;
 bool gbShowCarRoadGroups;
@@ -96,8 +97,12 @@ CRenderer::RenderOneRoad(CEntity *e)
 		return;
 	if(gbShowCollisionPolys)
 		CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(), e->GetModelIndex());
-	else
+	else{
+#ifdef EXTENDED_PIPELINES
+		CustomPipes::AttachGlossPipe(e->GetAtomic());
+#endif
 		e->Render();
+	}
 }
 
 void
@@ -232,6 +237,11 @@ CRenderer::RenderEverythingBarRoads(void)
 		if(e->IsBuilding() && ((CBuilding*)e)->GetIsATreadable())
 			continue;
 
+#ifdef EXTENDED_PIPELINES
+		if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle()))
+			continue;
+#endif
+
 		if(e->IsVehicle() ||
 		   e->IsPed() && CVisibilityPlugins::GetClumpAlpha((RpClump*)e->m_rwObject) != 255){
 			if(e->IsVehicle() && ((CVehicle*)e)->IsBoat()){
diff --git a/src/rw/Lights.h b/src/rw/Lights.h
index 6fdd51de..b296816b 100644
--- a/src/rw/Lights.h
+++ b/src/rw/Lights.h
@@ -1,5 +1,11 @@
 #pragma once
 
+extern RpLight *pAmbient;
+extern RpLight *pDirect;
+extern RpLight *pExtraDirectionals[4];
+extern int LightStrengths[4];
+extern int NumExtraDirLightsInWorld;
+
 void SetLightsWithTimeOfDayColour(RpWorld *);
 RpWorld *LightsCreate(RpWorld *world);
 void LightsDestroy(RpWorld *world);
diff --git a/src/rw/VisibilityPlugins.cpp b/src/rw/VisibilityPlugins.cpp
index f6a9c3b5..b2e252a0 100644
--- a/src/rw/VisibilityPlugins.cpp
+++ b/src/rw/VisibilityPlugins.cpp
@@ -8,6 +8,7 @@
 #include "Camera.h"
 #include "VisibilityPlugins.h"
 #include "World.h"
+#include "custompipes.h"
 
 #define FADE_DISTANCE 20.0f
 
@@ -146,6 +147,10 @@ CVisibilityPlugins::RenderFadingEntities(void)
 		CEntity *e = node->item.entity;
 		if(e->m_rwObject == nil)
 			continue;
+#ifdef EXTENDED_PIPELINES
+		if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle()))
+			continue;
+#endif
 		mi = (CSimpleModelInfo *)CModelInfo::GetModelInfo(e->GetModelIndex());
 #ifdef FIX_BUGS
 		if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite)