From c3fbb17e030c77cf94d1646dabd0e5af4150a02b Mon Sep 17 00:00:00 2001
From: Dalai Felinto <dfelinto@gmail.com>
Date: Sat, 4 Apr 2009 02:05:57 +0000
Subject: [PATCH] Warp Mesh supported now. CopyTexSubImage2D implemented now
 instead of CopyTexImage2D

---
 source/blender/blenkernel/BKE_blender.h       |   2 +-
 source/blender/blenloader/intern/readfile.c   |  10 +-
 source/blender/makesdna/DNA_scene_types.h     |   3 +
 source/blender/src/buttons_scene.c            |   6 +-
 .../BlenderRoutines/BL_KetsjiEmbedStart.cpp   |   2 +-
 .../GamePlayer/ghost/GPG_Application.cpp      |   2 +-
 source/gameengine/Ketsji/KX_Dome.cpp          | 371 +++++++++++++++---
 source/gameengine/Ketsji/KX_Dome.h            |  32 +-
 source/gameengine/Ketsji/KX_KetsjiEngine.cpp  |   5 +-
 source/gameengine/Ketsji/KX_KetsjiEngine.h    |   2 +-
 10 files changed, 361 insertions(+), 74 deletions(-)

diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h
index db6d4762b17..d49a5425b61 100644
--- a/source/blender/blenkernel/BKE_blender.h
+++ b/source/blender/blenkernel/BKE_blender.h
@@ -41,7 +41,7 @@ struct ListBase;
 struct MemFile;
 
 #define BLENDER_VERSION			248
-#define BLENDER_SUBVERSION		3
+#define BLENDER_SUBVERSION		4
 
 #define BLENDER_MINVERSION		245
 #define BLENDER_MINSUBVERSION	15
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 4317cddd4c2..d03bcfa09e5 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -3531,7 +3531,9 @@ static void lib_link_scene(FileData *fd, Main *main)
 				srl->mat_override= newlibadr_us(fd, sce->id.lib, srl->mat_override);
 				srl->light_override= newlibadr_us(fd, sce->id.lib, srl->light_override);
 			}
-			
+			/*Game Settings: Dome Warp Text*/
+			sce->r.dometext= newlibadr_us(fd, sce->id.lib, sce->r.dometext);
+
 			sce->id.flag -= LIB_NEEDLINK;
 		}
 
@@ -8035,7 +8037,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
 		}
 	}
 	
-	if (main->versionfile < 248 || (main->versionfile == 248 && main->subversionfile < 3)) {
+	if (main->versionfile < 248 || (main->versionfile == 248 && main->subversionfile < 4)) {
 		Scene *sce;
 
 		/*  Dome (Fisheye) default parameters  */
@@ -8854,7 +8856,9 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
 		expand_doit(fd, mainvar, srl->mat_override);
 		expand_doit(fd, mainvar, srl->light_override);
 	}
-				
+
+	if(sce->r.dometext)
+		expand_doit(fd, mainvar, sce->r.dometext);
 }
 
 static void expand_camera(FileData *fd, Main *mainvar, Camera *ca)
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 3742aabb95a..bf5b2ad3df5 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -45,6 +45,7 @@ struct World;
 struct Scene;
 struct Image;
 struct Group;
