summaryrefslogtreecommitdiffstats
path: root/src/extras
diff options
context:
space:
mode:
authoraap <aap@papnet.eu>2020-11-19 19:07:32 +0100
committeraap <aap@papnet.eu>2020-11-19 19:07:32 +0100
commit07fe099b4e1fd877176f2b2947841ee82aa91ed0 (patch)
treeb76756d54baef5a3ee506875c866cd8a5947242d /src/extras
parentMerge branch 'miami' of github.com:GTAmodding/re3 into miami (diff)
downloadre3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.tar
re3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.tar.gz
re3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.tar.bz2
re3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.tar.lz
re3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.tar.xz
re3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.tar.zst
re3-07fe099b4e1fd877176f2b2947841ee82aa91ed0.zip
Diffstat (limited to 'src/extras')
-rw-r--r--src/extras/custompipes.cpp2
-rw-r--r--src/extras/custompipes.h1
-rw-r--r--src/extras/custompipes_d3d9.cpp2
-rw-r--r--src/extras/custompipes_gl.cpp2
-rw-r--r--src/extras/postfx.cpp15
-rw-r--r--src/extras/postfx.h1
-rw-r--r--src/extras/screendroplets.cpp791
-rw-r--r--src/extras/screendroplets.h78
-rw-r--r--src/extras/shaders/Makefile10
-rw-r--r--src/extras/shaders/im2d_UV2.vert21
-rw-r--r--src/extras/shaders/screenDroplet.frag18
-rw-r--r--src/extras/shaders/screenDroplet_PS.csobin0 -> 324 bytes
-rw-r--r--src/extras/shaders/screenDroplet_PS.hlsl17
-rw-r--r--src/extras/shaders/screenDroplet_PS.inc29
14 files changed, 981 insertions, 6 deletions
diff --git a/src/extras/custompipes.cpp b/src/extras/custompipes.cpp
index 2ae87950..d6c8e065 100644
--- a/src/extras/custompipes.cpp
+++ b/src/extras/custompipes.cpp
@@ -44,7 +44,7 @@ CustomMatCopy(void *dst, void *src, int32, int32)
-static rw::TexDictionary *neoTxd;
+rw::TexDictionary *neoTxd;
bool bRenderingEnvMap;
int32 EnvMapSize = 128;
diff --git a/src/extras/custompipes.h b/src/extras/custompipes.h
index 6e9c6517..ca3f0fb4 100644
--- a/src/extras/custompipes.h
+++ b/src/extras/custompipes.h
@@ -6,6 +6,7 @@
namespace CustomPipes {
+extern rw::TexDictionary *neoTxd;
struct CustomMatExt
{
diff --git a/src/extras/custompipes_d3d9.cpp b/src/extras/custompipes_d3d9.cpp
index 06ce1461..01de3f78 100644
--- a/src/extras/custompipes_d3d9.cpp
+++ b/src/extras/custompipes_d3d9.cpp
@@ -132,6 +132,8 @@ vehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header)
inst++;
}
+ d3d::setTexture(1, nil);
+
SetRenderState(SRCBLEND, BLENDSRCALPHA);
}
diff --git a/src/extras/custompipes_gl.cpp b/src/extras/custompipes_gl.cpp
index 475680aa..4cd6607b 100644
--- a/src/extras/custompipes_gl.cpp
+++ b/src/extras/custompipes_gl.cpp
@@ -133,6 +133,8 @@ vehicleRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header)
inst++;
}
+ setTexture(1, nil);
+
SetRenderState(SRCBLEND, BLENDSRCALPHA);
#ifndef RW_GL_USE_VAOS
diff --git a/src/extras/postfx.cpp b/src/extras/postfx.cpp
index f7b120a8..2ea08141 100644
--- a/src/extras/postfx.cpp
+++ b/src/extras/postfx.cpp
@@ -376,6 +376,14 @@ CPostFX::NeedFrontBuffer(int32 type)
}
void
+CPostFX::GetBackBuffer(RwCamera *cam)
+{
+ RwRasterPushContext(pBackBuffer);
+ RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
+ RwRasterPopContext();
+}
+
+void
CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha)
{
if(pFrontBuffer == nil)
@@ -391,11 +399,8 @@ CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blu
blur = AvgAlpha;
}
- if(NeedBackBuffer()){
- RwRasterPushContext(pBackBuffer);
- RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
- RwRasterPopContext();
- }
+ if(NeedBackBuffer())
+ GetBackBuffer(cam);
DefinedState();
diff --git a/src/extras/postfx.h b/src/extras/postfx.h
index ace2e4a8..db702bf3 100644
--- a/src/extras/postfx.h
+++ b/src/extras/postfx.h
@@ -39,6 +39,7 @@ public:
static void SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha);
static bool NeedBackBuffer(void);
static bool NeedFrontBuffer(int32 type);
+ static void GetBackBuffer(RwCamera *cam);
static bool UseBlurColours(void) { return EffectSwitch != POSTFX_SIMPLE; }
};
diff --git a/src/extras/screendroplets.cpp b/src/extras/screendroplets.cpp
new file mode 100644
index 00000000..2d34cdcb
--- /dev/null
+++ b/src/extras/screendroplets.cpp
@@ -0,0 +1,791 @@
+#define WITH_D3D
+#include "common.h"
+
+#ifdef SCREEN_DROPLETS
+
+#ifndef LIBRW
+#error "Need librw for SCREEN_DROPLETS"
+#endif
+
+#include "General.h"
+#include "Main.h"
+#include "RwHelper.h"
+#include "Main.h"
+#include "Timer.h"
+#include "Camera.h"
+#include "ZoneCull.h"
+#include "Weather.h"
+#include "ParticleObject.h"
+ #include "Pad.h"
+#include "RenderBuffer.h"
+#include "custompipes.h"
+#include "postfx.h"
+#include "screendroplets.h"
+
+// for 640
+#define MAXSIZE 15
+#define MINSIZE 4
+
+int ScreenDroplets::ms_initialised;
+RwTexture *ScreenDroplets::ms_maskTex;
+RwTexture *ScreenDroplets::ms_screenTex;
+
+bool ScreenDroplets::ms_enabled = true;
+bool ScreenDroplets::ms_movingEnabled = true;
+
+ScreenDroplets::ScreenDrop ScreenDroplets::ms_drops[ScreenDroplets::MAXDROPS];
+int ScreenDroplets::ms_numDrops;
+ScreenDroplets::ScreenDropMoving ScreenDroplets::ms_dropsMoving[ScreenDroplets::MAXDROPSMOVING];
+int ScreenDroplets::ms_numDropsMoving;
+
+CVector ScreenDroplets::ms_prevCamUp;
+CVector ScreenDroplets::ms_prevCamPos;
+CVector ScreenDroplets::ms_camMoveDelta;
+float ScreenDroplets::ms_camMoveDist;
+CVector ScreenDroplets::ms_screenMoveDelta;
+float ScreenDroplets::ms_screenMoveDist;
+float ScreenDroplets::ms_camUpAngle;
+
+int ScreenDroplets::ms_splashDuration;
+CParticleObject *ScreenDroplets::ms_splashObject;
+
+struct Im2DVertexUV2 : rw::RWDEVICE::Im2DVertex
+{
+ rw::float32 u2, v2;
+};
+
+#ifdef RW_D3D9
+static void *screenDroplet_PS;
+#endif
+#ifdef RW_GL3
+static rw::gl3::Shader *screenDroplet;
+#endif
+
+// platform specific
+static void openim2d_uv2(void);
+static void closeim2d_uv2(void);
+static void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices);
+
+static Im2DVertexUV2 VertexBuffer[TEMPBUFFERVERTSIZE];
+
+void
+ScreenDroplets::Initialise(void)
+{
+ Clear();
+ ms_splashDuration = -1;
+ ms_splashObject = nil;
+}
+
+void
+ScreenDroplets::InitDraw(void)
+{
+ if(CustomPipes::neoTxd)
+ ms_maskTex = CustomPipes::neoTxd->find("dropmask");
+
+ ms_screenTex = RwTextureCreate(nil);
+ RwTextureSetFilterMode(ms_screenTex, rwFILTERLINEAR);
+
+ openim2d_uv2();
+#ifdef RW_D3D9
+#include "shaders/screenDroplet_PS.inc"
+ screenDroplet_PS = rw::d3d::createPixelShader(screenDroplet_PS_cso);
+#endif
+#ifdef RW_GL3
+ using namespace rw::gl3;
+ {
+#include "shaders/im2d_UV2_gl.inc"
+#include "shaders/screenDroplet_fs_gl.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, im2d_UV2_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, screenDroplet_frag_src, nil };
+ screenDroplet = Shader::create(vs, fs);
+ assert(screenDroplet);
+ }
+#endif
+
+ ms_initialised = 1;
+}
+
+void
+ScreenDroplets::Shutdown(void)
+{
+ if(ms_maskTex){
+ RwTextureDestroy(ms_maskTex);
+ ms_maskTex = nil;
+ }
+ if(ms_screenTex){
+ RwTextureSetRaster(ms_screenTex, nil);
+ RwTextureDestroy(ms_screenTex);
+ ms_screenTex = nil;
+ }
+#ifdef RW_D3D9
+ if(screenDroplet_PS){
+ rw::d3d::destroyPixelShader(screenDroplet_PS);
+ screenDroplet_PS = nil;
+ }
+#endif
+#ifdef RW_GL3
+ if(screenDroplet){
+ screenDroplet->destroy();
+ screenDroplet = nil;
+ }
+#endif
+
+ closeim2d_uv2();
+}
+
+void
+ScreenDroplets::Process(void)
+{
+ ProcessCameraMovement();
+ SprayDrops();
+ ProcessMoving();
+ Fade();
+}
+
+static void
+FlushBuffer(void)
+{
+ if(TempBufferIndicesStored){
+ RenderIndexedPrimitive_UV2(rwPRIMTYPETRILIST,
+ VertexBuffer, TempBufferVerticesStored,
+ TempBufferRenderIndexList, TempBufferIndicesStored);
+ TempBufferVerticesStored = 0;
+ TempBufferIndicesStored = 0;
+ }
+}
+
+static int
+StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, Im2DVertexUV2 **vertexStart)
+{
+ if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE ||
+ TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE)
+ FlushBuffer();
+ *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored];
+ *vertexStart = &VertexBuffer[TempBufferVerticesStored];
+ int vertOffset = TempBufferVerticesStored;
+ TempBufferIndicesStored += numIndices;
+ TempBufferVerticesStored += numVertices;
+ return vertOffset;
+}
+
+void
+ScreenDroplets::Render(void)
+{
+ ScreenDrop *drop;
+
+ DefinedState();
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(ms_maskTex));
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+
+ RwTextureSetRaster(ms_screenTex, CPostFX::pBackBuffer);
+#ifdef RW_D3D9
+ rw::d3d::im2dOverridePS = screenDroplet_PS;
+ rw::d3d::setTexture(1, ms_screenTex);
+#endif
+#ifdef RW_GL3
+ rw::gl3::im2dOverrideShader = screenDroplet;
+ rw::gl3::setTexture(1, ms_screenTex);
+#endif
+
+ RenderBuffer::ClearRenderBuffer();
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
+ if(drop->active)
+ AddToRenderList(drop);
+ FlushBuffer();
+
+#ifdef RW_D3D9
+ rw::d3d::im2dOverridePS = nil;
+ rw::d3d::setTexture(1, nil);
+#endif
+#ifdef RW_GL3
+ rw::gl3::im2dOverrideShader = nil;
+ rw::gl3::setTexture(1, nil);
+#endif
+
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, FALSE);
+}
+
+void
+ScreenDroplets::AddToRenderList(ScreenDroplets::ScreenDrop *drop)
+{
+ static float xy[] = {
+ -1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, -1.0f
+ };
+ static float uv[] = {
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f
+ };
+
+ int i;
+ RwImVertexIndex *indices;
+ Im2DVertexUV2 *verts;
+ int first = StartStoring(6, 4, &indices, &verts);
+
+ float scale = 0.5f*SCREEN_SCALE_X(drop->size);
+
+ float screenz = RwIm2DGetNearScreenZ();
+ float z = RwCameraGetNearClipPlane(Scene.camera);
+ float recipz = 1.0f/z;
+
+ float magSize = SCREEN_SCALE_Y(drop->magnification*(300.0f-40.0f) + 40.0f);
+ float ul = drop->x - magSize;
+ float vt = drop->y - magSize;
+ float ur = drop->x + magSize;
+ float vb = drop->y + magSize;
+ ul = Max(ul, 0.0f)/RwRasterGetWidth(CPostFX::pBackBuffer);
+ vt = Max(vt, 0.0f)/RwRasterGetHeight(CPostFX::pBackBuffer);
+ ur = Min(ur, SCREEN_WIDTH)/RwRasterGetWidth(CPostFX::pBackBuffer);
+ vb = Min(vb, SCREEN_HEIGHT)/RwRasterGetHeight(CPostFX::pBackBuffer);
+
+ for(i = 0; i < 4; i++){
+ RwIm2DVertexSetScreenX(&verts[i], drop->x + xy[i*2]*scale);
+ RwIm2DVertexSetScreenY(&verts[i], drop->y + xy[i*2+1]*scale);
+ RwIm2DVertexSetScreenZ(&verts[i], screenz);
+ RwIm2DVertexSetCameraZ(&verts[i], z);
+ RwIm2DVertexSetRecipCameraZ(&verts[i], recipz);
+ RwIm2DVertexSetIntRGBA(&verts[i], drop->color.r, drop->color.g, drop->color.b, drop->color.a);
+ RwIm2DVertexSetU(&verts[i], uv[i*2], recipz);
+ RwIm2DVertexSetV(&verts[i], uv[i*2+1], recipz);
+
+ verts[i].u2 = i < 2 ? ul : ur;
+ verts[i].v2 = i % 3 ? vt : vb;
+ }
+ indices[0] = first + 0;
+ indices[1] = first + 1;
+ indices[2] = first + 2;
+ indices[3] = first + 2;
+ indices[4] = first + 3;
+ indices[5] = first + 0;
+}
+
+void
+ScreenDroplets::Clear(void)
+{
+ ScreenDrop *drop;
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
+ drop->active = false;
+ ms_numDrops = 0;
+}
+
+ScreenDroplets::ScreenDrop*
+ScreenDroplets::NewDrop(float x, float y, float size, float lifetime, bool fades, int r, int g, int b)
+{
+ ScreenDrop *drop;
+ int i;
+
+ for(i = 0, drop = ms_drops; i < MAXDROPS; i++, drop++)
+ if(!ms_drops[i].active)
+ goto found;
+ return nil;
+found:
+ ms_numDrops++;
+ drop->x = x;
+ drop->y = y;
+ drop->size = size;
+ drop->magnification = (MAXSIZE - size + 1.0f) / (MAXSIZE - MINSIZE + 1.0f);
+ drop->fades = fades;
+ drop->active = true;
+ drop->color.r = r;
+ drop->color.g = g;
+ drop->color.b = b;
+ drop->color.a = 255;
+ drop->time = 0.0f;
+ drop->lifetime = lifetime;
+ return drop;
+}
+
+void
+ScreenDroplets::SetMoving(ScreenDroplets::ScreenDrop *drop)
+{
+ ScreenDropMoving *moving;
+ for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++)
+ if(moving->drop == nil)
+ goto found;
+ return;
+found:
+ ms_numDropsMoving++;
+ moving->drop = drop;
+ moving->dist = 0.0f;
+}
+
+void
+ScreenDroplets::FillScreen(int n)
+{
+ float x, y, size;
+ ScreenDrop *drop;
+
+ if(!ms_initialised)
+ return;
+ ms_numDrops = 0;
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++){
+ drop->active = false;
+ if(drop < &ms_drops[n]){
+ x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
+ y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
+ size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
+ NewDrop(x, y, size, 2000.0f, true);
+ }
+ }
+}
+
+void
+ScreenDroplets::FillScreenMoving(float amount, bool isBlood)
+{
+ int n = (ms_screenMoveDelta.z > 5.0f ? 1.5f : 1.0f)*amount*20.0f;
+ float x, y, size;
+ ScreenDrop *drop;
+
+ while(n--)
+ if(ms_numDrops < MAXDROPS && ms_numDropsMoving < MAXDROPSMOVING){
+ x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
+ y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
+ size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
+ drop = nil;
+ if(isBlood)
+ drop = NewDrop(x, y, size, 2000.0f, true, 255, 0, 0);
+ else
+ drop = NewDrop(x, y, size, 2000.0f, true);
+ if(drop)
+ SetMoving(drop);
+ }
+}
+
+void
+ScreenDroplets::RegisterSplash(CParticleObject *pobj)
+{
+ CVector dist = pobj->GetPosition() - ms_prevCamPos;
+ if(dist.MagnitudeSqr() < 20.0f){
+ ms_splashDuration = 14;
+ ms_splashObject = pobj;
+ }
+}
+
+void
+ScreenDroplets::ProcessCameraMovement(void)
+{
+ RwMatrix *camMat = RwFrameGetMatrix(RwCameraGetFrame(Scene.camera));
+ CVector camPos = camMat->pos;
+ CVector camUp = camMat->at;
+ ms_camMoveDelta = camPos - ms_prevCamPos;
+ ms_camMoveDist = ms_camMoveDelta.Magnitude();
+
+ ms_prevCamUp = camUp;
+ ms_prevCamPos = camPos;
+
+ ms_screenMoveDelta.x = -RwV3dDotProduct(&camMat->right, (RwV3d*)&ms_camMoveDelta);
+ ms_screenMoveDelta.y = RwV3dDotProduct(&camMat->up, (RwV3d*)&ms_camMoveDelta);
+ ms_screenMoveDelta.z = RwV3dDotProduct(&camMat->at, (RwV3d*)&ms_camMoveDelta);
+ ms_screenMoveDelta *= 10.0f;
+ ms_screenMoveDist = ms_screenMoveDelta.Magnitude2D();
+
+ uint16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode;
+ bool isTopDown = mode == CCam::MODE_TOPDOWN || mode == CCam::MODE_GTACLASSIC || mode == CCam::MODE_TOP_DOWN_PED;
+ bool isLookingInDirection = CPad::GetPad(0)->GetLookBehindForCar() || CPad::GetPad(0)->GetLookLeft() || CPad::GetPad(0)->GetLookRight();
+ ms_enabled = !isTopDown && !isLookingInDirection;
+ ms_movingEnabled = !isTopDown && !isLookingInDirection;
+
+ // 0 when looking stright up, 180 when looking up or down
+ ms_camUpAngle = RADTODEG(Acos(clamp(camUp.z, -1.0f, 1.0f)));
+}
+
+void
+ScreenDroplets::SprayDrops(void)
+{
+ bool noRain = CCullZones::PlayerNoRain() || CCullZones::CamNoRain();
+ if(!noRain && CWeather::Rain > 0.0f && ms_enabled){
+ // 180 when looking stright up, 0 when looking up or down
+ float angle = 180.0f - ms_camUpAngle;
+ angle = Max(angle, 40.0f); // want at least some rain
+ FillScreenMoving((angle - 40.0f) / 150.0f * CWeather::Rain * 0.5f);
+ }
+
+ int i;
+ for(i = 0; i < MAX_AUDIOHYDRANTS; i++){
+ CAudioHydrant *hyd = CAudioHydrant::Get(i);
+ if (hyd->pParticleObject){
+ CVector dist = hyd->pParticleObject->GetPosition() - ms_prevCamPos;
+ if(dist.MagnitudeSqr() > 40.0f ||
+ DotProduct(dist, ms_prevCamUp) < 0.0f) continue;
+
+ FillScreenMoving(1.0f);
+ }
+ }
+
+ static int ndrops[] = {
+ 125, 250, 500, 1000, 1000,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if(ms_splashDuration >= 0){
+ if(ms_numDrops < MAXDROPS) {
+ float numDropMult = 1.0f;
+ if(ms_splashObject){
+ float dist = (ms_splashObject->GetPosition() - ms_prevCamPos).Magnitude();
+ numDropMult = 1.0f - (dist - 5.0f)/15.0f;
+ if(numDropMult < 0) numDropMult = 0.0f; // fix
+ }
+ int n = ndrops[ms_splashDuration] * numDropMult;
+ while(n--)
+ if(ms_numDrops < MAXDROPS){
+ float x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH;
+ float y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT;
+ float size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE);
+ NewDrop(x, y, size, 10000.0f, false);
+ }
+ }
+ ms_splashDuration--;
+ }
+}
+
+void
+ScreenDroplets::NewTrace(ScreenDroplets::ScreenDropMoving *moving)
+{
+ if(ms_numDrops < MAXDROPS){
+ moving->dist = 0.0f;
+ NewDrop(moving->drop->x, moving->drop->y, MINSIZE, 500.0f, true,
+ moving->drop->color.r, moving->drop->color.g, moving->drop->color.b);
+ }
+}
+
+void
+ScreenDroplets::MoveDrop(ScreenDroplets::ScreenDropMoving *moving)
+{
+ ScreenDrop *drop = moving->drop;
+ if(!ms_movingEnabled)
+ return;
+ if(!drop->active){
+ moving->drop = nil;
+ ms_numDropsMoving--;
+ return;
+ }
+ if(ms_screenMoveDelta.z > 0.0f && ms_camMoveDist > 0.3f){
+ if(ms_screenMoveDist > 0.5f && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){
+ // movement when camera turns
+ moving->dist += ms_screenMoveDist;
+ if(moving->dist > 20.0f && drop->color.a > 100)
+ NewTrace(moving);
+
+ drop->x -= ms_screenMoveDelta.x;
+ drop->y += ms_screenMoveDelta.y;
+ }else{
+ // movement out of center
+ float d = ms_screenMoveDelta.z*0.2f;
+ float dx, dy, sum;
+ dx = drop->x - SCREEN_WIDTH*0.5f + ms_screenMoveDelta.x;
+ if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON)
+ dy = drop->y - SCREEN_HEIGHT*1.2f - ms_screenMoveDelta.y;
+ else
+ dy = drop->y - SCREEN_HEIGHT*0.5f - ms_screenMoveDelta.y;
+ sum = fabs(dx) + fabs(dy);
+ if(sum > 0.001f){
+ dx /= sum;
+ dy /= sum;
+ }
+ moving->dist += d;
+ if(moving->dist > 20.0f && drop->color.a > 100)
+ NewTrace(moving);
+ drop->x += dx * d;
+ drop->y += dy * d;
+ }
+
+ if(drop->x < 0.0f || drop->y < 0.0f ||
+ drop->x > SCREEN_WIDTH || drop->y > SCREEN_HEIGHT){
+ moving->drop = nil;
+ ms_numDropsMoving--;
+ }
+ }
+}
+
+void
+ScreenDroplets::ProcessMoving(void)
+{
+ ScreenDropMoving *moving;
+ if(!ms_movingEnabled)
+ return;
+ for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++)
+ if(moving->drop)
+ MoveDrop(moving);
+}
+
+void
+ScreenDroplets::Fade(void)
+{
+ ScreenDrop *drop;
+ for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++)
+ if(drop->active)
+ drop->Fade();
+}
+
+void
+ScreenDroplets::ScreenDrop::Fade(void)
+{
+ int delta = CTimer::GetTimeStepInMilliseconds();
+ time += delta;
+ if(time < lifetime){
+ color.a = 255 - time/lifetime*255;
+ }else if(fades){
+ ScreenDroplets::ms_numDrops--;
+ active = false;
+ }
+}
+
+
+/*
+ * Im2D with two uv coors
+ */
+
+#ifdef RW_D3D9
+// stolen from RW, not in a public header
+namespace rw {
+namespace d3d {
+void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf); // NB: don't share this pointer
+void removeDynamicVB(IDirect3DVertexBuffer9 **buf);
+void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf); // NB: don't share this pointer
+void removeDynamicIB(IDirect3DIndexBuffer9 **buf);
+}
+}
+// different than im2d
+#define NUMINDICES 1024
+#define NUMVERTICES 1024
+
+static int primTypeMap[] = {
+ D3DPT_POINTLIST, // invalid
+ D3DPT_LINELIST,
+ D3DPT_LINESTRIP,
+ D3DPT_TRIANGLELIST,
+ D3DPT_TRIANGLESTRIP,
+ D3DPT_TRIANGLEFAN,
+ D3DPT_POINTLIST, // actually not supported!
+};
+// end of stolen stuff
+
+
+static IDirect3DVertexDeclaration9 *im2ddecl_uv2;
+static IDirect3DVertexBuffer9 *im2dvertbuf_uv2;
+static IDirect3DIndexBuffer9 *im2dindbuf_uv2;
+
+void
+openim2d_uv2(void)
+{
+ using namespace rw;
+ using namespace d3d;
+ D3DVERTEXELEMENT9 elements[5] = {
+ { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },
+ { 0, offsetof(Im2DVertexUV2, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
+ { 0, offsetof(Im2DVertexUV2, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, offsetof(Im2DVertexUV2, u2), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ D3DDECL_END()
+ };
+ assert(im2ddecl_uv2 == nil);
+ im2ddecl_uv2 = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements);
+ assert(im2ddecl_uv2);
+
+ assert(im2dvertbuf_uv2 == nil);
+ im2dvertbuf_uv2 = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im2DVertexUV2), 0, true);
+ assert(im2dvertbuf_uv2);
+ addDynamicVB(NUMVERTICES*sizeof(Im2DVertexUV2), 0, &im2dvertbuf_uv2);
+
+ assert(im2dindbuf_uv2 == nil);
+ im2dindbuf_uv2 = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(rw::uint16), true);
+ assert(im2dindbuf_uv2);
+ addDynamicIB(NUMINDICES*sizeof(rw::uint16), &im2dindbuf_uv2);
+}
+
+void
+closeim2d_uv2(void)
+{
+ using namespace rw;
+ using namespace d3d;
+
+ d3d9::destroyVertexDeclaration(im2ddecl_uv2);
+ im2ddecl_uv2 = nil;
+
+ removeDynamicVB(&im2dvertbuf_uv2);
+ destroyVertexBuffer(im2dvertbuf_uv2);
+ im2dvertbuf_uv2 = nil;
+
+ removeDynamicIB(&im2dindbuf_uv2);
+ destroyIndexBuffer(im2dindbuf_uv2);
+ im2dindbuf_uv2 = nil;
+}
+
+void
+RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices)
+{
+ using namespace rw;
+ using namespace d3d;
+
+ if(numVertices > NUMVERTICES ||
+ numIndices > NUMINDICES){
+ // TODO: error
+ return;
+ }
+ rw::uint16 *lockedindices = lockIndices(im2dindbuf_uv2, 0, numIndices*sizeof(rw::uint16), D3DLOCK_DISCARD);
+ memcpy(lockedindices, indices, numIndices*sizeof(rw::uint16));
+ unlockIndices(im2dindbuf_uv2);
+
+ rw::uint8 *lockedvertices = lockVertices(im2dvertbuf_uv2, 0, numVertices*sizeof(Im2DVertexUV2), D3DLOCK_DISCARD);
+ memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertexUV2));
+ unlockVertices(im2dvertbuf_uv2);
+
+ setStreamSource(0, im2dvertbuf_uv2, 0, sizeof(Im2DVertexUV2));
+ setIndices(im2dindbuf_uv2);
+ setVertexDeclaration(im2ddecl_uv2);
+
+ if(im2dOverridePS)
+ setPixelShader(im2dOverridePS);
+ else if(engine->device.getRenderState(TEXTURERASTER))
+ setPixelShader(im2d_tex_PS);
+ else
+ setPixelShader(im2d_PS);
+
+ d3d::flushCache();
+
+ rw::uint32 primCount = 0;
+ switch(primType){
+ case PRIMTYPELINELIST:
+ primCount = numIndices/2;
+ break;
+ case PRIMTYPEPOLYLINE:
+ primCount = numIndices-1;
+ break;
+ case PRIMTYPETRILIST:
+ primCount = numIndices/3;
+ break;
+ case PRIMTYPETRISTRIP:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPETRIFAN:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPEPOINTLIST:
+ primCount = numIndices;
+ break;
+ }
+ d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0,
+ 0, numVertices,
+ 0, primCount);
+}
+#endif
+
+#ifdef RW_GL3
+// different than im2d
+#define NUMINDICES 1024
+#define NUMVERTICES 1024
+
+static rw::gl3::AttribDesc im2d_UV2_attribDesc[4] = {
+ { rw::gl3::ATTRIB_POS, GL_FLOAT, GL_FALSE, 4,
+ sizeof(Im2DVertexUV2), 0 },
+ { rw::gl3::ATTRIB_COLOR, GL_UNSIGNED_BYTE, GL_TRUE, 4,
+ sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, r) },
+ { rw::gl3::ATTRIB_TEXCOORDS0, GL_FLOAT, GL_FALSE, 2,
+ sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u) },
+ { rw::gl3::ATTRIB_TEXCOORDS1, GL_FLOAT, GL_FALSE, 2,
+ sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u2) }
+};
+
+static int primTypeMap[] = {
+ GL_POINTS, // invalid
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLE_FAN,
+ GL_POINTS
+};
+
+static int32 u_xform;
+
+uint32 im2D_UV2_Vbo, im2D_UV2_Ibo;
+#ifdef RW_GL_USE_VAOS
+uint32 im2D_UV2_Vao;
+#endif
+
+void
+openim2d_uv2(void)
+{
+ u_xform = rw::gl3::registerUniform("u_xform"); // this doesn't add a new one, so it's safe
+
+ glGenBuffers(1, &im2D_UV2_Ibo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW);
+
+ glGenBuffers(1, &im2D_UV2_Vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo);
+ glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW);
+
+#ifdef RW_GL_USE_VAOS
+ glGenVertexArrays(1, &im2D_UV2_Vao);
+ glBindVertexArray(im2D_UV2_Vao);
+ setAttribPointers(im2d_UV2_attribDesc, 4);
+#endif
+}
+
+void
+closeim2d_uv2(void)
+{
+ glDeleteBuffers(1, &im2D_UV2_Ibo);
+ glDeleteBuffers(1, &im2D_UV2_Vbo);
+#ifdef RW_GL_USE_VAOS
+ glDeleteVertexArrays(1, &im2D_UV2_Vao);
+#endif
+}
+
+void
+RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices)
+{
+ using namespace rw;
+ using namespace gl3;
+
+ GLfloat xform[4];
+ Camera *cam;
+ cam = (Camera*)engine->currentCamera;
+
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(im2D_UV2_Vao);
+#endif
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices);
+
+ glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo);
+ glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertexUV2), vertices);
+
+ xform[0] = 2.0f/cam->frameBuffer->width;
+ xform[1] = -2.0f/cam->frameBuffer->height;
+ xform[2] = -1.0f;
+ xform[3] = 1.0f;
+
+ if(im2dOverrideShader)
+ im2dOverrideShader->use();
+ else
+ assert(0);//im2dShader->use();
+#ifndef RW_GL_USE_VAOS
+ setAttribPointers(im2d_UV2_attribDesc, 4);
+#endif
+
+ glUniform4fv(currentShader->uniformLocations[u_xform], 1, xform);
+
+ flushCache();
+ glDrawElements(primTypeMap[primType], numIndices,
+ GL_UNSIGNED_SHORT, nil);
+#ifndef RW_GL_USE_VAOS
+ disableAttribPointers(im2d_UV2_attribDesc, 4);
+#endif
+}
+#endif
+
+#endif
diff --git a/src/extras/screendroplets.h b/src/extras/screendroplets.h
new file mode 100644
index 00000000..090b1923
--- /dev/null
+++ b/src/extras/screendroplets.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#ifdef SCREEN_DROPLETS
+
+class CParticleObject;
+
+class ScreenDroplets
+{
+public:
+ enum {
+ MAXDROPS = 2000,
+ MAXDROPSMOVING = 700
+ };
+
+ class ScreenDrop
+ {
+ public:
+ float x, y, time; // shorts on xbox (short float?)
+ float size, magnification, lifetime; // "
+ CRGBA color;
+ bool active;
+ bool fades;
+
+ void Fade(void);
+ };
+
+ struct ScreenDropMoving
+ {
+ ScreenDrop *drop;
+ float dist;
+ };
+
+ static int ms_initialised;
+ static RwTexture *ms_maskTex;
+ static RwTexture *ms_screenTex;
+
+ static bool ms_enabled;
+ static bool ms_movingEnabled;
+
+ static ScreenDrop ms_drops[MAXDROPS];
+ static int ms_numDrops;
+ static ScreenDropMoving ms_dropsMoving[MAXDROPSMOVING];
+ static int ms_numDropsMoving;
+
+ static CVector ms_prevCamUp;
+ static CVector ms_prevCamPos;
+ static CVector ms_camMoveDelta;
+ static float ms_camMoveDist;
+ static CVector ms_screenMoveDelta;
+ static float ms_screenMoveDist;
+ static float ms_camUpAngle;
+
+ static int ms_splashDuration;
+ static CParticleObject *ms_splashObject;
+
+ static void Initialise(void);
+ static void InitDraw(void);
+ static void Shutdown(void);
+ static void Process(void);
+ static void Render(void);
+ static void AddToRenderList(ScreenDrop *drop);
+
+ static void Clear(void);
+ static ScreenDrop *NewDrop(float x, float y, float size, float lifetime, bool fades, int r = 255, int g = 255, int b = 255);
+ static void SetMoving(ScreenDroplets::ScreenDrop *drop);
+ static void FillScreen(int n);
+ static void FillScreenMoving(float amount, bool isBlood = false);
+ static void RegisterSplash(CParticleObject *pobj);
+
+ static void ProcessCameraMovement(void);
+ static void SprayDrops(void);
+ static void NewTrace(ScreenDroplets::ScreenDropMoving *moving);
+ static void MoveDrop(ScreenDropMoving *moving);
+ static void ProcessMoving(void);
+ static void Fade(void);
+};
+
+#endif
diff --git a/src/extras/shaders/Makefile b/src/extras/shaders/Makefile
index 5260fc3a..09a60420 100644
--- a/src/extras/shaders/Makefile
+++ b/src/extras/shaders/Makefile
@@ -65,3 +65,13 @@ neoVehicle_fs_gl.inc: neoVehicle.frag
(echo 'const char *neoVehicle_frag_src =';\
sed 's/..*/"&\\n"/' neoVehicle.frag;\
echo ';') >neoVehicle_fs_gl.inc
+
+im2d_UV2_gl.inc: im2d_UV2.vert
+ (echo 'const char *im2d_UV2_vert_src =';\
+ sed 's/..*/"&\\n"/' im2d_UV2.vert;\
+ echo ';') >im2d_UV2_gl.inc
+
+screenDroplet_fs_gl.inc: screenDroplet.frag
+ (echo 'const char *screenDroplet_frag_src =';\
+ sed 's/..*/"&\\n"/' screenDroplet.frag;\
+ echo ';') >screenDroplet_fs_gl.inc
diff --git a/src/extras/shaders/im2d_UV2.vert b/src/extras/shaders/im2d_UV2.vert
new file mode 100644
index 00000000..e5fd4d08
--- /dev/null
+++ b/src/extras/shaders/im2d_UV2.vert
@@ -0,0 +1,21 @@
+uniform vec4 u_xform;
+
+VSIN(ATTRIB_POS) vec4 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT vec2 v_tex1;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ gl_Position = in_pos;
+ gl_Position.w = 1.0;
+ gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
+ v_fog = DoFog(gl_Position.z);
+ gl_Position.xyz *= gl_Position.w;
+ v_color = in_color;
+ v_tex0 = in_tex0;
+ v_tex1 = in_tex1;
+}
diff --git a/src/extras/shaders/screenDroplet.frag b/src/extras/shaders/screenDroplet.frag
new file mode 100644
index 00000000..84d30bd5
--- /dev/null
+++ b/src/extras/shaders/screenDroplet.frag
@@ -0,0 +1,18 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN vec2 v_tex1;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 color;
+ color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));
+
+ FRAGCOLOR(color);
+}
+
diff --git a/src/extras/shaders/screenDroplet_PS.cso b/src/extras/shaders/screenDroplet_PS.cso
new file mode 100644
index 00000000..5508096b
--- /dev/null
+++ b/src/extras/shaders/screenDroplet_PS.cso
Binary files differ
diff --git a/src/extras/shaders/screenDroplet_PS.hlsl b/src/extras/shaders/screenDroplet_PS.hlsl
new file mode 100644
index 00000000..4d41da68
--- /dev/null
+++ b/src/extras/shaders/screenDroplet_PS.hlsl
@@ -0,0 +1,17 @@
+struct VS_out {
+ float4 Position : POSITION;
+ float2 TexCoord0 : TEXCOORD0;
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+};
+
+sampler2D tex0 : register(s0);
+sampler2D tex1 : register(s1);
+
+float4 main(VS_out input) : COLOR
+{
+ float4 color = input.Color;
+ color *= tex2D(tex0, input.TexCoord0.xy);
+ color *= tex2D(tex1, input.TexCoord1.xy);
+ return color;
+}
diff --git a/src/extras/shaders/screenDroplet_PS.inc b/src/extras/shaders/screenDroplet_PS.inc
new file mode 100644
index 00000000..c2055188
--- /dev/null
+++ b/src/extras/shaders/screenDroplet_PS.inc
@@ -0,0 +1,29 @@
+static unsigned char screenDroplet_PS_cso[] = {
+ 0x00, 0x02, 0xff, 0xff, 0xfe, 0xff, 0x2c, 0x00, 0x43, 0x54, 0x41, 0x42,
+ 0x1c, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff,
+ 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x30,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x31,
+ 0x00, 0xab, 0xab, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, 0x5f, 0x32,
+ 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68,
+ 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+ 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e, 0x33,
+ 0x31, 0x31, 0x31, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x03, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x03, 0xb0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x00, 0x08, 0x0f, 0xa0, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90,
+ 0x01, 0x08, 0x0f, 0xa0, 0x42, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x00, 0x00, 0xe4, 0xb0, 0x00, 0x08, 0xe4, 0xa0, 0x42, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0xb0, 0x01, 0x08, 0xe4, 0xa0,
+ 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80,
+ 0x00, 0x00, 0xe4, 0x90, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80,
+ 0x01, 0x00, 0xe4, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02,
+ 0x00, 0x08, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0xff, 0xff, 0x00, 0x00
+};