diff --git a/src/d_player.h b/src/d_player.h
index 5a4ebc1de1de080287be645e6d6188a08c79b5ff..f5c9c0577c107aa8f99dcde729b80faea0447c14 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -427,6 +427,9 @@ typedef struct player_s
 	INT32 awayviewtics;
 	angle_t awayviewaiming; // Used for cut-away view
 
+    // miru: let's add stuff to player struct!
+	angle_t viewrollangle; // Roll angle (software)
+
 	boolean spectator;
 	UINT8 bot;
 
diff --git a/src/doomtype.h b/src/doomtype.h
index a711b466d6ccf61d8262534cb585f0a7a3ef7c2e..7923b4bd5b2f6a2c1c905cc46d5ec325166b213f 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -259,7 +259,10 @@ typedef enum
 	postimg_water,
 	postimg_motion,
 	postimg_flip,
-	postimg_heat
+	postimg_heat,
+
+	// miru: more postimg definitions
+	postimg_roll
 } postimg_t;
 
 typedef UINT32 lumpnum_t; // 16 : 16 unsigned long (wad num: lump num)
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index bd5605f235afa1f89425729e19d6443dacb34eeb..1c53afac55be7dd3ee0188a4b78c31a4b025c555 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -308,6 +308,9 @@ static int player_get(lua_State *L)
 		lua_pushinteger(L, plr->awayviewtics);
 	else if (fastcmp(field,"awayviewaiming"))
 		lua_pushangle(L, plr->awayviewaiming);
+    // miru: expose and "get" new player struct vars from Lua
+    else if (fastcmp(field,"viewrollangle"))
+		lua_pushangle(L, plr->viewrollangle);
 	else if (fastcmp(field,"spectator"))
 		lua_pushboolean(L, plr->spectator);
 	else if (fastcmp(field,"bot"))
@@ -579,6 +582,8 @@ static int player_set(lua_State *L)
 	}
 	else if (fastcmp(field,"awayviewaiming"))
 		plr->awayviewaiming = luaL_checkangle(L, 3);
+    else if (fastcmp(field,"viewrollangle"))
+		plr->viewrollangle = luaL_checkangle(L, 3);
 	else if (fastcmp(field,"spectator"))
 		plr->spectator = lua_toboolean(L, 3);
 	else if (fastcmp(field,"bot"))
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 9b680a85d69ef4d9041ff97b6e29cb00bd303119..4f4c1f305c9c5e3529be67ffbbc92b6f9deed7be 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -3496,6 +3496,13 @@ boolean P_CheckMotionBlur(void)
 	return false;
 }
 
+static boolean P_CheckViewRoll(player_t *player)
+{
+	if (player->viewrollangle != 0)
+	    return true;
+
+	return false;
+}
 
 // P_CameraThinker
 //
@@ -3524,13 +3531,20 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 			postimg = postimg_water;
 		else if (P_CameraCheckHeat(&dummycam))
 			postimg = postimg_heat;
-        // miru: Check for Motion Blur Activation
+        // miru: assign new postimg on displays
+        else if (P_CheckViewRoll(player))
+        {
+            postimg = postimg_roll;
+            postimgparam = player->viewrollangle;
+        }
         else if (P_CheckMotionBlur())
-			postimg = postimg_motion;
+		{
+		    postimg = postimg_motion;
 			if (!forward_postimgparam)
                 forward_postimgparam = 1;
             else
                 postimgparam = forward_postimgparam;
+		}
 	}
 	else
 	{
@@ -3539,13 +3553,19 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 			postimg = postimg_water;
 		else if (P_CameraCheckHeat(thiscam))
 			postimg = postimg_heat;
-		// miru: Check for Motion Blur Activation
+        else if (P_CheckViewRoll(player))
+        {
+            postimg = postimg_roll;
+            postimgparam = player->viewrollangle;
+        }
         else if (P_CheckMotionBlur())
-			postimg = postimg_motion;
+		{
+		    postimg = postimg_motion;
 			if (!forward_postimgparam)
                 forward_postimgparam = 1;
             else
                 postimgparam = forward_postimgparam;
+		}
 	}
 
 	if (postimg != postimg_none)
