diff options
author | Benoit Bolsee <benoit.bolsee@online.be> | 2009-04-08 19:06:20 +0400 |
---|---|---|
committer | Benoit Bolsee <benoit.bolsee@online.be> | 2009-04-08 19:06:20 +0400 |
commit | 2074128fadbfd58ea13a68cbccaa1f6771bbd710 (patch) | |
tree | 382eedec3266dc9780f3e29aa41a9153d4809b76 | |
parent | 4b77f9504c7f468fe3b3e91ab503b09b5f1e1a9e (diff) |
Patch #18462: Fisheye (Dome) and Spherical Panoramic mode in BGE.
User guide:
http://wiki.blender.org/index.php/Dev:Source/GameEngine/Fisheye_Dome_Camera
Fixed two bugs from original patch:
- deleting a text will clear the warp field from Game framing settings
- removed spurious black dots along the edge of the cube map in the gameplayer
Known limitation:
- resizing of the screen doesn't work in the gameplayer
Known bugs:
- Texture with reflexion are not rendered correctly
- Spurious problems with light
-rw-r--r-- | projectfiles_vc9/gameengine/ketsji/KX_ketsji.vcproj | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_blender.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_scene.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/scene.c | 16 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 26 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 9 | ||||
-rw-r--r-- | source/blender/src/buttons_scene.c | 17 | ||||
-rw-r--r-- | source/blender/src/header_text.c | 1 | ||||
-rw-r--r-- | source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp | 4 | ||||
-rw-r--r-- | source/gameengine/GamePlayer/ghost/GPG_Application.cpp | 5 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_Dome.cpp | 1820 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_Dome.h | 183 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_KetsjiEngine.cpp | 127 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_KetsjiEngine.h | 9 | ||||
-rw-r--r-- | source/gameengine/Rasterizer/RAS_IRasterizer.h | 1 | ||||
-rw-r--r-- | source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp | 6 |
16 files changed, 2227 insertions, 9 deletions
diff --git a/projectfiles_vc9/gameengine/ketsji/KX_ketsji.vcproj b/projectfiles_vc9/gameengine/ketsji/KX_ketsji.vcproj index c425b5664ad..80c34bef223 100644 --- a/projectfiles_vc9/gameengine/ketsji/KX_ketsji.vcproj +++ b/projectfiles_vc9/gameengine/ketsji/KX_ketsji.vcproj @@ -514,6 +514,10 @@ >
</File>
<File
+ RelativePath="..\..\..\source\gameengine\Ketsji\KX_Dome.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\..\source\gameengine\Ketsji\KX_EmptyObject.cpp"
>
</File>
@@ -803,6 +807,10 @@ >
</File>
<File
+ RelativePath="..\..\..\source\gameengine\Ketsji\KX_Dome.h"
+ >
+ </File>
+ <File
RelativePath="..\..\..\source\gameengine\Ketsji\KX_EmptyObject.h"
>
</File>
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/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 2bd528ab8c8..2c3ef42c021 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -86,5 +86,7 @@ int get_render_child_particle_number(struct RenderData *r, int num); int get_render_shadow_samples(struct RenderData *r, int samples); float get_render_aosss_error(struct RenderData *r, float error); +void free_dome_warp_text(struct Text *txt); + #endif diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 1727edc10fc..5def3577218 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -230,6 +230,11 @@ Scene *add_scene(char *name) sce->r.threads= 1; sce->r.stereomode = 1; // no stereo + sce->r.domeangle = 180; + sce->r.domemode = 1; + sce->r.domesize = 1.0f; + sce->r.domeres = 4; + sce->r.domeresbuf = 1.0f; sce->r.simplify_subsurf= 6; sce->r.simplify_particles= 1.0f; @@ -775,3 +780,14 @@ float get_render_aosss_error(RenderData *r, float error) return error; } +void free_dome_warp_text(struct Text *txt) +{ + Scene *scene; + + scene = G.main->scene.first; + while(scene) { + if (scene->r.dometext == txt) + scene->r.dometext = NULL; + scene = scene->id.next; + } +}
\ No newline at end of file diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c2eaae011c8..06c8370bde2 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,6 +8037,24 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } + if (main->versionfile < 248 || (main->versionfile == 248 && main->subversionfile < 4)) { + Scene *sce; + World *wrld; + + /* Dome (Fisheye) default parameters */ + for (sce= main->scene.first; sce; sce= sce->id.next) { + sce->r.domeangle = 180; + sce->r.domemode = 1; + sce->r.domesize = 1.0f; + sce->r.domeres = 4; + sce->r.domeresbuf = 1.0f; + } + /* DBVT culling by default */ + for(wrld=main->world.first; wrld; wrld= wrld->id.next) { + wrld->mode |= WO_DBVT_CAMERA_CULLING; + } + } + /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init has to be in src/usiblender.c! */ @@ -8841,7 +8861,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 b8a734b6e94..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 { @@ -314,6 +315,14 @@ typedef struct RenderData { /* jpeg2000 */ short jp2_preset, jp2_depth; int rpad3; + + /* Dome variables */ + short domeres, domemode; + 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 aa072285556..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 = 190, 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, 224, 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); @@ -1814,6 +1814,7 @@ static uiBlock *framing_render_menu(void *arg_unused) * RAS_STEREO_ANAGLYPH 5 * RAS_STEREO_SIDEBYSIDE 6 * RAS_STEREO_VINTERLACE 7 + * RAS_STEREO_DOME 8 */ uiBlockBeginAlign(block); uiDefButS(block, ROW, 0, "No Stereo", xco, yco-=30, 88, 19, &(G.scene->r.stereomode), 7.0, 1.0, 0, 0, "Disables stereo"); @@ -1825,6 +1826,18 @@ static uiBlock *framing_render_menu(void *arg_unused) uiBlockEndAlign(block); + uiBlockBeginAlign(block); + uiDefButS(block, ROW, 0, "Dome", xco-=180, yco-=30, 88, 19, &(G.scene->r.stereomode), 7.0, 8.0, 0, 0, "Enables dome camera"); + uiDefButS(block, NUM, 0, "Ang:", xco+=90, yco, 88, 19, &G.scene->r.domeangle, 90.0, 250.0, 0, 0, "Angle (Aperture) of the Dome - it only works in mode 1"); + uiDefButS(block, NUM, 0, "Mode:", xco+=90, yco, 88, 19, &G.scene->r.domemode, 1.0, 3.0, 0, 0, "Dome mode - 1 fisheye, 2 truncated, 3 spherical panoramic"); + + 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); return block; diff --git a/source/blender/src/header_text.c b/source/blender/src/header_text.c index 9268642db2f..050ff192df6 100644 --- a/source/blender/src/header_text.c +++ b/source/blender/src/header_text.c @@ -173,6 +173,7 @@ void do_text_buttons(unsigned short event) BPY_clear_bad_scriptlinks(text); BPY_free_pyconstraint_links(text); free_text_controllers(text); + free_dome_warp_text(text); #endif unlink_text(text); free_libblock(&G.main->text, text); diff --git a/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp b/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp index 1c91ad784ac..fee5a4ad899 100644 --- a/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp +++ b/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp @@ -373,6 +373,10 @@ extern "C" void StartKetsjiShell(struct ScrArea *area, initVideoTexture(); #endif + //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, blscene->r.dometext); + if (sceneconverter) { // convert and add scene diff --git a/source/gameengine/GamePlayer/ghost/GPG_Application.cpp b/source/gameengine/GamePlayer/ghost/GPG_Application.cpp index 3432d498981..6ff46ca8200 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_Application.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_Application.cpp @@ -693,6 +693,11 @@ bool GPG_Application::startEngine(void) #ifdef WITH_FFMPEG initVideoTexture(); #endif + + //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_startScene->r.dometext); + // Set the GameLogic.globalDict from marshal'd data, so we can // load new blend files and keep data in GameLogic.globalDict loadGamePythonConfig(m_pyGlobalDictString, m_pyGlobalDictString_Length); diff --git a/source/gameengine/Ketsji/KX_Dome.cpp b/source/gameengine/Ketsji/KX_Dome.cpp new file mode 100644 index 00000000000..4ab0f93f687 --- /dev/null +++ b/source/gameengine/Ketsji/KX_Dome.cpp @@ -0,0 +1,1820 @@ +/* $Id$ +----------------------------------------------------------------------------- + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place - Suite 330, Boston, MA 02111-1307, USA, or go to +http://www.gnu.org/copyleft/lesser.txt. + +Contributor(s): Dalai Felinto + +This code is originally inspired on some of the ideas and codes from Paul Bourke. +Developed as part of a Research and Development project for SAT - La Société des arts technologiques. +----------------------------------------------------------------------------- +*/ + +#include "KX_Dome.h" + +#include <structmember.h> +#include <float.h> +#include <math.h> + +#include "DNA_scene_types.h" +#include "RAS_CameraData.h" +#include "BLI_arithb.h" + +#include "GL/glew.h" +#include "GL/glu.h" + +// constructor +KX_Dome::KX_Dome ( + RAS_ICanvas* canvas, + /// rasterizer + RAS_IRasterizer* rasterizer, + /// render tools + RAS_IRenderTools* rendertools, + /// engine + KX_KetsjiEngine* engine, + + float size, //size for adjustments + short res, //resolution of the mesh + short mode, //mode - fisheye, truncated, warped, panoramic, ... + short angle, + float resbuf, //size adjustment of the buffer + struct Text* warptext + +): + m_canvas(canvas), + m_rasterizer(rasterizer), + m_rendertools(rendertools), + m_engine(engine), + m_drawingmode(engine->GetDrawType()), + m_size(size), + m_resolution(res), + m_mode(mode), + m_angle(angle), + m_resbuffer(resbuf), + canvaswidth(-1), canvasheight(-1), + dlistSupported(false) +{ + 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); + + SetViewPort(viewport); + + switch(m_mode){ + case DOME_FISHEYE: + if (m_angle <= 180){ + cubetop.resize(1); + cubebottom.resize(1); + cubeleft.resize(2); + cuberight.resize(2); + + CreateMeshDome180(); + m_numfaces = 4; + }else if (m_angle > 180){ + cubetop.resize(2); + cubebottom.resize(2); + cubeleft.resize(2); + cubefront.resize(2); + cuberight.resize(2); + + CreateMeshDome250(); + m_numfaces = 5; + } break; + case DOME_TRUNCATED: + cubetop.resize(1); + cubebottom.resize(1); + cubeleft.resize(2); + cuberight.resize(2); + + m_angle = 180; + CreateMeshDome180(); + m_numfaces = 4; + break; + case DOME_PANORAM_SPH: + cubeleft.resize(2); + cubeleftback.resize(2); + cuberight.resize(2); + cuberightback.resize(2); + cubetop.resize(2); + cubebottom.resize(2); + + m_angle = 360; + CreateMeshPanorama(); + m_numfaces = 6; + break; + } + + m_numimages =(warp.usemesh?m_numfaces+1:m_numfaces); + + CalculateCameraOrientation(); + + CreateGLImages(); + + dlistSupported = CreateDL(); +} + +// destructor +KX_Dome::~KX_Dome (void) +{ + GLuint m_numimages = m_numfaces; + + ClearGLImages(); + + if(dlistSupported) + glDeleteLists(dlistId, (GLsizei) m_numimages); +} + +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_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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +void KX_Dome::ClearGLImages(void) +{ + glDeleteTextures(m_numimages, (GLuint*)&domefacesId); +/* + for (int i=0;i<m_numimages;i++) + if(glIsTexture(domefacesId[i])) + glDeleteTextures(1, (GLuint*)&domefacesId[i]); +*/ +} + +void KX_Dome::CalculateImageSize(void) +{ +/* +- determine the minimum buffer size +- reduce the buffer for better performace +- create a power of 2 texture bigger than the buffer +*/ + + canvaswidth = m_canvas->GetWidth(); + canvasheight = m_canvas->GetHeight(); + + m_buffersize = (canvaswidth > canvasheight?canvasheight:canvaswidth); + m_buffersize *= m_resbuffer; //reduce buffer size for better performance + + int i = 0; + 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); + } +} + +bool KX_Dome::CreateDL(){ + int i,j; + + dlistId = glGenLists((GLsizei) m_numimages); + if (dlistId != 0) { + if(m_mode == DOME_FISHEYE || m_mode == DOME_TRUNCATED){ + glNewList(dlistId, GL_COMPILE); + GLDrawTriangles(cubetop, nfacestop); + glEndList(); + + glNewList(dlistId+1, GL_COMPILE); + GLDrawTriangles(cubebottom, nfacesbottom); + glEndList(); + + glNewList(dlistId+2, GL_COMPILE); + GLDrawTriangles(cubeleft, nfacesleft); + glEndList(); + + glNewList(dlistId+3, GL_COMPILE); + GLDrawTriangles(cuberight, nfacesright); + glEndList(); + + if (m_angle > 180){ + glNewList(dlistId+4, GL_COMPILE); + GLDrawTriangles(cubefront, nfacesfront); + glEndList(); + } + } + else if (m_mode == DOME_PANORAM_SPH) + { + glNewList(dlistId, GL_COMPILE); + GLDrawTriangles(cubetop, nfacestop); + glEndList(); + + glNewList(dlistId+1, GL_COMPILE); + GLDrawTriangles(cubebottom, nfacesbottom); + glEndList(); + + glNewList(dlistId+2, GL_COMPILE); + GLDrawTriangles(cubeleft, nfacesleft); + glEndList(); + + glNewList(dlistId+3, GL_COMPILE); + GLDrawTriangles(cuberight, nfacesright); + glEndList(); + + glNewList(dlistId+4, GL_COMPILE); + GLDrawTriangles(cubeleftback, nfacesleftback); + glEndList(); + + glNewList(dlistId+5, GL_COMPILE); + GLDrawTriangles(cuberightback, nfacesrightback); + glEndList(); + } + + if(warp.usemesh){ + glNewList((dlistId + m_numfaces), GL_COMPILE); + GLDrawWarpQuads(); + glEndList(); + } + + //clearing the vectors + cubetop.clear(); + cubebottom.clear(); + cuberight.clear(); + cubeleft.clear(); + cubefront.clear(); + cubeleftback.clear(); + cuberightback.clear(); + warp.nodes.clear(); + + } else // genList failed + return false; + + return true; +} + +void KX_Dome::GLDrawTriangles(vector <DomeFace>& face, int nfaces) +{ + int i,j; + glBegin(GL_TRIANGLES); + for (i=0;i<nfaces;i++) { + for (j=0;j<3;j++) { + glTexCoord2f(face[i].u[j],face[i].v[j]); + glVertex3f((GLfloat)face[i].verts[j][0],(GLfloat)face[i].verts[j][1],(GLfloat)face[i].verts[j][2]); + } + } + glEnd(); +} + +void KX_Dome::GLDrawWarpQuads(void) +{ + int i, j, i2; + float uv_width = (float)(warp.bufferwidth-1) / warp.imagewidth; + float uv_height = (float)(warp.bufferheight-1) / 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) +{ +/* +1)- Define the faces of half of a cube + - each face is made out of 2 triangles +2) Subdivide the faces + - more resolution == more curved lines +3) Spherize the cube + - normalize the verts +4) Flatten onto xz plane + - transform it onto an equidistant spherical projection techniques to transform the sphere onto a dome image +*/ + int i,j; + float sqrt_2 = sqrt(2.0); + float uv_ratio = (float)(m_buffersize-1) / m_imagesize; + + m_radangle = m_angle * M_PI/180.0;//calculates the radians angle, used for flattening + + //creating faces for the env mapcube 180º Dome + // Top Face - just a triangle + cubetop[0].verts[0][0] = -sqrt_2 / 2.0; + cubetop[0].verts[0][1] = 0.0; + cubetop[0].verts[0][2] = 0.5; + cubetop[0].u[0] = 0.0; + cubetop[0].v[0] = uv_ratio; + + cubetop[0].verts[1][0] = 0.0; + cubetop[0].verts[1][1] = sqrt_2 / 2.0; + cubetop[0].verts[1][2] = 0.5; + cubetop[0].u[1] = 0.0; + cubetop[0].v[1] = 0.0; + + cubetop[0].verts[2][0] = sqrt_2 / 2.0; + cubetop[0].verts[2][1] = 0.0; + cubetop[0].verts[2][2] = 0.5; + cubetop[0].u[2] = uv_ratio; + cubetop[0].v[2] = 0.0; + + nfacestop = 1; + + /* Bottom face - just a triangle */ + cubebottom[0].verts[0][0] = -sqrt_2 / 2.0; + cubebottom[0].verts[0][1] = 0.0; + cubebottom[0].verts[0][2] = -0.5; + cubebottom[0].u[0] = uv_ratio; + cubebottom[0].v[0] = 0.0; + + cubebottom[0].verts[1][0] = sqrt_2 / 2.0; + cubebottom[0].verts[1][1] = 0; + cubebottom[0].verts[1][2] = -0.5; + cubebottom[0].u[1] = 0.0; + cubebottom[0].v[1] = uv_ratio; + + cubebottom[0].verts[2][0] = 0.0; + cubebottom[0].verts[2][1] = sqrt_2 / 2.0; + cubebottom[0].verts[2][2] = -0.5; + cubebottom[0].u[2] = 0.0; + cubebottom[0].v[2] = 0.0; + + nfacesbottom = 1; + + /* Left face - two triangles */ + + cubeleft[0].verts[0][0] = -sqrt_2 / 2.0; + cubeleft[0].verts[0][1] = .0; + cubeleft[0].verts[0][2] = -0.5; + cubeleft[0].u[0] = 0.0; + cubeleft[0].v[0] = 0.0; + + cubeleft[0].verts[1][0] = 0.0; + cubeleft[0].verts[1][1] = sqrt_2 / 2.0; + cubeleft[0].verts[1][2] = -0.5; + cubeleft[0].u[1] = uv_ratio; + cubeleft[0].v[1] = 0.0; + + cubeleft[0].verts[2][0] = -sqrt_2 / 2.0; + cubeleft[0].verts[2][1] = 0.0; + cubeleft[0].verts[2][2] = 0.5; + cubeleft[0].u[2] = 0.0; + cubeleft[0].v[2] = uv_ratio; + + //second triangle + cubeleft[1].verts[0][0] = -sqrt_2 / 2.0; + cubeleft[1].verts[0][1] = 0.0; + cubeleft[1].verts[0][2] = 0.5; + cubeleft[1].u[0] = 0.0; + cubeleft[1].v[0] = uv_ratio; + + cubeleft[1].verts[1][0] = 0.0; + cubeleft[1].verts[1][1] = sqrt_2 / 2.0; + cubeleft[1].verts[1][2] = -0.5; + cubeleft[1].u[1] = uv_ratio; + cubeleft[1].v[1] = 0.0; + + cubeleft[1].verts[2][0] = 0.0; + cubeleft[1].verts[2][1] = sqrt_2 / 2.0; + cubeleft[1].verts[2][2] = 0.5; + cubeleft[1].u[2] = uv_ratio; + cubeleft[1].v[2] = uv_ratio; + + nfacesleft = 2; + + /* Right face - two triangles */ + cuberight[0].verts[0][0] = 0.0; + cuberight[0].verts[0][1] = sqrt_2 / 2.0; + cuberight[0].verts[0][2] = -0.5; + cuberight[0].u[0] = 0.0; + cuberight[0].v[0] = 0.0; + + cuberight[0].verts[1][0] = sqrt_2 / 2.0; + cuberight[0].verts[1][1] = 0.0; + cuberight[0].verts[1][2] = -0.5; + cuberight[0].u[1] = uv_ratio; + cuberight[0].v[1] = 0.0; + + cuberight[0].verts[2][0] = sqrt_2 / 2.0; + cuberight[0].verts[2][1] = 0.0; + cuberight[0].verts[2][2] = 0.5; + cuberight[0].u[2] = uv_ratio; + cuberight[0].v[2] = uv_ratio; + + //second triangle + cuberight[1].verts[0][0] = 0.0; + cuberight[1].verts[0][1] = sqrt_2 / 2.0; + cuberight[1].verts[0][2] = -0.5; + cuberight[1].u[0] = 0.0; + cuberight[1].v[0] = 0.0; + + cuberight[1].verts[1][0] = sqrt_2 / 2.0; + cuberight[1].verts[1][1] = 0.0; + cuberight[1].verts[1][2] = 0.5; + cuberight[1].u[1] = uv_ratio; + cuberight[1].v[1] = uv_ratio; + + cuberight[1].verts[2][0] = 0.0; + cuberight[1].verts[2][1] = sqrt_2 / 2.0; + cuberight[1].verts[2][2] = 0.5; + cuberight[1].u[2] = 0.0; + cuberight[1].v[2] = uv_ratio; + + nfacesright = 2; + + //Refine a triangular mesh by bisecting each edge forms 3 new triangles for each existing triangle on each iteration + //Could be made more efficient for drawing if the triangles were ordered in a fan. Not that important since we are using DisplayLists + + for(i=0;i<m_resolution;i++){ + cubetop.resize(4*nfacestop); + SplitFace(cubetop,&nfacestop); + cubebottom.resize(4*nfacesbottom); + SplitFace(cubebottom,&nfacesbottom); + cubeleft.resize(4*nfacesleft); + SplitFace(cubeleft,&nfacesleft); + cuberight.resize(4*nfacesright); + SplitFace(cuberight,&nfacesright); + } + + // Turn into a hemisphere + for(j=0;j<3;j++){ + for(i=0;i<nfacestop;i++) + cubetop[i].verts[j].normalize(); + for(i=0;i<nfacesbottom;i++) + cubebottom[i].verts[j].normalize(); + for(i=0;i<nfacesleft;i++) + cubeleft[i].verts[j].normalize(); + for(i=0;i<nfacesright;i++) + cuberight[i].verts[j].normalize(); + } + + //flatten onto xz plane + for(i=0;i<nfacestop;i++) + FlattenDome(cubetop[i].verts); + for(i=0;i<nfacesbottom;i++) + FlattenDome(cubebottom[i].verts); + for(i=0;i<nfacesleft;i++) + FlattenDome(cubeleft[i].verts); + for(i=0;i<nfacesright;i++) + FlattenDome(cuberight[i].verts); + +} + +void KX_Dome::CreateMeshDome250(void) +{ +/* +1)- Define the faces of a cube without the back face + - each face is made out of 2 triangles +2) Subdivide the faces + - more resolution == more curved lines +3) Spherize the cube + - normalize the verts +4) Flatten onto xz plane + - transform it onto an equidistant spherical projection techniques to transform the sphere onto a dome image +*/ + + int i,j; + float uv_height, uv_base; + float verts_height; + + float rad_ang = m_angle * MT_PI / 180.0; + float uv_ratio = (float)(m_buffersize-1) / m_imagesize; + + m_radangle = m_angle * M_PI/180.0;//calculates the radians angle, used for flattening +/* +verts_height is the exactly needed height of the cube faces (not always 1.0). +When we want some horizontal information (e.g. for horizontal 220º domes) we don't need to create and tesselate the whole cube. +Therefore the lateral cube faces could be small, and the tesselate mesh would be completely used. +(if we always worked with verts_height = 1.0, we would be discarding a lot of the calculated and tesselated geometry). + +So I came out with this formula: +verts_height = tan((rad_ang/2) - (MT_PI/2))*sqrt(2.0); + +Here we take half the sphere(rad_ang/2) and subtract a quarter of it (MT_PI/2) +Therefore we have the lenght in radians of the dome/sphere over the horizon. +Once we take the tangent of that angle, you have the verts coordinate corresponding to the verts on the side faces. +Then we need to multiply it by sqrt(2.0) to get the coordinate of the verts on the diagonal of the original cube. +*/ + verts_height = tan((rad_ang/2) - (MT_PI/2))*sqrt(2.0); + + uv_height = uv_ratio * ((verts_height/2) + 0.5); + uv_base = uv_ratio * (1.0 - ((verts_height/2) + 0.5)); + + //creating faces for the env mapcube 180º Dome + // Front Face - 2 triangles + cubefront[0].verts[0][0] =-1.0; + cubefront[0].verts[0][1] = 1.0; + cubefront[0].verts[0][2] =-1.0; + cubefront[0].u[0] = 0.0; + cubefront[0].v[0] = 0.0; + + cubefront[0].verts[1][0] = 1.0; + cubefront[0].verts[1][1] = 1.0; + cubefront[0].verts[1][2] = 1.0; + cubefront[0].u[1] = uv_ratio; + cubefront[0].v[1] = uv_ratio; + + cubefront[0].verts[2][0] =-1.0; + cubefront[0].verts[2][1] = 1.0; + cubefront[0].verts[2][2] = 1.0; + cubefront[0].u[2] = 0.0; + cubefront[0].v[2] = uv_ratio; + + //second triangle + cubefront[1].verts[0][0] = 1.0; + cubefront[1].verts[0][1] = 1.0; + cubefront[1].verts[0][2] = 1.0; + cubefront[1].u[0] = uv_ratio; + cubefront[1].v[0] = uv_ratio; + + cubefront[1].verts[1][0] =-1.0; + cubefront[1].verts[1][1] = 1.0; + cubefront[1].verts[1][2] =-1.0; + cubefront[1].u[1] = 0.0; + cubefront[1].v[1] = 0.0; + + cubefront[1].verts[2][0] = 1.0; + cubefront[1].verts[2][1] = 1.0; + cubefront[1].verts[2][2] =-1.0; + cubefront[1].u[2] = uv_ratio; + cubefront[1].v[2] = 0.0; + + nfacesfront = 2; + + // Left Face - 2 triangles + cubeleft[0].verts[0][0] =-1.0; + cubeleft[0].verts[0][1] = 1.0; + cubeleft[0].verts[0][2] =-1.0; + cubeleft[0].u[0] = uv_ratio; + cubeleft[0].v[0] = 0.0; + + cubeleft[0].verts[1][0] =-1.0; + cubeleft[0].verts[1][1] =-verts_height; + cubeleft[0].verts[1][2] = 1.0; + cubeleft[0].u[1] = uv_base; + cubeleft[0].v[1] = uv_ratio; + + cubeleft[0].verts[2][0] =-1.0; + cubeleft[0].verts[2][1] =-verts_height; + cubeleft[0].verts[2][2] =-1.0; + cubeleft[0].u[2] = uv_base; + cubeleft[0].v[2] = 0.0; + + //second triangle + cubeleft[1].verts[0][0] =-1.0; + cubeleft[1].verts[0][1] =-verts_height; + cubeleft[1].verts[0][2] = 1.0; + cubeleft[1].u[0] = uv_base; + cubeleft[1].v[0] = uv_ratio; + + cubeleft[1].verts[1][0] =-1.0; + cubeleft[1].verts[1][1] = 1.0; + cubeleft[1].verts[1][2] =-1.0; + cubeleft[1].u[1] = uv_ratio; + cubeleft[1].v[1] = 0.0; + + cubeleft[1].verts[2][0] =-1.0; + cubeleft[1].verts[2][1] = 1.0; + cubeleft[1].verts[2][2] = 1.0; + cubeleft[1].u[2] = uv_ratio; + cubeleft[1].v[2] = uv_ratio; + + nfacesleft = 2; + + // right Face - 2 triangles + cuberight[0].verts[0][0] = 1.0; + cuberight[0].verts[0][1] = 1.0; + cuberight[0].verts[0][2] = 1.0; + cuberight[0].u[0] = 0.0; + cuberight[0].v[0] = uv_ratio; + + cuberight[0].verts[1][0] = 1.0; + cuberight[0].verts[1][1] =-verts_height; + cuberight[0].verts[1][2] =-1.0; + cuberight[0].u[1] = uv_height; + cuberight[0].v[1] = 0.0; + + cuberight[0].verts[2][0] = 1.0; + cuberight[0].verts[2][1] =-verts_height; + cuberight[0].verts[2][2] = 1.0; + cuberight[0].u[2] = uv_height; + cuberight[0].v[2] = uv_ratio; + + //second triangle + cuberight[1].verts[0][0] = 1.0; + cuberight[1].verts[0][1] =-verts_height; + cuberight[1].verts[0][2] =-1.0; + cuberight[1].u[0] = uv_height; + cuberight[1].v[0] = 0.0; + + cuberight[1].verts[1][0] = 1.0; + cuberight[1].verts[1][1] = 1.0; + cuberight[1].verts[1][2] = 1.0; + cuberight[1].u[1] = 0.0; + cuberight[1].v[1] = uv_ratio; + + cuberight[1].verts[2][0] = 1.0; + cuberight[1].verts[2][1] = 1.0; + cuberight[1].verts[2][2] =-1.0; + cuberight[1].u[2] = 0.0; + cuberight[1].v[2] = 0.0; + + nfacesright = 2; + + // top Face - 2 triangles + cubetop[0].verts[0][0] =-1.0; + cubetop[0].verts[0][1] = 1.0; + cubetop[0].verts[0][2] = 1.0; + cubetop[0].u[0] = 0.0; + cubetop[0].v[0] = 0.0; + + cubetop[0].verts[1][0] = 1.0; + cubetop[0].verts[1][1] =-verts_height; + cubetop[0].verts[1][2] = 1.0; + cubetop[0].u[1] = uv_ratio; + cubetop[0].v[1] = uv_height; + + cubetop[0].verts[2][0] =-1.0; + cubetop[0].verts[2][1] =-verts_height; + cubetop[0].verts[2][2] = 1.0; + cubetop[0].u[2] = 0.0; + cubetop[0].v[2] = uv_height; + + //second triangle + cubetop[1].verts[0][0] = 1.0; + cubetop[1].verts[0][1] =-verts_height; + cubetop[1].verts[0][2] = 1.0; + cubetop[1].u[0] = uv_ratio; + cubetop[1].v[0] = uv_height; + + cubetop[1].verts[1][0] =-1.0; + cubetop[1].verts[1][1] = 1.0; + cubetop[1].verts[1][2] = 1.0; + cubetop[1].u[1] = 0.0; + cubetop[1].v[1] = 0.0; + + cubetop[1].verts[2][0] = 1.0; + cubetop[1].verts[2][1] = 1.0; + cubetop[1].verts[2][2] = 1.0; + cubetop[1].u[2] = uv_ratio; + cubetop[1].v[2] = 0.0; + + nfacestop = 2; + + // bottom Face - 2 triangles + cubebottom[0].verts[0][0] =-1.0; + cubebottom[0].verts[0][1] =-verts_height; + cubebottom[0].verts[0][2] =-1.0; + cubebottom[0].u[0] = 0.0; + cubebottom[0].v[0] = uv_base; + + cubebottom[0].verts[1][0] = 1.0; + cubebottom[0].verts[1][1] = 1.0; + cubebottom[0].verts[1][2] =-1.0; + cubebottom[0].u[1] = uv_ratio; + cubebottom[0].v[1] = uv_ratio; + + cubebottom[0].verts[2][0] =-1.0; + cubebottom[0].verts[2][1] = 1.0; + cubebottom[0].verts[2][2] =-1.0; + cubebottom[0].u[2] = 0.0; + cubebottom[0].v[2] = uv_ratio; + + //second triangle + cubebottom[1].verts[0][0] = 1.0; + cubebottom[1].verts[0][1] = 1.0; + cubebottom[1].verts[0][2] =-1.0; + cubebottom[1].u[0] = uv_ratio; + cubebottom[1].v[0] = uv_ratio; + + cubebottom[1].verts[1][0] =-1.0; + cubebottom[1].verts[1][1] =-verts_height; + cubebottom[1].verts[1][2] =-1.0; + cubebottom[1].u[1] = 0.0; + cubebottom[1].v[1] = uv_base; + + cubebottom[1].verts[2][0] = 1.0; + cubebottom[1].verts[2][1] =-verts_height; + cubebottom[1].verts[2][2] =-1.0; + cubebottom[1].u[2] = uv_ratio; + cubebottom[1].v[2] = uv_base; + + nfacesbottom = 2; + + //Refine a triangular mesh by bisecting each edge forms 3 new triangles for each existing triangle on each iteration + //It could be made more efficient for drawing if the triangles were ordered in a strip! + + for(i=0;i<m_resolution;i++){ + cubefront.resize(4*nfacesfront); + SplitFace(cubefront,&nfacesfront); + cubetop.resize(4*nfacestop); + SplitFace(cubetop,&nfacestop); + cubebottom.resize(4*nfacesbottom); + SplitFace(cubebottom,&nfacesbottom); + cubeleft.resize(4*nfacesleft); + SplitFace(cubeleft,&nfacesleft); + cuberight.resize(4*nfacesright); + SplitFace(cuberight,&nfacesright); + } + + // Turn into a hemisphere/sphere + for(j=0;j<3;j++){ + for(i=0;i<nfacesfront;i++) + cubefront[i].verts[j].normalize(); + for(i=0;i<nfacestop;i++) + cubetop[i].verts[j].normalize(); + for(i=0;i<nfacesbottom;i++) + cubebottom[i].verts[j].normalize(); + for(i=0;i<nfacesleft;i++) + cubeleft[i].verts[j].normalize(); + for(i=0;i<nfacesright;i++) + cuberight[i].verts[j].normalize(); + } + + //flatten onto xz plane + for(i=0;i<nfacesfront;i++) + FlattenDome(cubefront[i].verts); + for(i=0;i<nfacestop;i++) + FlattenDome(cubetop[i].verts); + for(i=0;i<nfacesbottom;i++) + FlattenDome(cubebottom[i].verts); + for(i=0;i<nfacesleft;i++) + FlattenDome(cubeleft[i].verts); + for(i=0;i<nfacesright;i++) + FlattenDome(cuberight[i].verts); +} + +void KX_Dome::CreateMeshPanorama(void) +{ +/* +1)- Define the faces of a cube without the top and bottom faces + - each face is made out of 2 triangles +2) Subdivide the faces + - more resolution == more curved lines +3) Spherize the cube + - normalize the verts t +4) Flatten onto xz plane + - use spherical projection techniques to transform the sphere onto a flat panorama +*/ + int i,j; + + float sqrt_2 = sqrt(2.0); + float uv_ratio = (float)(m_buffersize-1) / m_imagesize; + + /* Top face - two triangles */ + cubetop[0].verts[0][0] = -sqrt_2; + cubetop[0].verts[0][1] = 0.0; + cubetop[0].verts[0][2] = 1.0; + cubetop[0].u[0] = 0.0; + cubetop[0].v[0] = uv_ratio; + + cubetop[0].verts[1][0] = 0.0; + cubetop[0].verts[1][1] = sqrt_2; + cubetop[0].verts[1][2] = 1.0; + cubetop[0].u[1] = 0.0; + cubetop[0].v[1] = 0.0; + + //second triangle + cubetop[0].verts[2][0] = sqrt_2; + cubetop[0].verts[2][1] = 0.0; + cubetop[0].verts[2][2] = 1.0; + cubetop[0].u[2] = uv_ratio; + cubetop[0].v[2] = 0.0; + + cubetop[1].verts[0][0] = sqrt_2; + cubetop[1].verts[0][1] = 0.0; + cubetop[1].verts[0][2] = 1.0; + cubetop[1].u[0] = uv_ratio; + cubetop[1].v[0] = 0.0; + + cubetop[1].verts[1][0] = 0.0; + cubetop[1].verts[1][1] = -sqrt_2; + cubetop[1].verts[1][2] = 1.0; + cubetop[1].u[1] = uv_ratio; + cubetop[1].v[1] = uv_ratio; + + cubetop[1].verts[2][0] = -sqrt_2; + cubetop[1].verts[2][1] = 0.0; + cubetop[1].verts[2][2] = 1.0; + cubetop[1].u[2] = 0.0; + cubetop[1].v[2] = uv_ratio; + + nfacestop = 2; + + /* Bottom face - two triangles */ + cubebottom[0].verts[0][0] = -sqrt_2; + cubebottom[0].verts[0][1] = 0.0; + cubebottom[0].verts[0][2] = -1.0; + cubebottom[0].u[0] = uv_ratio; + cubebottom[0].v[0] = 0.0; + + cubebottom[0].verts[1][0] = sqrt_2; + cubebottom[0].verts[1][1] = 0.0; + cubebottom[0].verts[1][2] = -1.0; + cubebottom[0].u[1] = 0.0; + cubebottom[0].v[1] = uv_ratio; + + cubebottom[0].verts[2][0] = 0.0; + cubebottom[0].verts[2][1] = sqrt_2; + cubebottom[0].verts[2][2] = -1.0; + cubebottom[0].u[2] = 0.0; + cubebottom[0].v[2] = 0.0; + + //second triangle + cubebottom[1].verts[0][0] = sqrt_2; + cubebottom[1].verts[0][1] = 0.0; + cubebottom[1].verts[0][2] = -1.0; + cubebottom[1].u[0] = 0.0; + cubebottom[1].v[0] = uv_ratio; + + cubebottom[1].verts[1][0] = -sqrt_2; + cubebottom[1].verts[1][1] = 0.0; + cubebottom[1].verts[1][2] = -1.0; + cubebottom[1].u[1] = uv_ratio; + cubebottom[1].v[1] = 0.0; + + cubebottom[1].verts[2][0] = 0.0; + cubebottom[1].verts[2][1] = -sqrt_2; + cubebottom[1].verts[2][2] = -1.0; + cubebottom[1].u[2] = uv_ratio; + cubebottom[1].v[2] = uv_ratio; + + nfacesbottom = 2; + + /* Left Back (135º) face - two triangles */ + + cubeleftback[0].verts[0][0] = 0; + cubeleftback[0].verts[0][1] = -sqrt_2; + cubeleftback[0].verts[0][2] = -1.0; + cubeleftback[0].u[0] = 0; + cubeleftback[0].v[0] = 0; + + cubeleftback[0].verts[1][0] = -sqrt_2; + cubeleftback[0].verts[1][1] = 0; + cubeleftback[0].verts[1][2] = -1.0; + cubeleftback[0].u[1] = uv_ratio; + cubeleftback[0].v[1] = 0; + + cubeleftback[0].verts[2][0] = 0; + cubeleftback[0].verts[2][1] = -sqrt_2; + cubeleftback[0].verts[2][2] = 1.0; + cubeleftback[0].u[2] = 0; + cubeleftback[0].v[2] = uv_ratio; + + //second triangle + cubeleftback[1].verts[0][0] = 0; + cubeleftback[1].verts[0][1] = -sqrt_2; + cubeleftback[1].verts[0][2] = 1.0; + cubeleftback[1].u[0] = 0; + cubeleftback[1].v[0] = uv_ratio; + + cubeleftback[1].verts[1][0] = -sqrt_2; + cubeleftback[1].verts[1][1] = 0; + cubeleftback[1].verts[1][2] = -1.0; + cubeleftback[1].u[1] = uv_ratio; + cubeleftback[1].v[1] = 0; + + cubeleftback[1].verts[2][0] = -sqrt_2; + cubeleftback[1].verts[2][1] = 0; + cubeleftback[1].verts[2][2] = 1.0; + cubeleftback[1].u[2] = uv_ratio; + cubeleftback[1].v[2] = uv_ratio; + + nfacesleftback = 2; + + /* Left face - two triangles */ + + cubeleft[0].verts[0][0] = -sqrt_2; + cubeleft[0].verts[0][1] = 0; + cubeleft[0].verts[0][2] = -1.0; + cubeleft[0].u[0] = 0; + cubeleft[0].v[0] = 0; + + cubeleft[0].verts[1][0] = 0; + cubeleft[0].verts[1][1] = sqrt_2; + cubeleft[0].verts[1][2] = -1.0; + cubeleft[0].u[1] = uv_ratio; + cubeleft[0].v[1] = 0; + + cubeleft[0].verts[2][0] = -sqrt_2; + cubeleft[0].verts[2][1] = 0; + cubeleft[0].verts[2][2] = 1.0; + cubeleft[0].u[2] = 0; + cubeleft[0].v[2] = uv_ratio; + + //second triangle + cubeleft[1].verts[0][0] = -sqrt_2; + cubeleft[1].verts[0][1] = 0; + cubeleft[1].verts[0][2] = 1.0; + cubeleft[1].u[0] = 0; + cubeleft[1].v[0] = uv_ratio; + + cubeleft[1].verts[1][0] = 0; + cubeleft[1].verts[1][1] = sqrt_2; + cubeleft[1].verts[1][2] = -1.0; + cubeleft[1].u[1] = uv_ratio; + cubeleft[1].v[1] = 0; + + cubeleft[1].verts[2][0] = 0; + cubeleft[1].verts[2][1] = sqrt_2; + cubeleft[1].verts[2][2] = 1.0; + cubeleft[1].u[2] = uv_ratio; + cubeleft[1].v[2] = uv_ratio; + + nfacesleft = 2; + + /* Right face - two triangles */ + cuberight[0].verts[0][0] = 0; + cuberight[0].verts[0][1] = sqrt_2; + cuberight[0].verts[0][2] = -1.0; + cuberight[0].u[0] = 0; + cuberight[0].v[0] = 0; + + cuberight[0].verts[1][0] = sqrt_2; + cuberight[0].verts[1][1] = 0; + cuberight[0].verts[1][2] = -1.0; + cuberight[0].u[1] = uv_ratio; + cuberight[0].v[1] = 0; + + cuberight[0].verts[2][0] = sqrt_2; + cuberight[0].verts[2][1] = 0; + cuberight[0].verts[2][2] = 1.0; + cuberight[0].u[2] = uv_ratio; + cuberight[0].v[2] = uv_ratio; + + //second triangle + cuberight[1].verts[0][0] = 0; + cuberight[1].verts[0][1] = sqrt_2; + cuberight[1].verts[0][2] = -1.0; + cuberight[1].u[0] = 0; + cuberight[1].v[0] = 0; + + cuberight[1].verts[1][0] = sqrt_2; + cuberight[1].verts[1][1] = 0; + cuberight[1].verts[1][2] = 1.0; + cuberight[1].u[1] = uv_ratio; + cuberight[1].v[1] = uv_ratio; + + cuberight[1].verts[2][0] = 0; + cuberight[1].verts[2][1] = sqrt_2; + cuberight[1].verts[2][2] = 1.0; + cuberight[1].u[2] = 0; + cuberight[1].v[2] = uv_ratio; + + nfacesright = 2; + + /* Right Back (-135º) face - two triangles */ + cuberightback[0].verts[0][0] = sqrt_2; + cuberightback[0].verts[0][1] = 0; + cuberightback[0].verts[0][2] = -1.0; + cuberightback[0].u[0] = 0; + cuberightback[0].v[0] = 0; + + cuberightback[0].verts[1][0] = 0; + cuberightback[0].verts[1][1] = -sqrt_2; + cuberightback[0].verts[1][2] = -1.0; + cuberightback[0].u[1] = uv_ratio; + cuberightback[0].v[1] = 0; + + cuberightback[0].verts[2][0] = 0; + cuberightback[0].verts[2][1] = -sqrt_2; + cuberightback[0].verts[2][2] = 1.0; + cuberightback[0].u[2] = uv_ratio; + cuberightback[0].v[2] = uv_ratio; + + //second triangle + cuberightback[1].verts[0][0] = sqrt_2; + cuberightback[1].verts[0][1] = 0; + cuberightback[1].verts[0][2] = -1.0; + cuberightback[1].u[0] = 0; + cuberightback[1].v[0] = 0; + + cuberightback[1].verts[1][0] = 0; + cuberightback[1].verts[1][1] = -sqrt_2; + cuberightback[1].verts[1][2] = 1.0; + cuberightback[1].u[1] = uv_ratio; + cuberightback[1].v[1] = uv_ratio; + + cuberightback[1].verts[2][0] = sqrt_2; + cuberightback[1].verts[2][1] = 0; + cuberightback[1].verts[2][2] = 1.0; + cuberightback[1].u[2] = 0; + cuberightback[1].v[2] = uv_ratio; + + nfacesrightback = 2; + + // Subdivide the faces + for(i=0;i<m_resolution;i++) + { + cubetop.resize(4*nfacestop); + SplitFace(cubetop,&nfacestop); + + cubebottom.resize(4*nfacesbottom); + SplitFace(cubebottom,&nfacesbottom); + + cubeleft.resize(4*nfacesleft); + SplitFace(cubeleft,&nfacesleft); + + cuberight.resize(4*nfacesright); + SplitFace(cuberight,&nfacesright); + + cubeleftback.resize(4*nfacesleftback); + SplitFace(cubeleftback,&nfacesleftback); + + cuberightback.resize(4*nfacesrightback); + SplitFace(cuberightback,&nfacesrightback); + } + + // Spherize the cube + for(j=0;j<3;j++) + { + for(i=0;i<nfacestop;i++) + cubetop[i].verts[j].normalize(); + + for(i=0;i<nfacesbottom;i++) + cubebottom[i].verts[j].normalize(); + + for(i=0;i<nfacesleftback;i++) + cubeleftback[i].verts[j].normalize(); + + for(i=0;i<nfacesleft;i++) + cubeleft[i].verts[j].normalize(); + + for(i=0;i<nfacesright;i++) + cuberight[i].verts[j].normalize(); + + for(i=0;i<nfacesrightback;i++) + cuberightback[i].verts[j].normalize(); + } + + //Flatten onto xz plane + for(i=0;i<nfacesleftback;i++) + FlattenPanorama(cubeleftback[i].verts); + + for(i=0;i<nfacesleft;i++) + FlattenPanorama(cubeleft[i].verts); + + for(i=0;i<nfacesright;i++) + FlattenPanorama(cuberight[i].verts); + + for(i=0;i<nfacesrightback;i++) + FlattenPanorama(cuberightback[i].verts); + + for(i=0;i<nfacestop;i++) + FlattenPanorama(cubetop[i].verts); + + for(i=0;i<nfacesbottom;i++) + FlattenPanorama(cubebottom[i].verts); +} + +void KX_Dome::FlattenDome(MT_Vector3 verts[3]) +{ + double phi, r; + + for (int i=0;i<3;i++){ + r = atan2(sqrt(verts[i][0]*verts[i][0] + verts[i][2]*verts[i][2]), verts[i][1]); + r /= m_radangle/2; + + phi = atan2(verts[i][2], verts[i][0]); + + verts[i][0] = r * cos(phi); + verts[i][1] = 0; + verts[i][2] = r * sin(phi); + + if (r > 1.0){ + //round the border + verts[i][0] = cos(phi); + verts[i][1] = -3.0; + verts[i][2] = sin(phi); + } + } +} + +void KX_Dome::FlattenPanorama(MT_Vector3 verts[3]) +{ +// it creates a full spherical panoramic (360º) + int i; + double phi; + bool edge=false; + + for (i=0;i<3;i++){ + phi = atan2(verts[i][1], verts[i][0]); + phi *= -1.0; //flipping + + if (phi == -MT_PI) //It's on the edge + edge=true; + + verts[i][0] = phi / MT_PI; + verts[i][1] = 0; + + verts[i][2] = atan2(verts[i][2], 1.0); + verts[i][2] /= MT_PI / 2; + } + if(edge){ + bool right=false; + + for (i=0;i<3;i++){ + if(fmod(verts[i][0],1.0) > 0.0){ + right=true; + break; + } + } + if(right){ + for (i=0;i<3;i++){ + if(verts[i][0] < 0.0) + verts[i][0] *= -1.0; + } + } + } +} + +void KX_Dome::SplitFace(vector <DomeFace>& face, int *nfaces) +{ + int i; + int n1, n2; + + n1 = n2 = *nfaces; + + for(i=0;i<n1;i++){ + + face[n2].verts[0] = (face[i].verts[0] + face[i].verts[1]) /2; + face[n2].verts[1] = face[i].verts[1]; + face[n2].verts[2] = (face[i].verts[1] + face[i].verts[2]) /2; + face[n2].u[0] = (face[i].u[0] + face[i].u[1]) /2; + face[n2].u[1] = face[i].u[1]; + face[n2].u[2] = (face[i].u[1] + face[i].u[2]) /2; + face[n2].v[0] = (face[i].v[0] + face[i].v[1]) /2; + face[n2].v[1] = face[i].v[1]; + face[n2].v[2] = (face[i].v[1] + face[i].v[2]) /2; + + face[n2+1].verts[0] = (face[i].verts[1] + face[i].verts[2]) /2; + face[n2+1].verts[1] = face[i].verts[2]; + face[n2+1].verts[2] = (face[i].verts[2] + face[i].verts[0]) /2; + face[n2+1].u[0] = (face[i].u[1] + face[i].u[2]) /2; + face[n2+1].u[1] = face[i].u[2]; + face[n2+1].u[2] = (face[i].u[2] + face[i].u[0]) /2; + face[n2+1].v[0] = (face[i].v[1] + face[i].v[2]) /2; + face[n2+1].v[1] = face[i].v[2]; + face[n2+1].v[2] = (face[i].v[2] + face[i].v[0]) /2; + + face[n2+2].verts[0] = (face[i].verts[0] + face[i].verts[1]) /2; + face[n2+2].verts[1] = (face[i].verts[1] + face[i].verts[2]) /2; + face[n2+2].verts[2] = (face[i].verts[2] + face[i].verts[0]) /2; + face[n2+2].u[0] = (face[i].u[0] + face[i].u[1]) /2; + face[n2+2].u[1] = (face[i].u[1] + face[i].u[2]) /2; + face[n2+2].u[2] = (face[i].u[2] + face[i].u[0]) /2; + face[n2+2].v[0] = (face[i].v[0] + face[i].v[1]) /2; + face[n2+2].v[1] = (face[i].v[1] + face[i].v[2]) /2; + face[n2+2].v[2] = (face[i].v[2] + face[i].v[0]) /2; + + //face[i].verts[0] = face[i].verts[0] ; + face[i].verts[1] = (face[i].verts[0] + face[i].verts[1]) /2; + face[i].verts[2] = (face[i].verts[0] + face[i].verts[2]) /2; + //face[i].u[0] = face[i].u[0]; + face[i].u[1] = (face[i].u[0] + face[i].u[1]) /2; + face[i].u[2] = (face[i].u[0] + face[i].u[2]) /2; + //face[i].v[0] = face[i].v[0] ; + face[i].v[1] = (face[i].v[0] + face[i].v[1]) /2; + face[i].v[2] = (face[i].v[0] + face[i].v[2]) /2; + + n2 += 3; // number of faces + } + *nfaces = n2; +} + +void KX_Dome::CalculateFrustum(KX_Camera * cam) +{ + /* + // manually creating a 90º Field of View Frustum + + the original formula: + top = tan(fov*3.14159/360.0) * near [for fov in degrees] + fov*0.5 = arctan ((top-bottom)*0.5 / near) [for fov in radians] + bottom = -top + left = aspect * bottom + right = aspect * top + + // the equivalent GLU call is: + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(90.0,1.0,cam->GetCameraNear(),cam->GetCameraFar()); + */ + + RAS_FrameFrustum m_frustrum; //90 deg. Frustum + + m_frustrum.camnear = cam->GetCameraNear(); + m_frustrum.camfar = cam->GetCameraFar(); + +// float top = tan(90.0*MT_PI/360.0) * m_frustrum.camnear; + float top = m_frustrum.camnear; // for deg = 90º, tan = 1 + + m_frustrum.x1 = -top; + m_frustrum.x2 = top; + m_frustrum.y1 = -top; + m_frustrum.y2 = top; + + m_projmat = m_rasterizer->GetFrustumMatrix( + m_frustrum.x1, m_frustrum.x2, m_frustrum.y1, m_frustrum.y2, m_frustrum.camnear, m_frustrum.camfar); + +} + +void KX_Dome::CalculateCameraOrientation() +{ +/* +Uses 4 cameras for angles up to 180º +Uses 5 cameras for angles up to 250º +Uses 6 cameras for angles up to 360º +*/ + float deg45 = MT_PI / 4; + MT_Scalar c = cos(deg45); + MT_Scalar s = sin(deg45); + + if ((m_mode == DOME_FISHEYE && m_angle <= 180)|| m_mode == DOME_TRUNCATED){ + + m_locRot[0] = MT_Matrix3x3( // 90º - Top + c, -s, 0.0, + 0.0,0.0, -1.0, + s, c, 0.0); + + m_locRot[1] = MT_Matrix3x3( // 90º - Bottom + -s, c, 0.0, + 0.0,0.0, 1.0, + s, c, 0.0); + + m_locRot[2] = MT_Matrix3x3( // 45º - Left + c, 0.0, s, + 0, 1.0, 0.0, + -s, 0.0, c); + + m_locRot[3] = MT_Matrix3x3( // 45º - Right + c, 0.0, -s, + 0.0, 1.0, 0.0, + s, 0.0, c); + + } else if ((m_mode == DOME_FISHEYE && m_angle > 180)){ + + m_locRot[0] = MT_Matrix3x3( // 90º - Top + 1.0, 0.0, 0.0, + 0.0, 0.0,-1.0, + 0.0, 1.0, 0.0); + + m_locRot[1] = MT_Matrix3x3( // 90º - Bottom + 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, + 0.0,-1.0, 0.0); + + m_locRot[2] = MT_Matrix3x3( // -90º - Left + 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, + -1.0, 0.0, 0.0); + + m_locRot[3] = MT_Matrix3x3( // 90º - Right + 0.0, 0.0,-1.0, + 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0); + + m_locRot[4] = MT_Matrix3x3( // 0º - Front + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + + m_locRot[5] = MT_Matrix3x3( // 180º - Back - NOT USING + -1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0,-1.0); + + } else if (m_mode == DOME_PANORAM_SPH){ + + m_locRot[0] = MT_Matrix3x3( // Top + c, s, 0.0, + 0.0,0.0, -1.0, + -s, c, 0.0); + + m_locRot[1] = MT_Matrix3x3( // Bottom + c, s, 0.0, + 0.0 ,0.0, 1.0, + s, -c, 0.0); + + m_locRot[2] = MT_Matrix3x3( // 45º - Left + -s, 0.0, c, + 0, 1.0, 0.0, + -c, 0.0, -s); + + m_locRot[3] = MT_Matrix3x3( // 45º - Right + c, 0.0, s, + 0, 1.0, 0.0, + -s, 0.0, c); + + m_locRot[4] = MT_Matrix3x3( // 135º - LeftBack + -s, 0.0, -c, + 0.0, 1.0, 0.0, + c, 0.0, -s); + + m_locRot[5] = MT_Matrix3x3( // 135º - RightBack + c, 0.0, -s, + 0.0, 1.0, 0.0, + s, 0.0, c); + } +} + +void KX_Dome::RotateCamera(KX_Camera* cam, int i) +{ +// I'm not using it, I'm doing inline calls for these commands +// but it's nice to have it here in case I need it + + MT_Matrix3x3 camori = cam->GetSGNode()->GetLocalOrientation(); + + cam->NodeSetLocalOrientation(camori*m_locRot[i]); + cam->NodeUpdateGS(0.f); + + MT_Transform camtrans(cam->GetWorldToCamera()); + MT_Matrix4x4 viewmat(camtrans); + m_rasterizer->SetViewMatrix(viewmat, cam->NodeGetWorldPosition(), + cam->GetCameraLocation(), cam->GetCameraOrientation()); + cam->SetModelviewMatrix(viewmat); + + // restore the original orientation + cam->NodeSetLocalOrientation(camori); + cam->NodeUpdateGS(0.f); +} + +void KX_Dome::Draw(void) +{ + + switch(m_mode){ + case DOME_FISHEYE: + DrawDomeFisheye(); + break; + case DOME_TRUNCATED: + DrawDomeFisheye(); + break; + case DOME_PANORAM_SPH: + 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, warp.bufferheight); + DrawDomeWarped(); + } +} + +void KX_Dome::DrawDomeFisheye(void) +{ + int i,j; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // Making the viewport always square + + int can_width = m_viewport.GetRight(); + int can_height = m_viewport.GetTop(); + + float ortho_width, ortho_height; + + 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); + } else { + if (can_width < can_height){ + ortho_width = 1.0; + ortho_height = (float)can_height/can_width; + }else{ + ortho_width = (float)can_width/can_height; + ortho_height = 1.0; + } + + 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(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt(0.0,-1.0,0.0, 0.0,0.0,0.0, 0.0,0.0,1.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); + + if (dlistSupported){ + for(i=0;i<m_numfaces;i++){ + glBindTexture(GL_TEXTURE_2D, domefacesId[i]); + glCallList(dlistId+i); + } + } + else { // DisplayLists not supported + // top triangle + glBindTexture(GL_TEXTURE_2D, domefacesId[0]); + GLDrawTriangles(cubetop, nfacestop); + + // bottom triangle + glBindTexture(GL_TEXTURE_2D, domefacesId[1]); + GLDrawTriangles(cubebottom, nfacesbottom); + + // left triangle + glBindTexture(GL_TEXTURE_2D, domefacesId[2]); + GLDrawTriangles(cubeleft, nfacesleft); + + // right triangle + glBindTexture(GL_TEXTURE_2D, domefacesId[3]); + GLDrawTriangles(cuberight, nfacesright); + + if (m_angle > 180){ + // front triangle + glBindTexture(GL_TEXTURE_2D, domefacesId[4]); + GLDrawTriangles(cubefront, nfacesfront); + } + } + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +} + +void KX_Dome::DrawPanorama(void) +{ + int i,j; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // Making the viewport always square + + int can_width = m_viewport.GetRight(); + int can_height = m_viewport.GetTop(); + + float ortho_height = 1.0; + float ortho_width = 1.0; + + if (warp.usemesh) + glOrtho((-1.0), 1.0, (-0.5), 0.5, -20.0, 10.0); //stretch the image to reduce resolution lost + + 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(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt(0.0,-1.0,0.0, 0.0,0.0,0.0, 0.0,0.0,1.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); + + if (dlistSupported){ + for(i=0;i<m_numfaces;i++){ + glBindTexture(GL_TEXTURE_2D, domefacesId[i]); + glCallList(dlistId+i); + } + } + else { + // domefacesId[4] => (top) + glBindTexture(GL_TEXTURE_2D, domefacesId[0]); + GLDrawTriangles(cubetop, nfacestop); + + // domefacesId[5] => (bottom) + glBindTexture(GL_TEXTURE_2D, domefacesId[1]); + GLDrawTriangles(cubebottom, nfacesbottom); + + // domefacesId[1] => -45º (left) + glBindTexture(GL_TEXTURE_2D, domefacesId[2]); + GLDrawTriangles(cubeleft, nfacesleft); + + // domefacesId[2] => 45º (right) + glBindTexture(GL_TEXTURE_2D, domefacesId[3]); + GLDrawTriangles(cuberight, nfacesright); + + // domefacesId[0] => -135º (leftback) + glBindTexture(GL_TEXTURE_2D, domefacesId[4]); + GLDrawTriangles(cubeleftback, nfacesleftback); + + // domefacesId[3] => 135º (rightback) + glBindTexture(GL_TEXTURE_2D, domefacesId[5]); + GLDrawTriangles(cuberightback, nfacesrightback); + } + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +} + +void KX_Dome::DrawDomeWarped(void) +{ + int i,j; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // 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-1) / warp.imagewidth; + float uv_height = (float)(warp.bufferheight-1) / 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, m_buffersize); +} + +void KX_Dome::RenderDomeFrame(KX_Scene* scene, KX_Camera* cam, int i) +{ + if (!cam) + return; + + m_canvas->SetViewPort(0,0,m_buffersize-1,m_buffersize-1); + +// m_rasterizer->SetAmbient(); + m_rasterizer->DisplayFog(); + + CalculateFrustum(cam); //calculates m_projmat + cam->SetProjectionMatrix(m_projmat); + m_rasterizer->SetProjectionMatrix(cam->GetProjectionMatrix()); +// Dome_RotateCamera(cam,i); + + MT_Matrix3x3 camori = cam->GetSGNode()->GetLocalOrientation(); + + cam->NodeSetLocalOrientation(camori*m_locRot[i]); + cam->NodeUpdateGS(0.f); + + MT_Transform camtrans(cam->GetWorldToCamera()); + MT_Matrix4x4 viewmat(camtrans); + m_rasterizer->SetViewMatrix(viewmat, cam->NodeGetWorldPosition(), + cam->GetCameraLocation(), cam->GetCameraOrientation()); + cam->SetModelviewMatrix(viewmat); + + scene->CalculateVisibleMeshes(m_rasterizer,cam); + scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools); + + // restore the original orientation + cam->NodeSetLocalOrientation(camori); + cam->NodeUpdateGS(0.f); +}
\ No newline at end of file diff --git a/source/gameengine/Ketsji/KX_Dome.h b/source/gameengine/Ketsji/KX_Dome.h new file mode 100644 index 00000000000..de3360cd897 --- /dev/null +++ b/source/gameengine/Ketsji/KX_Dome.h @@ -0,0 +1,183 @@ +/* $Id$ +----------------------------------------------------------------------------- + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place - Suite 330, Boston, MA 02111-1307, USA, or go to +http://www.gnu.org/copyleft/lesser.txt. + +Contributor(s): Dalai Felinto + +This source uses some of the ideas and code from Paul Bourke. +Developed as part of a Research and Development project for SAT - La Société des arts technologiques. +----------------------------------------------------------------------------- +*/ + +#if !defined KX_DOME_H +#define KX_DOME_H + +#include "KX_Scene.h" +#include "KX_Camera.h" +#include "DNA_screen_types.h" +#include "RAS_ICanvas.h" +#include "RAS_IRasterizer.h" +#include "RAS_IRenderTools.h" +#include "KX_KetsjiEngine.h" + +#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 +#define DOME_PANORAM_SPH 3 +#define DOME_NUM_MODES 4 + + +/// class for render 3d scene +class KX_Dome +{ +public: + /// constructor + KX_Dome ( + RAS_ICanvas* m_canvas, + /// rasterizer + RAS_IRasterizer* m_rasterizer, + /// render tools + RAS_IRenderTools* m_rendertools, + /// engine + KX_KetsjiEngine* m_engine, + + float size, + short res, + short mode, + short angle, + float resbuf, + struct Text* warptext + ); + + /// destructor + virtual ~KX_Dome (void); + + //openGL checks: + bool dlistSupported; + + //openGL names: + 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 { + double u[3], v[3]; + MT_Vector3 verts[3]; //three verts + } DomeFace; + + //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; + int nfacesleftback, nfacesrightback; + + int GetNumberRenders(){return m_numfaces;}; + + void RenderDome(void); + void RenderDomeFrame(KX_Scene* scene, KX_Camera* cam, int i); + void BindImages(int i); + + void SetViewPort(GLuint viewport[4]); + void CalculateFrustum(KX_Camera* cam); + void RotateCamera(KX_Camera* cam, int i); + + //Mesh Creating Functions + void CreateMeshDome180(void); + void CreateMeshDome250(void); + void CreateMeshPanorama(void); + + void SplitFace(vector <DomeFace>& face, int *nfaces); + + void FlattenDome(MT_Vector3 verts[3]); + void FlattenPanorama(MT_Vector3 verts[3]); + + //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 + bool CreateDL(void); //create Display Lists + void ClearDL(void); //remove Display Lists + + void CalculateCameraOrientation(); + void CalculateImageSize(); //set m_imagesize + + int canvaswidth; + int canvasheight; + +protected: + int m_drawingmode; + + 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 + short m_mode; // the mode (truncated, warped, panoramic,...) + short m_angle; //the angle of the fisheye + float m_radangle; //the angle of the fisheye in radians + float m_resbuffer; //the resolution of the buffer + + RAS_Rect m_viewport; + + MT_Matrix4x4 m_projmat; + + MT_Matrix3x3 m_locRot [6];// the rotation matrix + + /// rendered scene + KX_Scene * m_scene; + + /// canvas + RAS_ICanvas* m_canvas; + /// rasterizer + RAS_IRasterizer* m_rasterizer; + /// render tools + RAS_IRenderTools* m_rendertools; + /// engine + KX_KetsjiEngine* m_engine; +}; + +#endif
\ No newline at end of file diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index 70ae0e4b937..e64ffa95161 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -55,6 +55,7 @@ #include "KX_Scene.h" #include "MT_CmMatrix4x4.h" #include "KX_Camera.h" +#include "KX_Dome.h" #include "KX_Light.h" #include "KX_PythonInit.h" #include "KX_PyConstraintBinding.h" @@ -144,6 +145,8 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) m_stereo(false), m_curreye(0), + m_usedome(false), + m_logger(NULL), // Set up timing info display variables @@ -179,6 +182,8 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) KX_KetsjiEngine::~KX_KetsjiEngine() { delete m_logger; + if(m_usedome) + delete m_dome; } @@ -256,7 +261,124 @@ void KX_KetsjiEngine::SetSceneConverter(KX_ISceneConverter* sceneconverter) m_sceneconverter = sceneconverter; } +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, 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]}; + + m_dome->SetViewPort(viewport); + + KX_Scene* firstscene = *m_scenes.begin(); + const RAS_FrameSettings &framesettings = firstscene->GetFramingType(); + + m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true); + + // hiding mouse cursor each frame + // (came back when going out of focus and then back in again) + if (m_hideCursor) + m_canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE); + + // clear the entire game screen with the border color + // only once per frame + + m_canvas->BeginDraw(); + + // BeginFrame() sets the actual drawing area. You can use a part of the window + if (!BeginFrame()) + return; + + int n_renders=m_dome->GetNumberRenders();// usually 4 or 6 + KX_SceneList::iterator sceneit; + for (int i=0;i<n_renders;i++){ + m_canvas->ClearBuffer(RAS_ICanvas::COLOR_BUFFER|RAS_ICanvas::DEPTH_BUFFER); + for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); sceneit++) + // for each scene, call the proceed functions + { + KX_Scene* scene = *sceneit; + KX_Camera* cam = scene->GetActiveCamera(); + + m_rendertools->BeginFrame(m_rasterizer); + // pass the scene's worldsettings to the rasterizer + SetWorldSettings(scene->GetWorldInfo()); + + // shadow buffers + if (i == 0){ + RenderShadowBuffers(scene); + scene->UpdateMeshTransformations();//I need to run it somewherelse, otherwise Im overrunning it + } + // Avoid drawing the scene with the active camera twice when it's viewport is enabled + if(cam && !cam->GetViewport()) + { + if (scene->IsClearingZBuffer()) + m_rasterizer->ClearDepthBuffer(); + + m_rendertools->SetAuxilaryClientInfo(scene); + + // do the rendering + m_dome->RenderDomeFrame(scene,cam, i); + } + + list<class KX_Camera*>* cameras = scene->GetCameras(); + + // Draw the scene once for each camera with an enabled viewport + list<KX_Camera*>::iterator it = cameras->begin(); + while(it != cameras->end()) + { + if((*it)->GetViewport()) + { + if (scene->IsClearingZBuffer()) + m_rasterizer->ClearDepthBuffer(); + + m_rendertools->SetAuxilaryClientInfo(scene); + + // do the rendering + m_dome->RenderDomeFrame(scene, (*it),i); + } + + it++; + } + } + m_dome->BindImages(i); + } + +// m_dome->Dome_PostRender(scene, cam, stereomode); + m_canvas->EndFrame();//XXX do we really need that? + + m_canvas->SetViewPort(0, 0, m_canvas->GetWidth(), m_canvas->GetHeight()); + + if (m_overrideFrameColor) //XXX why do we want + { + // Do not use the framing bar color set in the Blender scenes + m_canvas->ClearColor( + m_overrideFrameColorR, + m_overrideFrameColorG, + m_overrideFrameColorB, + 1.0 + ); + } + else + { + // Use the framing bar color set in the Blender scenes + m_canvas->ClearColor( + framesettings.BarRed(), + framesettings.BarGreen(), + framesettings.BarBlue(), + 1.0 + ); + } + + m_dome->Draw(); + //run 2dfilters + EndFrame(); +} /** * Ketsji Init(), Initializes datastructures and converts data from @@ -631,6 +753,10 @@ else void KX_KetsjiEngine::Render() { + if(m_usedome){ + RenderDome(); + return; + } KX_Scene* firstscene = *m_scenes.begin(); const RAS_FrameSettings &framesettings = firstscene->GetFramingType(); @@ -1699,4 +1825,3 @@ void KX_KetsjiEngine::GetOverrideFrameColor(float& r, float& g, float& b) const } - diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h index 8516049f6d8..a8ccd6100d7 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.h +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h @@ -74,6 +74,7 @@ private: PyObject* m_pythondictionary; class SCA_IInputDevice* m_keyboarddevice; class SCA_IInputDevice* m_mousedevice; + class KX_Dome* m_dome; // dome stereo mode /** Lists of scenes scheduled to be removed at the end of the frame. */ std::set<STR_String> m_removingScenes; @@ -208,6 +209,12 @@ public: RAS_ICanvas* GetCanvas(){return m_canvas;}; RAS_IRenderTools* GetRenderTools(){return m_rendertools;}; + /// Dome functions + void InitDome(float size, short res, short mode, short angle, float resbuf, struct Text* text); + void EndDome(); + void RenderDome(); + bool m_usedome; + ///returns true if an update happened to indicate -> Render bool NextFrame(); void Render(); @@ -234,6 +241,8 @@ public: void GetSceneViewport(KX_Scene* scene, KX_Camera* cam, RAS_Rect& area, RAS_Rect& viewport); void SetDrawType(int drawingtype); + int GetDrawType(){return m_drawingmode;}; + void SetCameraZoom(float camzoom); void EnableCameraOverride(const STR_String& forscene); diff --git a/source/gameengine/Rasterizer/RAS_IRasterizer.h b/source/gameengine/Rasterizer/RAS_IRasterizer.h index 83adcfd8321..cfeda06e670 100644 --- a/source/gameengine/Rasterizer/RAS_IRasterizer.h +++ b/source/gameengine/Rasterizer/RAS_IRasterizer.h @@ -113,6 +113,7 @@ public: RAS_STEREO_ANAGLYPH, RAS_STEREO_SIDEBYSIDE, RAS_STEREO_VINTERLACE, + RAS_STEREO_DOME, RAS_STEREO_MAXSTEREO }; diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp index 765ff0174ee..1a9a28916de 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp @@ -436,7 +436,7 @@ RAS_IRasterizer::StereoMode RAS_OpenGLRasterizer::GetStereoMode() bool RAS_OpenGLRasterizer::Stereo() { - if(m_stereomode == RAS_STEREO_NOSTEREO) + if(m_stereomode == RAS_STEREO_NOSTEREO || m_stereomode == RAS_STEREO_DOME) return false; else return true; @@ -803,7 +803,7 @@ MT_Matrix4x4 RAS_OpenGLRasterizer::GetFrustumMatrix( double mat[16]; // correction for stereo - if(m_stereomode != RAS_STEREO_NOSTEREO) + if(Stereo()) { float near_div_focallength; // next 2 params should be specified on command line and in Blender publisher @@ -846,7 +846,7 @@ void RAS_OpenGLRasterizer::SetViewMatrix(const MT_Matrix4x4 &mat, const MT_Vecto m_viewmatrix = mat; // correction for stereo - if(m_stereomode != RAS_STEREO_NOSTEREO) + if(Stereo()) { MT_Matrix3x3 camOrientMat3x3(camOrientQuat); MT_Vector3 unitViewDir(0.0, -1.0, 0.0); // minus y direction, Blender convention |