481 lines
12 KiB
C++
481 lines
12 KiB
C++
|
// Begin License:
|
||
|
// Copyright (C) 2006-2008 Tobias Sargeant (tobias.sargeant@gmail.com).
|
||
|
// All rights reserved.
|
||
|
//
|
||
|
// This file is part of the Carve CSG Library (http://carve-csg.com/)
|
||
|
//
|
||
|
// This file may be used under the terms of the GNU General Public
|
||
|
// License version 2.0 as published by the Free Software Foundation
|
||
|
// and appearing in the file LICENSE.GPL2 included in the packaging of
|
||
|
// this file.
|
||
|
//
|
||
|
// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
|
||
|
// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
|
||
|
// A PARTICULAR PURPOSE.
|
||
|
// End:
|
||
|
|
||
|
|
||
|
#if defined(HAVE_CONFIG_H)
|
||
|
# include <carve_config.h>
|
||
|
#endif
|
||
|
|
||
|
#include "scene.hpp"
|
||
|
|
||
|
#include <carve/matrix.hpp>
|
||
|
#include <carve/geom3d.hpp>
|
||
|
|
||
|
#include <GL/glui.h>
|
||
|
|
||
|
static int lastx = 0, lasty = 0;
|
||
|
static unsigned buttons;
|
||
|
static int lastbutton = 0;
|
||
|
|
||
|
static Scene *g_scene = NULL;
|
||
|
static int g_mainWindow = NULL;
|
||
|
static GLUI *g_rightPanel = NULL;
|
||
|
static double near_plane = 0.2;
|
||
|
static double far_plane = 200;
|
||
|
carve::math::Matrix g_projection, g_modelview;
|
||
|
|
||
|
void Scene::updateDisplay() {
|
||
|
if (CAM_ELEVATION < -90.0) CAM_ELEVATION = -90;
|
||
|
if (CAM_ELEVATION > 90.0) CAM_ELEVATION = 90;
|
||
|
if (CAM_DIST < 0.05) {
|
||
|
CAM_DIST = 0.05;
|
||
|
}
|
||
|
glutPostWindowRedisplay(g_mainWindow);
|
||
|
glutPostRedisplay();
|
||
|
}
|
||
|
|
||
|
carve::geom3d::Vector rotateWithVector(const carve::geom3d::Vector &x, carve::geom3d::Vector u, float ang) {
|
||
|
carve::geom3d::Vector h,v,uxx;
|
||
|
|
||
|
u.normalize();
|
||
|
|
||
|
|
||
|
uxx = cross(u, x) * (float)sin(ang);
|
||
|
|
||
|
h = u * (dot(x,u));
|
||
|
|
||
|
v = (x - h) * (float)cos(ang);
|
||
|
|
||
|
return (h + v) + uxx;
|
||
|
}
|
||
|
|
||
|
// Our world is Z up.
|
||
|
const carve::geom3d::Vector WORLD_RIGHT = carve::geom::VECTOR(1,0,0);
|
||
|
const carve::geom3d::Vector WORLD_UP = carve::geom::VECTOR(0,0,1);
|
||
|
const carve::geom3d::Vector WORLD_IN = carve::geom::VECTOR(0,1,0);
|
||
|
|
||
|
void getCameraVectors(float rot, float elev, carve::geom3d::Vector &right, carve::geom3d::Vector &up, carve::geom3d::Vector &in) {
|
||
|
right = WORLD_RIGHT;
|
||
|
up = WORLD_UP;
|
||
|
in = WORLD_IN;
|
||
|
|
||
|
right = rotateWithVector(right, WORLD_RIGHT, carve::math::radians(elev));
|
||
|
up = rotateWithVector(up, WORLD_RIGHT, carve::math::radians(elev));
|
||
|
in = rotateWithVector(in, WORLD_RIGHT, carve::math::radians(elev));
|
||
|
right = rotateWithVector(right, WORLD_UP, carve::math::radians(rot));
|
||
|
up = rotateWithVector(up, WORLD_UP, carve::math::radians(rot));
|
||
|
in = rotateWithVector(in, WORLD_UP, carve::math::radians(rot));
|
||
|
}
|
||
|
|
||
|
GLvoid Scene::_drag(int x, int y) {
|
||
|
int dx = x - lastx;
|
||
|
int dy = y - lasty;
|
||
|
|
||
|
if (buttons == 0x04) {
|
||
|
CAM_DIST *= 1.0 + 0.01 * (y - lasty);
|
||
|
CAM_DIST_REAL *= 1.0 + 0.01 * (y - lasty);
|
||
|
updateDisplay();
|
||
|
} else if (buttons == 0x01) {
|
||
|
CAM_ELEVATION += 0.5 * (y-lasty);
|
||
|
CAM_ROT -= 0.5 * (x - lastx);
|
||
|
updateDisplay();
|
||
|
} else if (buttons == 0x02) {
|
||
|
carve::geom3d::Vector right, up, in;
|
||
|
getCameraVectors(CAM_ROT, CAM_ELEVATION, right, up, in);
|
||
|
|
||
|
right.scaleBy(2 * dx * CAM_DIST / WIDTH);
|
||
|
up.scaleBy(2 * dy * CAM_DIST / HEIGHT);
|
||
|
CAM_LOOK += right;
|
||
|
CAM_LOOK += up;
|
||
|
CAM_LOOK_REAL += right;
|
||
|
CAM_LOOK_REAL += up;
|
||
|
updateDisplay();
|
||
|
}
|
||
|
|
||
|
|
||
|
lastx = x;
|
||
|
lasty = y;
|
||
|
}
|
||
|
|
||
|
GLvoid Scene::_click(int button, int state, int x, int y) {
|
||
|
unsigned mask = 1 << button;
|
||
|
if (state) {
|
||
|
buttons &= ~mask;
|
||
|
} else {
|
||
|
buttons |= mask;
|
||
|
}
|
||
|
|
||
|
lastx = x;
|
||
|
lasty = y;
|
||
|
click(button, state, x, y);
|
||
|
}
|
||
|
|
||
|
GLvoid Scene::_key(unsigned char k, int x, int y) {
|
||
|
double rate = 1.0;
|
||
|
if (isupper(k)) { rate = 0.1; k = tolower(k); }
|
||
|
|
||
|
switch (k) {
|
||
|
case 'q':
|
||
|
exit(0);
|
||
|
break;
|
||
|
case 'g':
|
||
|
disp_grid = !disp_grid;
|
||
|
break;
|
||
|
case 'h':
|
||
|
disp_axes = !disp_axes;
|
||
|
break;
|
||
|
case 'w':
|
||
|
if (CAM_ELEVATION > -85.0) CAM_ELEVATION -= 5.0 * rate;
|
||
|
break;
|
||
|
case 's':
|
||
|
if (CAM_ELEVATION < 85.0) CAM_ELEVATION += 5.0 * rate;
|
||
|
break;
|
||
|
case 'a':
|
||
|
CAM_ROT += 5.0 * rate;
|
||
|
break;
|
||
|
case 'd':
|
||
|
CAM_ROT -= 5.0 * rate;
|
||
|
break;
|
||
|
case 'r':
|
||
|
CAM_DIST += 2.0 * rate;
|
||
|
break;
|
||
|
case 'f':
|
||
|
CAM_DIST -= 2.0 * rate;
|
||
|
break;
|
||
|
case 'i':
|
||
|
CAM_LOOK.x -= cos(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
CAM_LOOK.z -= sin(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
break;
|
||
|
case 'j':
|
||
|
CAM_LOOK.x -= sin(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
CAM_LOOK.z += cos(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
break;
|
||
|
case 'k':
|
||
|
CAM_LOOK.x += cos(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
CAM_LOOK.z += sin(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
break;
|
||
|
case 'l':
|
||
|
CAM_LOOK.x += sin(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
CAM_LOOK.z -= cos(carve::math::radians(CAM_ROT)) * 2.0 * rate;
|
||
|
break;
|
||
|
case 'z':
|
||
|
near_plane *= 1.1;
|
||
|
break;
|
||
|
case 'x':
|
||
|
near_plane /= 1.1;
|
||
|
break;
|
||
|
case 'c':
|
||
|
far_plane *= 1.1;
|
||
|
break;
|
||
|
case 'v':
|
||
|
far_plane /= 1.1;
|
||
|
break;
|
||
|
default: {
|
||
|
if (!key(k, x, y)) goto skip_redisplay;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
updateDisplay();
|
||
|
skip_redisplay:;
|
||
|
}
|
||
|
|
||
|
|
||
|
GLvoid Scene::_draw() {
|
||
|
glMatrixMode(GL_PROJECTION);
|
||
|
glLoadIdentity();
|
||
|
double w = WIDTH;
|
||
|
double h = HEIGHT;
|
||
|
|
||
|
if (h == 0) h = 1;
|
||
|
glViewport(0, 0, WIDTH, HEIGHT);
|
||
|
if (w > h) {
|
||
|
double r = w / h;
|
||
|
glFrustum(-0.2 * r, 0.2 * r, -0.2, 0.2, near_plane, far_plane);
|
||
|
} else {
|
||
|
double r = h / w;
|
||
|
glFrustum(-0.2, 0.2, -0.2 * r, 0.2 * r, near_plane, far_plane);
|
||
|
}
|
||
|
|
||
|
glMatrixMode(GL_MODELVIEW);
|
||
|
glLoadIdentity();
|
||
|
|
||
|
CAM_LOOK_REAL = (CAM_LOOK_REAL * 4 + CAM_LOOK) / 5;
|
||
|
CAM_DIST_REAL = (CAM_DIST_REAL * 4 + CAM_DIST) / 5;
|
||
|
|
||
|
// if they are far apart post another redisplay
|
||
|
if ((CAM_LOOK_REAL - CAM_LOOK).length() > 0.001) {
|
||
|
glutPostRedisplay();
|
||
|
} else {
|
||
|
CAM_LOOK_REAL = CAM_LOOK;
|
||
|
}
|
||
|
if (fabs(CAM_DIST_REAL - CAM_DIST) > 0.001) {
|
||
|
glutPostRedisplay();
|
||
|
} else {
|
||
|
CAM_DIST_REAL = CAM_DIST;
|
||
|
}
|
||
|
|
||
|
carve::geom3d::Vector right, up, in;
|
||
|
getCameraVectors(CAM_ROT, CAM_ELEVATION, right, up, in);
|
||
|
in = CAM_DIST_REAL * in;
|
||
|
gluLookAt(CAM_LOOK_REAL.x + in.x, CAM_LOOK_REAL.y + in.y, CAM_LOOK_REAL.z + in.z,
|
||
|
CAM_LOOK_REAL.x, CAM_LOOK_REAL.y, CAM_LOOK_REAL.z,
|
||
|
up.x, up.y, up.z);
|
||
|
|
||
|
glGetDoublev(GL_MODELVIEW_MATRIX, g_modelview.v);
|
||
|
glGetDoublev(GL_PROJECTION_MATRIX, g_projection.v);
|
||
|
|
||
|
|
||
|
glShadeModel(GL_SMOOTH);
|
||
|
glClearDepth(1.0);
|
||
|
glClearColor(0.05f, 0.075f, 0.2f, 1.0f);
|
||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||
|
|
||
|
{
|
||
|
const float amb[] = {0.05f, 0.05f, 0.05f, 1.0f};
|
||
|
const float diff[] = {1.0f, 0.8f, 0.5f, 1.0f};
|
||
|
const float spec[] = {0.4f, 0.4f, 0.4f, 1.0f};
|
||
|
const float pos[] = {-40.0f, 40.0f, 80.0f, 0.0f};
|
||
|
|
||
|
glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
|
||
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, diff);
|
||
|
glLightfv(GL_LIGHT0, GL_SPECULAR, spec);
|
||
|
glLightfv(GL_LIGHT0, GL_POSITION, pos);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
const float amb[] = {0.05f, 0.05f, 0.05f, 1.0f};
|
||
|
const float diff[] = {0.8f, 0.8f, 1.0f, 1.0f};
|
||
|
const float spec[] = {0.4f, 0.4f, 0.4f, 1.0f};
|
||
|
const float pos[] = {+50.0f, -10.0f, 10.0f, 0.0f};
|
||
|
|
||
|
glLightfv(GL_LIGHT1, GL_AMBIENT, amb);
|
||
|
glLightfv(GL_LIGHT1, GL_DIFFUSE, diff);
|
||
|
glLightfv(GL_LIGHT1, GL_SPECULAR, spec);
|
||
|
glLightfv(GL_LIGHT1, GL_POSITION, pos);
|
||
|
}
|
||
|
|
||
|
glCullFace(GL_BACK);
|
||
|
|
||
|
glEnable(GL_DEPTH_TEST);
|
||
|
glEnable(GL_LIGHT0);
|
||
|
glEnable(GL_LIGHT1);
|
||
|
glEnable(GL_LIGHTING);
|
||
|
glEnable(GL_NORMALIZE);
|
||
|
glEnable(GL_COLOR_MATERIAL);
|
||
|
|
||
|
glMatrixMode(GL_MODELVIEW);
|
||
|
glPushMatrix();
|
||
|
|
||
|
glEnable(GL_BLEND);
|
||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
|
||
|
glDisable(GL_LIGHTING);
|
||
|
glDisable(GL_DEPTH);
|
||
|
|
||
|
if (disp_grid) {
|
||
|
glBegin(GL_LINE_LOOP);
|
||
|
glColor4f(1,1,1,0.5);
|
||
|
glVertex3f(-30,-30,0);
|
||
|
glVertex3f(+30,-30,0);
|
||
|
glVertex3f(+30,+30,0);
|
||
|
glVertex3f(-30,+30,0);
|
||
|
glEnd();
|
||
|
|
||
|
glBegin(GL_LINES);
|
||
|
glColor4f(1.0f, 1.0f, 1.0f, 0.3f);
|
||
|
for (int i = 0; i < 30; i++) {
|
||
|
glVertex3f(i * 2.0f - 30.0f, -30.0f, 0.0f);
|
||
|
glVertex3f(i * 2.0f - 30.0f, +30.0f, 0.0f);
|
||
|
glVertex3f(-30.0f, i * 2.0f - 30.0f, 0.0f);
|
||
|
glVertex3f(+30.0f, i * 2.0f - 30.0f, 0.0f);
|
||
|
}
|
||
|
glEnd();
|
||
|
}
|
||
|
|
||
|
glEnable(GL_DEPTH_TEST);
|
||
|
glEnable(GL_LIGHTING);
|
||
|
glEnable(GL_COLOR_MATERIAL);
|
||
|
glDepthFunc(GL_LESS);
|
||
|
glDepthMask(GL_TRUE);
|
||
|
|
||
|
draw();
|
||
|
|
||
|
glDisable(GL_COLOR_MATERIAL);
|
||
|
glDisable(GL_DEPTH_TEST);
|
||
|
glDisable(GL_LIGHTING);
|
||
|
|
||
|
if (disp_axes) {
|
||
|
glBegin(GL_LINES);
|
||
|
glColor4f(1,0,0,1); glVertex3f(0,0,0); glVertex3f(10,0,0);
|
||
|
glColor4f(0,1,0,1); glVertex3f(0,0,0); glVertex3f(0,10,0);
|
||
|
glColor4f(0,0,1,1); glVertex3f(0,0,0); glVertex3f(0,0,10);
|
||
|
glEnd();
|
||
|
}
|
||
|
|
||
|
glPopMatrix();
|
||
|
|
||
|
glutSwapBuffers();
|
||
|
}
|
||
|
|
||
|
bool Scene::key(unsigned char k, int x, int y) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
GLvoid Scene::draw() {
|
||
|
}
|
||
|
|
||
|
void Scene::click(int button, int state, int x, int y) {
|
||
|
}
|
||
|
|
||
|
GLvoid Scene::_resize(int w, int h) {
|
||
|
int tx, ty, tw, th;
|
||
|
GLUI_Master.get_viewport_area(&tx, &ty, &tw, &th);
|
||
|
WIDTH = tw;
|
||
|
HEIGHT = th;
|
||
|
}
|
||
|
|
||
|
void control_cb(int control) {
|
||
|
glutPostRedisplay();
|
||
|
glutPostWindowRedisplay(g_mainWindow);
|
||
|
}
|
||
|
|
||
|
#define WIREFRAME_ENABLED_ID 200
|
||
|
|
||
|
int wireframe = 0;
|
||
|
|
||
|
static std::map<OptionGroup*, GLUI_Rollout *> groupToRollouts;
|
||
|
static std::map<Option*, GLUI_Checkbox *> optionToCheckboxes;
|
||
|
|
||
|
OptionGroup* Scene::createOptionGroup(const char *caption) {
|
||
|
// make sure our UI has been initialised.
|
||
|
init();
|
||
|
|
||
|
// Create a rollout GUI item.
|
||
|
OptionGroup *group = new OptionGroup();
|
||
|
GLUI_Rollout *rollout = new GLUI_Rollout(g_rightPanel, caption);
|
||
|
groupToRollouts[group] = rollout;
|
||
|
return group;
|
||
|
}
|
||
|
|
||
|
Option* OptionGroup::createOption(const char *caption, bool initialValue) {
|
||
|
GLUI_Rollout *rollout = groupToRollouts[this];
|
||
|
if (rollout == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
GLUI_Checkbox *cb = new GLUI_Checkbox(rollout, caption, NULL, 1, control_cb);
|
||
|
cb->set_int_val(initialValue);
|
||
|
|
||
|
Option *option = new Option();
|
||
|
|
||
|
optionToCheckboxes[option] = cb;
|
||
|
|
||
|
return option;
|
||
|
|
||
|
}
|
||
|
|
||
|
bool Option::isChecked() {
|
||
|
GLUI_Checkbox *cb = optionToCheckboxes[this];
|
||
|
if (cb != NULL) {
|
||
|
return cb->get_int_val();
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Option::setChecked(bool value) {
|
||
|
GLUI_Checkbox *cb = optionToCheckboxes[this];
|
||
|
if (cb != NULL) {
|
||
|
return cb->set_int_val(value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Scene::init() {
|
||
|
if (!initialised) {
|
||
|
initialised = true;
|
||
|
|
||
|
GLUI_Master.set_glutDisplayFunc(s_draw);
|
||
|
GLUI_Master.set_glutReshapeFunc(s_resize);
|
||
|
GLUI_Master.set_glutKeyboardFunc(s_key);
|
||
|
GLUI_Master.set_glutSpecialFunc(NULL);
|
||
|
GLUI_Master.set_glutMouseFunc(s_click);
|
||
|
GLUI_Master.set_glutMotionFunc(s_drag);
|
||
|
|
||
|
g_rightPanel = GLUI_Master.create_glui_subwindow(g_mainWindow, GLUI_SUBWINDOW_RIGHT);
|
||
|
|
||
|
this->_init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
carve::geom3d::Ray Scene::getRay(int x, int y) {
|
||
|
carve::geom3d::Vector from, to;
|
||
|
|
||
|
GLint view[4];
|
||
|
glGetIntegerv(GL_VIEWPORT, view);
|
||
|
gluUnProject(x, view[3] - y, 0, g_modelview.v, g_projection.v, view, &from.x, &from.y, &from.z);
|
||
|
gluUnProject(x, view[3] - y, 50, g_modelview.v, g_projection.v, view, &to.x, &to.y, &to.z);
|
||
|
|
||
|
|
||
|
return carve::geom3d::Ray((to - from).normalized(), from);
|
||
|
}
|
||
|
|
||
|
void Scene::zoomTo(carve::geom3d::Vector pos, double dist) {
|
||
|
CAM_LOOK = pos;
|
||
|
CAM_DIST = dist;
|
||
|
updateDisplay();
|
||
|
}
|
||
|
void Scene::_init() {
|
||
|
|
||
|
}
|
||
|
|
||
|
GLvoid Scene::s_draw() { g_scene->_draw(); }
|
||
|
GLvoid Scene::s_resize(int w, int h) { g_scene->_resize(w, h); }
|
||
|
GLvoid Scene::s_drag(int x, int y) { g_scene->_drag(x, y); }
|
||
|
GLvoid Scene::s_click(int button, int state, int x, int y) { g_scene->_click(button, state, x, y); }
|
||
|
GLvoid Scene::s_key(unsigned char k, int x, int y) { g_scene->_key(k, x, y); }
|
||
|
|
||
|
Scene::Scene(int argc, char **argv) {
|
||
|
CAM_ROT = 0.0;
|
||
|
CAM_ELEVATION = 45.0;
|
||
|
CAM_DIST = 70.0;
|
||
|
CAM_DIST_REAL = 10000;
|
||
|
|
||
|
WIDTH = 1024;
|
||
|
HEIGHT = 768;
|
||
|
|
||
|
disp_grid = true;
|
||
|
disp_axes = true;
|
||
|
|
||
|
glutInit(&argc, argv);
|
||
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
|
||
|
|
||
|
glutInitWindowSize(WIDTH, HEIGHT);
|
||
|
g_mainWindow = glutCreateWindow("Main");
|
||
|
|
||
|
initialised = false;
|
||
|
|
||
|
g_scene = this;
|
||
|
}
|
||
|
|
||
|
Scene::~Scene() {
|
||
|
g_scene = NULL;
|
||
|
}
|
||
|
|
||
|
void Scene::run() {
|
||
|
init();
|
||
|
glutMainLoop();
|
||
|
}
|