blender/source/gameengine/Ketsji/KX_Dome.cpp
Benoit Bolsee ba563216e9 BGE: Fix Orthographic mode and viewport scaling
- the BGE now uses correct glOrtho projection whe camera is in orthographic mode
-
2009-04-26 12:23:30 +00:00

1927 lines
50 KiB
C++

/* $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"
// 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
):
dlistSupported(false),
canvaswidth(-1), canvasheight(-1),
m_drawingmode(engine->GetDrawType()),
m_size(size),
m_resolution(res),
m_mode(mode),
m_angle(angle),
m_resbuffer(resbuf),
m_canvas(canvas),
m_rasterizer(rasterizer),
m_rendertools(rendertools),
m_engine(engine)
{
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_ENVMAP:
m_angle = 360;
m_numfaces = 6;
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 = (int)(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(){
dlistId = glGenLists((GLsizei) m_numimages);
if (dlistId != 0) {
if(m_mode == DOME_FISHEYE){
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 %d unsupported. Try 1 for Polar Mesh or 2 for Fisheye.\n", warp.mode);
}
}
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;
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 ((int)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_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_mode == DOME_ENVMAP){
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 - USED for ENVMAP only
-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->NodeGetWorldOrientation(), cam->NodeGetWorldPosition(), cam->GetCameraData()->m_perspective);
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_ENVMAP:
DrawEnvMap();
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::DrawEnvMap(void)
{
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, (-0.66), 0.66, -20.0, 10.0); //stretch the image to reduce resolution lost
else {
if (can_width/3 <= can_height/2){
ortho_width = 1.0;
ortho_height = (float)can_height/can_width;
}else{
ortho_height = 2.0f / 3;
ortho_width = (float)can_width/can_height * ortho_height;
}
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,0.0,1.0, 0.0,0.0,0.0, 0.0,1.0,0.0);
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_ratio = (float)(m_buffersize-1) / m_imagesize;
double onebythree = 1.0f / 3;
// domefacesId[0] => (top)
glBindTexture(GL_TEXTURE_2D, domefacesId[0]);
glBegin(GL_QUADS);
glTexCoord2f(uv_ratio,uv_ratio);
glVertex3f( onebythree, 0.0f, 3.0f);
glTexCoord2f(0.0,uv_ratio);
glVertex3f(-onebythree, 0.0f, 3.0f);
glTexCoord2f(0.0,0.0);
glVertex3f(-onebythree,-2 * onebythree, 3.0f);
glTexCoord2f(uv_ratio,0.0);
glVertex3f(onebythree,-2 * onebythree, 3.0f);
glEnd();
// domefacesId[1] => (bottom)
glBindTexture(GL_TEXTURE_2D, domefacesId[1]);
glBegin(GL_QUADS);
glTexCoord2f(uv_ratio,uv_ratio);
glVertex3f(-onebythree, 0.0f, 3.0f);
glTexCoord2f(0.0,uv_ratio);
glVertex3f(-1.0f, 0.0f, 3.0f);
glTexCoord2f(0.0,0.0);
glVertex3f(-1.0f,-2 * onebythree, 3.0f);
glTexCoord2f(uv_ratio,0.0);
glVertex3f(-onebythree,-2 * onebythree, 3.0f);
glEnd();
// domefacesId[2] => -90º (left)
glBindTexture(GL_TEXTURE_2D, domefacesId[2]);
glBegin(GL_QUADS);
glTexCoord2f(uv_ratio,uv_ratio);
glVertex3f(-onebythree, 2 * onebythree, 3.0f);
glTexCoord2f(0.0,uv_ratio);
glVertex3f(-1.0f, 2 * onebythree, 3.0f);
glTexCoord2f(0.0,0.0);
glVertex3f(-1.0f, 0.0f, 3.0f);
glTexCoord2f(uv_ratio,0.0);
glVertex3f(-onebythree, 0.0f, 3.0f);
glEnd();
// domefacesId[3] => 90º (right)
glBindTexture(GL_TEXTURE_2D, domefacesId[3]);
glBegin(GL_QUADS);
glTexCoord2f(uv_ratio,uv_ratio);
glVertex3f( 1.0f, 2 * onebythree, 3.0f);
glTexCoord2f(0.0,uv_ratio);
glVertex3f( onebythree, 2 * onebythree, 3.0f);
glTexCoord2f(0.0,0.0);
glVertex3f( onebythree, 0.0f, 3.0f);
glTexCoord2f(uv_ratio,0.0);
glVertex3f(1.0f, 0.0f, 3.0f);
glEnd();
// domefacesId[4] => 0º (front)
glBindTexture(GL_TEXTURE_2D, domefacesId[4]);
glBegin(GL_QUADS);
glTexCoord2f(uv_ratio,uv_ratio);
glVertex3f( 1.0f, 0.0f, 3.0f);
glTexCoord2f(0.0,uv_ratio);
glVertex3f( onebythree, 0.0f, 3.0f);
glTexCoord2f(0.0,0.0);
glVertex3f( onebythree,-2 * onebythree, 3.0f);
glTexCoord2f(uv_ratio,0.0);
glVertex3f(1.0f, -2 * onebythree, 3.0f);
glEnd();
// domefacesId[5] => 180º (back)
glBindTexture(GL_TEXTURE_2D, domefacesId[5]);
glBegin(GL_QUADS);
glTexCoord2f(uv_ratio,uv_ratio);
glVertex3f( onebythree, 2 * onebythree, 3.0f);
glTexCoord2f(0.0,uv_ratio);
glVertex3f(-onebythree, 2 * onebythree, 3.0f);
glTexCoord2f(0.0,0.0);
glVertex3f(-onebythree, 0.0f, 3.0f);
glTexCoord2f(uv_ratio,0.0);
glVertex3f(onebythree, 0.0f, 3.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
void KX_Dome::DrawDomeFisheye(void)
{
int i;
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 (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;
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)
{
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);
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->NodeGetWorldOrientation(), cam->NodeGetWorldPosition(), cam->GetCameraData()->m_perspective);
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);
}