diff --git a/src/p_user.c b/src/p_user.c
index 0413e58265257f7355a5dc8e59623c17fc0d4089..5ae0d03f058867a417324bd30f6fff62512cc9fb 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8566,6 +8566,14 @@ static void P_CalcPostImg(player_t *player)
 	if (player->mo->eflags & MFE_VERTICALFLIP)
 		*type = postimg_flip;
 
+    // miru: postimg types and params need to be set here
+
+    if (player->viewrollangle != 0)
+    {
+        *type = postimg_roll;
+        *param = (player->viewrollangle);
+    }
+
     //miru: Motion blur won't work without this i guess, either way its enabled
     //TODO: Opengl motion blur
 	// Motion blur
diff --git a/src/v_video.c b/src/v_video.c
index 3cc6d195f51e5b879e2704320cc65c70744a2615..b786d56139c772b01c13b516a79df4af5d45f4a7 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -1849,6 +1849,7 @@ INT32 V_ThinStringWidth(const char *string, INT32 option)
 boolean *heatshifter = NULL;
 INT32 lastheight = 0;
 INT32 heatindex[2] = { 0, 0 };
+#define ANG2RAD(angle) ((float)((angle)*M_PI)/ANGLE_180)
 
 //
 // V_DoPostProcessor
@@ -2019,6 +2020,84 @@ Unoptimized version
 		VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
 				vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
 	}
+	else if (type == postimg_roll) // miru: View Roll Angle
+	{
+        UINT8 *tmpscr = screens[4];
+        UINT8 *srcscr = screens[0];
+        INT32 x, y;
+        angle_t your_angle = param;
+        float f_angle = ANG2RAD(your_angle);
+        //CONS_Printf("%f\n", f_angle);
+
+        INT32 hwidth = vid.width / 2;
+        INT32 hheight = height / 2;
+
+        float sinma = sin(f_angle);
+
+        float cosma = cos(f_angle);
+
+        float xst = (INT32)round((cosma * -hwidth - sinma * -hheight) + hwidth);
+        float yst = (INT32)round((sinma * -hwidth + cosma * -hheight) + hheight);
+
+#define OUT_OF_RANGE (xs < 0 || xs >= vid.width || ys < 0 || ys >= height)
+        for (y = 0; y < hheight; y++) {
+            float xs = xst;
+            float ys = yst;
+
+            x = 0;
+
+            while (x < vid.width && OUT_OF_RANGE) {
+                xs += cosma;
+                ys += sinma;
+                x++;
+            }
+
+            for (; x < vid.width && !OUT_OF_RANGE; x++) {
+                INT16 xn = (int) xs,
+                      yn = (int) ys;
+
+                tmpscr[x + (y + yoffset) * vid.width] = srcscr[xn + (yn + yoffset) * vid.width];
+
+                xn = vid.width - 1 - xn;
+                yn = height - 1 - yn;
+
+                tmpscr[(vid.width - 1 - x) + (height - 1 - y + yoffset) * vid.width] = srcscr[xn + (yn + yoffset) * vid.width];
+
+                xs += cosma;
+                ys += sinma;
+            }
+
+            xst -= sinma;
+            yst += cosma;
+        }
+#undef OUT_OF_RANGE
+/* reference implementation
+        for(x = 0; x < vid.width; x++) {
+            for(y = 0; y < height; y++) {
+
+                INT32 xt = x - hwidth;
+                INT32 yt = y - hheight;
+
+                INT32 xs = (INT32)round((cosma * xt - sinma * yt) + hwidth);
+                INT32 ys = (INT32)round((sinma * xt + cosma * yt) + hheight);
+
+                if(xs >= 0 && xs < vid.width && ys >= 0 && ys < height)
+                {
+                    // set target pixel (x,y) to color at (xs,ys)
+                    tmpscr[x + (y + yoffset) * vid.width] = srcscr[xs + (ys + yoffset) * vid.width];
+                }
+                else
+                {
+                    // set target pixel (x,y) to some default background
+                    tmpscr[x + (y+yoffset)*vid.width] = 0;
+                }
+            }
+        }
+*/
+
+        VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
+                vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
+	}
 #endif
 }