+struct Text;
 struct bNodeTree;
 
 typedef struct Base {
@@ -320,6 +321,8 @@ typedef struct RenderData {
 	short domeangle, pad9;
 	float domesize;
 	float domeresbuf;
+	struct Text *dometext;
+
 } RenderData;
 
 /* control render convert and shading engine */
diff --git a/source/blender/src/buttons_scene.c b/source/blender/src/buttons_scene.c
index 68f043540c7..f026e5f4034 100644
--- a/source/blender/src/buttons_scene.c
+++ b/source/blender/src/buttons_scene.c
@@ -1766,13 +1766,13 @@ static uiBlock *edge_render_menu(void *arg_unused)
 static uiBlock *framing_render_menu(void *arg_unused)
 {
 	uiBlock *block;
-	short yco = 241, xco = 0;
+	short yco = 267, xco = 0;
 	int randomcolorindex = 1234;
 
 	block= uiNewBlock(&curarea->uiblocks, "framing_options", UI_EMBOSS, UI_HELV, curarea->win);
 
 	/* use this for a fake extra empy space around the buttons */
-	uiDefBut(block, LABEL, 0, "",			-5, -10, 295, 275, NULL, 0, 0, 0, 0, "");
+	uiDefBut(block, LABEL, 0, "",			-5, -10, 295, 300, NULL, 0, 0, 0, 0, "");
 
 	uiDefBut(block, LABEL, 0, "Framing:", xco, yco, 68,19, 0, 0, 0, 0, 0, "");
 	uiBlockBeginAlign(block);
@@ -1834,6 +1834,8 @@ static uiBlock *framing_render_menu(void *arg_unused)
 	uiDefButF(block, NUM, 0, "Size:",		xco-=180, yco-=21, 88, 19, &G.scene->r.domesize, 0.5, 3.5, 0, 0, "Size adjustments");
 	uiDefButS(block, NUM, 0, "Tes:",		xco+=90, yco, 88, 19, &G.scene->r.domeres, 1.0, 8.0, 0, 0, "Tesselation level - 1 to 8");
 	uiDefButF(block, NUM, 0, "Res:",	xco+=90, yco, 88, 19, &G.scene->r.domeresbuf, 0.1, 1.0, 0, 0, "Buffer Resolution - decrease it to increase speed");
+
+	uiDefIDPoinBut(block, test_scriptpoin_but, ID_SCRIPT, 1, "Warp Data: ", xco-180,yco-=21,268, 19, &G.scene->r.dometext, "Custom Warp Mesh data file");
 	uiBlockEndAlign(block);
 
 	uiBlockSetDirection(block, UI_TOP);
diff --git a/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp b/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
index b2761e31f24..0cc574d762d 100644
--- a/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
+++ b/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
@@ -374,7 +374,7 @@ extern "C" void StartKetsjiShell(struct ScrArea *area,
 
 			//initialize Dome Settings
 			if(blscene->r.stereomode == RAS_IRasterizer::RAS_STEREO_DOME)
-				ketsjiengine->InitDome(blscene->r.domesize, blscene->r.domeres, blscene->r.domemode, blscene->r.domeangle, blscene->r.domeresbuf);
+				ketsjiengine->InitDome(blscene->r.domesize, blscene->r.domeres, blscene->r.domemode, blscene->r.domeangle, blscene->r.domeresbuf, blscene->r.dometext);
 
 			if (sceneconverter)
 			{
diff --git a/source/gameengine/GamePlayer/ghost/GPG_Application.cpp b/source/gameengine/GamePlayer/ghost/GPG_Application.cpp
index 9c28a26cf1b..beb74add946 100644
--- a/source/gameengine/GamePlayer/ghost/GPG_Application.cpp
+++ b/source/gameengine/GamePlayer/ghost/GPG_Application.cpp
@@ -695,7 +695,7 @@ bool GPG_Application::startEngine(void)
 
 		//initialize Dome Settings
 		if(m_startScene->r.stereomode == RAS_IRasterizer::RAS_STEREO_DOME)
-			m_ketsjiengine->InitDome(m_startScene->r.domesize, m_startScene->r.domeres, m_startScene->r.domemode, m_startScene->r.domeangle, m_startScene->r.domeresbuf);
+			m_ketsjiengine->InitDome(m_startScene->r.domesize, m_startScene->r.domeres, m_startScene->r.domemode, m_startScene->r.domeangle, m_startScene->r.domeresbuf, m_startScene->r.dometext);
 
 		// Set the GameLogic.globalDict from marshal'd data, so we can
 		// load new blend files and keep data in GameLogic.globalDict
diff --git a/source/gameengine/Ketsji/KX_Dome.cpp b/source/gameengine/Ketsji/KX_Dome.cpp
index 6c0e1db01c3..3485d0f506f 100644
--- a/source/gameengine/Ketsji/KX_Dome.cpp
+++ b/source/gameengine/Ketsji/KX_Dome.cpp
@@ -49,7 +49,8 @@ KX_Dome::KX_Dome (
 	short res,		//resolution of the mesh
 	short mode,		//mode - fisheye, truncated, warped, panoramic, ...
 	short angle,
-	float resbuf	//size adjustment of the buffer
+	float resbuf,	//size adjustment of the buffer
+	struct Text* warptext
 
 ):
 	m_canvas(canvas),
@@ -65,9 +66,22 @@ KX_Dome::KX_Dome (
 	canvaswidth(-1), canvasheight(-1),
 	dlistSupported(false)
 {
-	if (mode > DOME_NUM_MODES)
+	warp.usemesh = false;
+
+	if (mode >= DOME_NUM_MODES)
 		m_mode = DOME_FISHEYE;
-	
+
+	if (warptext) // it there is a text data try to warp it
+	{
+		char *buf;
+		buf = txt_to_buf(warptext);
+		if (buf)
+		{
+			warp.usemesh = ParseWarpMesh(STR_String(buf));
+			MEM_freeN(buf);
+		}
+	}
+
 	//setting the viewport size
 	GLuint	viewport[4]={0};
 	glGetIntegerv(GL_VIEWPORT,(GLint *)viewport);
@@ -118,33 +132,58 @@ KX_Dome::KX_Dome (
 			break;
 	}
 
+	m_numimages =(warp.usemesh?m_numfaces+1:m_numfaces);
+
 	CalculateCameraOrientation();
 
 	CreateGLImages();
 
-	//openGL check 
-	if(GLEW_VERSION_1_1){
-		dlistSupported = true;
-		CreateDL();
-	}
+	dlistSupported = CreateDL();
 }
 
 // destructor
 KX_Dome::~KX_Dome (void)
 {
+	GLuint m_numimages = m_numfaces;
+
 	ClearGLImages();
 
 	if(dlistSupported)
-		glDeleteLists(dlistId, (GLsizei) m_numfaces);
+		glDeleteLists(dlistId, (GLsizei) m_numimages);
 }
 
-void KX_Dome::CreateGLImages(void){
-	glGenTextures(m_numfaces, (GLuint*)&domefacesId);
+void KX_Dome::SetViewPort(GLuint viewport[4])
+{
+	if(canvaswidth != m_canvas->GetWidth() || canvasheight != m_canvas->GetHeight())
+	{
+		m_viewport.SetLeft(viewport[0]); 
+		m_viewport.SetBottom(viewport[1]);
+		m_viewport.SetRight(viewport[2]);
+		m_viewport.SetTop(viewport[3]);
+
+		CalculateImageSize();
+	}
+}
+
+void KX_Dome::CreateGLImages(void)
+{
+	glGenTextures(m_numimages, (GLuint*)&domefacesId);
 
 	for (int j=0;j<m_numfaces;j++){
 		glBindTexture(GL_TEXTURE_2D, domefacesId[j]);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, m_imagesize, m_imagesize, 0, GL_RGBA,
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m_imagesize, m_imagesize, 0, GL_RGB8,
+				GL_UNSIGNED_BYTE, 0);
+		glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, m_imagesize, m_imagesize, 0);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	}
+	if(warp.usemesh){
+		glBindTexture(GL_TEXTURE_2D, domefacesId[m_numfaces]);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, warp.imagewidth, warp.imageheight, 0, GL_RGB8,
 				GL_UNSIGNED_BYTE, 0);
+		glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, warp.imagewidth, warp.imageheight, 0);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -154,9 +193,9 @@ void KX_Dome::CreateGLImages(void){
 
 void KX_Dome::ClearGLImages(void)
 {
-	glDeleteTextures(m_numfaces, (GLuint*)&domefacesId);
+	glDeleteTextures(m_numimages, (GLuint*)&domefacesId);
 /*
-	for (int i=0;i<m_numfaces;i++)
+	for (int i=0;i<m_numimages;i++)
 		if(glIsTexture(domefacesId[i]))
 			glDeleteTextures(1, (GLuint*)&domefacesId[i]);
 */
@@ -180,12 +219,27 @@ void KX_Dome::CalculateImageSize(void)
 	while ((1 << i) <= m_buffersize)
 		i++;
 	m_imagesize = (1 << i);
+
+	if (warp.usemesh){
+		warp.bufferwidth = canvaswidth;
+		warp.bufferheight = canvasheight;
+
+		i = 0;
+		while ((1 << i) <= warp.bufferwidth)
+			i++;
+		warp.imagewidth = (1 << i);
+
+		i = 0;
+		while ((1 << i) <= warp.bufferheight)
+			i++;
+		warp.imageheight = (1 << i);
+	}
 }
 
-void KX_Dome::CreateDL(){
+bool KX_Dome::CreateDL(){
 	int i,j;
 
-	dlistId = glGenLists((GLsizei) m_numfaces);
+	dlistId = glGenLists((GLsizei) m_numimages);
 	if (dlistId != 0) {
 		if(m_mode == DOME_FISHEYE || m_mode == DOME_TRUNCATED){
 			glNewList(dlistId, GL_COMPILE);
@@ -210,8 +264,8 @@ void KX_Dome::CreateDL(){
 				glEndList();
 			}
 		}
-		else if (m_mode == DOME_PANORAM_SPH){
-
+		else if (m_mode == DOME_PANORAM_SPH)
+		{
 			glNewList(dlistId, GL_COMPILE);
 				GLDrawTriangles(cubetop, nfacestop);
 			glEndList();
@@ -236,6 +290,13 @@ void KX_Dome::CreateDL(){
 				GLDrawTriangles(cuberightback, nfacesrightback);
 			glEndList();
 		}
+
+		if(warp.usemesh){
+			glNewList((dlistId + m_numfaces), GL_COMPILE);
+				GLDrawWarpQuads();
+			glEndList();
+		}
+
 		//clearing the vectors 
 		cubetop.clear();
 		cubebottom.clear();
@@ -244,9 +305,12 @@ void KX_Dome::CreateDL(){
 		cubefront.clear();
 		cubeleftback.clear();
 		cuberightback.clear();
+		warp.nodes.clear();
 
 	} else // genList failed
-		dlistSupported = false;
+		return false;
+
+	return true;
 }
 
 void KX_Dome::GLDrawTriangles(vector <DomeFace>& face, int nfaces)
@@ -261,6 +325,148 @@ void KX_Dome::GLDrawTriangles(vector <DomeFace>& face, int nfaces)
 		}
 	glEnd();
 }
+
+void KX_Dome::GLDrawWarpQuads(void)
+{
+	int i, j, i2;
+	float uv_width = (float)warp.bufferwidth / warp.imagewidth;
+	float uv_height = (float)warp.bufferheight / warp.imageheight;
+
+	if(warp.mode ==2 ){
+		glBegin(GL_QUADS);
+		for (i=0;i<warp.n_height-1;i++) {
+			for (j=0;j<warp.n_width-1;j++) {
+				if(warp.nodes[i][j].i < 0 || warp.nodes[i+1][j].i < 0 || warp.nodes[i+1][j+1].i < 0 || warp.nodes[i][j+1].i < 0)
+					continue;
+
+				glColor3f(warp.nodes[i][j].i, warp.nodes[i][j].i, warp.nodes[i][j].i);
+				glTexCoord2f((warp.nodes[i][j].u * uv_width), (warp.nodes[i][j].v * uv_height));
+				glVertex3f(warp.nodes[i][j].x, warp.nodes[i][j].y,0.0);
+
+				glColor3f(warp.nodes[i+1][j].i, warp.nodes[i+1][j].i, warp.nodes[i+1][j].i);
+				glTexCoord2f((warp.nodes[i+1][j].u * uv_width), (warp.nodes[i+1][j].v * uv_height));
+				glVertex3f(warp.nodes[i+1][j].x, warp.nodes[i+1][j].y,0.0);
+
+				glColor3f(warp.nodes[i+1][j+1].i, warp.nodes[i+1][j+1].i, warp.nodes[i+1][j+1].i);
+				glTexCoord2f((warp.nodes[i+1][j+1].u * uv_width), (warp.nodes[i+1][j+1].v * uv_height));
+				glVertex3f(warp.nodes[i+1][j+1].x, warp.nodes[i+1][j+1].y,0.0);
+
+				glColor3f(warp.nodes[i][j+1].i, warp.nodes[i][j+1].i, warp.nodes[i][j+1].i);
+				glTexCoord2f((warp.nodes[i][j+1].u * uv_width), (warp.nodes[i][j+1].v * uv_height));
+				glVertex3f(warp.nodes[i][j+1].x, warp.nodes[i][j+1].y,0.0);
+			}
+		}
+		glEnd();
+	}
+	else if (warp.mode == 1){
+		glBegin(GL_QUADS);
+		for (i=0;i<warp.n_height-1;i++) {
+			for (j=0;j<warp.n_width-1;j++) {
+				i2 = (i+1) % warp.n_width; // Wrap around, i = warp.n_width = 0
+
+				if (warp.nodes[i][j].i < 0 || warp.nodes[i2][j].i < 0 || warp.nodes[i2][j+1].i < 0 || warp.nodes[i][j+1].i < 0)
+					continue;
+
+				 glColor3f(warp.nodes[i][j].i,warp.nodes[i][j].i,warp.nodes[i][j].i);
+				 glTexCoord2f((warp.nodes[i][j].u * uv_width), (warp.nodes[i][j].v * uv_height));
+				 glVertex3f(warp.nodes[i][j].x,warp.nodes[i][j].y,0.0);
+
+				 glColor3f(warp.nodes[i2][j].i,warp.nodes[i2][j].i,warp.nodes[i2][j].i);
+				 glTexCoord2f((warp.nodes[i2][j].u * uv_width), (warp.nodes[i2][j].v * uv_height));
+				 glVertex3f(warp.nodes[i2][j].x,warp.nodes[i2][j].y,0.0);
+
+				 glColor3f(warp.nodes[i2][j+1].i,warp.nodes[i2][j+1].i,warp.nodes[i2][j+1].i);
+				 glTexCoord2f((warp.nodes[i2][j+1].u * uv_width), (warp.nodes[i2][j+1].v * uv_height));
+				 glVertex3f(warp.nodes[i2][j+1].x,warp.nodes[i2][j+1].y,0.0);
+
+				 glColor3f(warp.nodes[i2][j+1].i,warp.nodes[i2][j+1].i,warp.nodes[i2][j+1].i);
+				 glTexCoord2f((warp.nodes[i2][j+1].u * uv_width), (warp.nodes[i2][j+1].v * uv_height));
+				 glVertex3f(warp.nodes[i2][j+1].x,warp.nodes[i2][j+1].y,0.0);
+
+			}
+		}
+		glEnd();
+	} else{
+		printf("Error: Warp Mode unsupported. Try 1 for Polar Mesh or 2 for Fisheye.\n");
+	}
+}
+
+
+bool KX_Dome::ParseWarpMesh(STR_String text)
+{
+/*
+//Notes about the supported data format:
+File example::
+	mode
+	width height
+	n0_x n0_y n0_u n0_v n0_i
+	n1_x n1_y n1_u n1_v n1_i
+	n2_x n1_y n2_u n2_v n2_i
+	n3_x n3_y n3_u n3_v n3_i
+	(...)
+First line is the image type the mesh is support to be applied to: 2 = fisheye, 1=radial
+Tthe next line has the mesh dimensions
+Rest of the lines are the nodes of the mesh. Each line has x y u v i
+  (x,y) are the normalised screen coordinates
+  (u,v) texture coordinates
+  i a multiplicative intensity factor
+
+x varies from -screen aspect to screen aspect
+y varies from -1 to 1
+u and v vary from 0 to 1
+i ranges from 0 to 1, if negative don't draw that mesh node
+*/
+	int i,j,k;
+	int nodeX=0, nodeY=0;
+
+	vector<STR_String> columns, lines;
+
+	lines = text.Explode('\n');
+	if(lines.size() < 6){
+		printf("Error: Warp Mesh File with insufficient data!\n");
+		return false;
+	}
+	columns = lines[1].Explode(' ');
+
+	if(columns.size() !=2){
+		printf("Error: Warp Mesh File incorrect. The second line should contain: width height.\n");
+		return false;
+	}
+
+	warp.mode = atoi(lines[0]);// 1 = radial, 2 = fisheye
+
+	warp.n_width = atoi(columns[0]);
+	warp.n_height = atoi(columns[1]);
+
+	if (lines.size() < 2 + (warp.n_width * warp.n_height)){
+		printf("Error: Warp Mesh File with insufficient data!\n");
+		return false;
+	}else{
+		warp.nodes = vector<vector <WarpMeshNode>> (warp.n_height, vector<WarpMeshNode>(warp.n_width));
+
+		for(i=2; i-2 < (warp.n_width*warp.n_height); i++){
+			columns = lines[i].Explode(' ');
+
+			if (columns.size() == 5){
+				nodeX = (i-2)%warp.n_width;
+				nodeY = ((i-2) - nodeX) / warp.n_width;
+
+				warp.nodes[nodeY][nodeX].x = atof(columns[0]);
+				warp.nodes[nodeY][nodeX].y = atof(columns[1]);
+				warp.nodes[nodeY][nodeX].u = atof(columns[2]);
+				warp.nodes[nodeY][nodeX].v = atof(columns[3]);
+				warp.nodes[nodeY][nodeX].i = atof(columns[4]);
+			}
+			else{
+				warp.nodes.clear();
+				printf("Error: Warp Mesh File with wrong number of fields. You should use 5: x y u v i.\n");
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
 void KX_Dome::CreateMeshDome180(void)
 {
 /*
@@ -1321,6 +1527,7 @@ void KX_Dome::RotateCamera(KX_Camera* cam, int i)
 
 void KX_Dome::Draw(void)
 {
+
 	switch(m_mode){
 		case DOME_FISHEYE:
 			DrawDomeFisheye();
@@ -1332,6 +1539,13 @@ void KX_Dome::Draw(void)
 			DrawPanorama();
 			break;
 	}
+
+	if(warp.usemesh)
+	{
+		glBindTexture(GL_TEXTURE_2D, domefacesId[m_numfaces]);
+		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_viewport.GetLeft(), m_viewport.GetBottom(), warp.bufferwidth+1, warp.bufferheight+1);
+		DrawDomeWarped();
+	}
 }
 
 void KX_Dome::DrawDomeFisheye(void)
@@ -1349,15 +1563,17 @@ void KX_Dome::DrawDomeFisheye(void)
 
 	float ortho_width, ortho_height;
 
-	if (m_mode == DOME_TRUNCATED){
-			ortho_width = 1.0;
-			ortho_height = 2 * ((float)can_height/can_width) - 1.0 ;
-			
-			ortho_width /= m_size;
-			ortho_height /= m_size;
+	if (warp.usemesh)
+		glOrtho((-1.0), 1.0, (-1.0), 1.0, -20.0, 10.0); //stretch the image to reduce resolution lost
+
+	else if(m_mode == DOME_TRUNCATED){
+		ortho_width = 1.0;
+		ortho_height = 2 * ((float)can_height/can_width) - 1.0 ;
+		
+		ortho_width /= m_size;
+		ortho_height /= m_size;
 
-			glOrtho((-ortho_width), ortho_width, (-ortho_height), ortho_width, -20.0, 10.0);
-			
+		glOrtho((-ortho_width), ortho_width, (-ortho_height), ortho_width, -20.0, 10.0);
 	} else {
 		if (can_width < can_height){
 			ortho_width = 1.0;
@@ -1439,19 +1655,24 @@ void KX_Dome::DrawPanorama(void)
 	float ortho_height = 1.0;
 	float ortho_width = 1.0;
 
-	//using all the screen
-	if ((can_width / 2) <= (can_height)){
-		ortho_width = 1.0;
-		ortho_height = (float)can_height/can_width;
-	}else{
-		ortho_width = (float)can_width/can_height * 0.5;
-		ortho_height = 0.5;
-	}
+	if (warp.usemesh)
+		glOrtho((-1.0), 1.0, (-0.5), 0.5, -20.0, 10.0); //stretch the image to reduce resolution lost
 
-	ortho_width /= m_size;
-	ortho_height /= m_size;
-	
-	glOrtho((-ortho_width), ortho_width, (-ortho_height), ortho_height, -20.0, 10.0);
+	else {
+		//using all the screen
+		if ((can_width / 2) <= (can_height)){
+			ortho_width = 1.0;
+			ortho_height = (float)can_height/can_width;
+		}else{
+			ortho_width = (float)can_width/can_height * 0.5;
+			ortho_height = 0.5;
+		}
+
+		ortho_width /= m_size;
+		ortho_height /= m_size;
+		
+		glOrtho((-ortho_width), ortho_width, (-ortho_height), ortho_height, -20.0, 10.0);
+	}
 
 	glMatrixMode(GL_TEXTURE);
 	glLoadIdentity();
@@ -1506,28 +1727,62 @@ void KX_Dome::DrawPanorama(void)
 	glEnable(GL_DEPTH_TEST);
 }
 
-void KX_Dome::BindImages(int i)
+void KX_Dome::DrawDomeWarped(void)
 {
-/*
-todo: I'm copying more than I need.
-I would like to change glCopyTexImage by glCopyTexSubImage but I couldn't make it work.
-the copy size can be only m_buffersize
-*/
-	glBindTexture(GL_TEXTURE_2D, domefacesId[i]);
-	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_viewport.GetLeft(), m_viewport.GetBottom(), m_imagesize, m_imagesize, 0);
-}
+	int i,j;
 
-void KX_Dome::SetViewPort(GLuint viewport[4])
-{
-	if(canvaswidth != m_canvas->GetWidth() || canvasheight != m_canvas->GetHeight())
-	{
-		m_viewport.SetLeft(viewport[0]); 
-		m_viewport.SetBottom(viewport[1]);
-		m_viewport.SetRight(viewport[2]);
-		m_viewport.SetTop(viewport[3]);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
 
-		CalculateImageSize();
+	// Making the viewport always square 
+	int can_width = m_viewport.GetRight();
+	int can_height = m_viewport.GetTop();
+
+	double screen_ratio = can_width/ (double) can_height;	
+	screen_ratio /= m_size;
+
+    glOrtho(-screen_ratio,screen_ratio,-1.0,1.0,-20.0,10.0);
+
+
+	glMatrixMode(GL_TEXTURE);
+	glLoadIdentity();
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	gluLookAt(0.0, 0.0, 1.0, 0.0,0.0,0.0, 0.0,1.0,0.0);
+
+	if(m_drawingmode == RAS_IRasterizer::KX_WIREFRAME)
+		glPolygonMode(GL_FRONT, GL_LINE);
+	else
+		glPolygonMode(GL_FRONT, GL_FILL);
+
+	glShadeModel(GL_SMOOTH);
+	glDisable(GL_LIGHTING);
+	glDisable(GL_DEPTH_TEST);
+
+	glEnable(GL_TEXTURE_2D);
+	glColor3f(1.0,1.0,1.0);
+
+
+	float uv_width = (float)warp.bufferwidth / warp.imagewidth;
+	float uv_height = (float)warp.bufferheight / warp.imageheight;
+
+	if (dlistSupported){
+		glBindTexture(GL_TEXTURE_2D, domefacesId[m_numfaces]);
+		glCallList(dlistId + m_numfaces);
+	}
+	else{
+		glBindTexture(GL_TEXTURE_2D, domefacesId[m_numfaces]);
+		GLDrawWarpQuads();
 	}
+	glDisable(GL_TEXTURE_2D);
+	glEnable(GL_DEPTH_TEST);
+}
+
+void KX_Dome::BindImages(int i)
+{
+	glBindTexture(GL_TEXTURE_2D, domefacesId[i]);
+	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_viewport.GetLeft(), m_viewport.GetBottom(), m_buffersize+1, m_buffersize+1);
 }
 
 void KX_Dome::RenderDomeFrame(KX_Scene* scene, KX_Camera* cam, int i)
@@ -1537,7 +1792,7 @@ void KX_Dome::RenderDomeFrame(KX_Scene* scene, KX_Camera* cam, int i)
 
 	m_canvas->SetViewPort(0,0,m_buffersize,m_buffersize);
 
-	//m_rasterizer->SetAmbient();
+//	m_rasterizer->SetAmbient();
 	m_rasterizer->DisplayFog();
 
 	CalculateFrustum(cam); //calculates m_projmat
diff --git a/source/gameengine/Ketsji/KX_Dome.h b/source/gameengine/Ketsji/KX_Dome.h
index aac35003653..de3360cd897 100644
--- a/source/gameengine/Ketsji/KX_Dome.h
+++ b/source/gameengine/Ketsji/KX_Dome.h
@@ -36,6 +36,10 @@ Developed as part of a Research and Development project for SAT - La Soci
 #include <BIF_gl.h>
 #include <vector>
 
+#include "MEM_guardedalloc.h"
+#include "BKE_text.h"
+//#include "BLI_blenlib.h"
+
 //Dome modes: limit hardcoded in buttons_scene.c
 #define DOME_FISHEYE		1
 #define DOME_TRUNCATED		2
@@ -61,7 +65,8 @@ public:
 	short res,
 	short mode,
 	short angle,
-	float resbuf
+	float resbuf,
+	struct Text* warptext
 	);
 
 	/// destructor
@@ -71,7 +76,7 @@ public:
 	bool	dlistSupported;
 
 	//openGL names:
-	GLuint domefacesId[6];		// ID of the images -- room for 6 images, using only 4 for 180� x 360� dome
+	GLuint domefacesId[7];		// ID of the images -- room for 7 images, using only 4 for 180� x 360� dome, 6 for panoramic and +1 for warp mesh
 	GLuint dlistId;				// ID of the Display Lists of the images (used as an offset)
 	
 	typedef struct {
@@ -79,7 +84,23 @@ public:
 		MT_Vector3 verts[3]; //three verts
 	} DomeFace;
 
-	vector <DomeFace> cubetop, cubebottom, cuberight, cubeleft, cubefront, cubeback; //for dome
+	//mesh warp functions
+	typedef struct {
+		double x, y, u, v, i;
+	} WarpMeshNode;
+
+	struct {
+		bool usemesh;
+		int mode;
+		int n_width, n_height; //nodes width and height
+		int imagewidth, imageheight;
+		int bufferwidth, bufferheight;
+		vector <vector <WarpMeshNode> > nodes;
+	} warp;
+
+	bool ParseWarpMesh(STR_String text);
+
+	vector <DomeFace> cubetop, cubebottom, cuberight, cubeleft, cubefront, cubeback; //for fisheye
 	vector <DomeFace> cubeleftback, cuberightback; //for panorama
 	
 	int nfacestop, nfacesbottom, nfacesleft, nfacesright, nfacesfront, nfacesback;
@@ -107,14 +128,16 @@ public:
 
 	//Draw functions
 	void GLDrawTriangles(vector <DomeFace>& face, int nfaces);
+	void GLDrawWarpQuads(void);
 	void Draw(void);
 	void DrawDomeFisheye(void);
 	void DrawPanorama(void);
+	void DrawDomeWarped(void);
 
 	//setting up openGL
 	void CreateGLImages(void);
 	void ClearGLImages(void);//called on resize
-	void CreateDL(void); //create Display Lists
+	bool CreateDL(void); //create Display Lists
 	void ClearDL(void);  //remove Display Lists 
 
 	void CalculateCameraOrientation();
@@ -129,6 +152,7 @@ protected:
 	int m_imagesize;
 	int m_buffersize;	// canvas small dimension
 	int m_numfaces;		// 4 to 6 depending on the kind of dome image
+	int m_numimages;	//numfaces +1 if we have warp mesh
 	
 	float m_size;		// size to adjust
 	short m_resolution;	//resolution to tesselate the mesh
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
index 5bd74f4621d..68eb9f55378 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
@@ -261,15 +261,14 @@ void KX_KetsjiEngine::SetSceneConverter(KX_ISceneConverter* sceneconverter)
 	m_sceneconverter = sceneconverter;
 }
 
-void KX_KetsjiEngine::InitDome(float size, short res, short mode, short angle, float resbuf)
+void KX_KetsjiEngine::InitDome(float size, short res, short mode, short angle, float resbuf, struct Text* text)
 {
-	m_dome = new KX_Dome(m_canvas, m_rasterizer, m_rendertools,this, size, res, mode, angle, resbuf);
+	m_dome = new KX_Dome(m_canvas, m_rasterizer, m_rendertools,this, size, res, mode, angle, resbuf, text);
 	m_usedome = true;
 }
 
 void KX_KetsjiEngine::RenderDome()
 {
-
 	GLuint	viewport[4]={0};
 	glGetIntegerv(GL_VIEWPORT,(GLint *)viewport);
 //	unsigned int m_viewport[4] = {viewport[0], viewport[1], viewport[2], viewport[3]};
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h
index ccffd7b1e51..a8ccd6100d7 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.h
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h
@@ -210,7 +210,7 @@ public:
 	RAS_IRenderTools*	    GetRenderTools(){return m_rendertools;};
 
 	/// Dome functions
-	void			InitDome(float size, short res, short mode, short angle, float resbuf); 
+	void			InitDome(float size, short res, short mode, short angle, float resbuf, struct Text* text); 
 	void			EndDome();
 	void			RenderDome();
 	bool			m_usedome;
-- 
GitLab