+// Copyright (C) 2006 Erik Dahlberg
+//
+// This file is part of LSystem3d.
+//
+// LSystem3D is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// LSystem3D 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with LSystem3D; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+
+
+#include <string>
+
+#include <glui.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/freeglut.h>
+
+#include "callbacks.h"
+#include "lindenmayersystem.h"
+#include "lsystemparameters.h"
+#include "model.h"
+#include "openglwindow.h"
+
+using namespace std;
+
+extern OpenGLWindow *openglWindow;
+
+
+
+/**
+ * Constructor
+ */
+OpenGLWindow::OpenGLWindow()
+{
+ // misc init
+
+ openglWindow = this;
+ _busyGenerating = true;
+ lsystemParametersPath = "lsystem.xml";
+ defaultView();
+
+
+
+ // init GLUT & GLUI
+ // ----------------
+
+ // drawing window
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
+ glutInitWindowSize(800, 800);
+ int _window = glutCreateWindow("lsystem");
+
+ // return control after closing the window
+ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, 1);
+
+ // register callbacks
+ glutKeyboardFunc(glutKeyboardCB);
+ glutSpecialFunc(glutSpecialCB);
+ glutDisplayFunc(glutDisplayCB);
+ GLUI_Master.set_glutIdleFunc(glutIdleCB);
+
+ // GUI window
+ _glui = GLUI_Master.create_glui("lsystem");
+
+ // model and l-system creation
+ _model = new Model;
+ _lsystem = new LindenmayerSystem(_model);
+
+ // load lsystem parameters
+ _lsystemParameters.load(_lsystem, lsystemParametersPath.c_str());
+
+
+
+ // create GLUI widgets
+ // -------------------
+
+ // axiom
+ _axiomEditText = _glui->add_edittext("Axiom", GLUI_EDITTEXT_TEXT);
+ _axiomEditText->set_text((char *)_lsystem->getAxiom().c_str());
+
+ // rules
+ // TODO: make dynamic!
+ GLUI_Panel *panel = _glui->add_panel("Rules", GLUI_PANEL_EMBOSSED);
+ _ruleEditTexts.push_back(_glui->add_edittext_to_panel(panel, "#1", GLUI_EDITTEXT_TEXT));
+ _ruleEditTexts.push_back(_glui->add_edittext_to_panel(panel, "#2", GLUI_EDITTEXT_TEXT));
+ _ruleEditTexts.push_back(_glui->add_edittext_to_panel(panel, "#3", GLUI_EDITTEXT_TEXT));
+ _ruleEditTexts.push_back(_glui->add_edittext_to_panel(panel, "#4", GLUI_EDITTEXT_TEXT));
+ _ruleEditTexts.push_back(_glui->add_edittext_to_panel(panel, "#5", GLUI_EDITTEXT_TEXT));
+ _ruleEditTexts[0]->set_w(200);
+ _ruleEditTexts[1]->set_w(200);
+ _ruleEditTexts[2]->set_w(200);
+ _ruleEditTexts[3]->set_w(200);
+ _ruleEditTexts[4]->set_w(200);
+
+ // sync gui and l-system
+ map<string,string> rules = _lsystem->getRules();
+ map<string,string>::iterator currentRule;
+ int i = 0;
+ for (currentRule = rules.begin(); currentRule != rules.end(); currentRule++)
+ {
+ string rule = currentRule->first + "=" + currentRule->second;
+ // TODO: bounds?
+ _ruleEditTexts[i++]->set_text((char *)rule.c_str());
+ }
+
+ // angle
+ _angleSpinner = _glui->add_spinner("Angle", GLUI_SPINNER_FLOAT, NULL, -1, (GLUI_Update_CB)gluiSpinnerAngleCB);
+ _angleSpinner->set_float_val(_lsystem->getAngle());
+ _angleSpinner->set_speed(1.0);
+
+ // iterations
+ _iterationsSpinner = _glui->add_spinner("Iterations", GLUI_SPINNER_INT);
+ _iterationsSpinner->set_int_val(_lsystem->getNumIterations());
+
+
+ _glui->add_separator();
+
+
+ // live update
+ _liveUpdates = 1;
+ _liveCheckbox = _glui->add_checkbox("Live update", &_liveUpdates);
+
+ // reset
+ _glui->add_button("Reset view", -1, (GLUI_Update_CB)gluiButtonResetCB);
+
+ // generate
+ _glui->add_button("Generate", -1, (GLUI_Update_CB)gluiButtonGenerateCB);
+
+
+ _glui->set_main_gfx_window(_window);
+
+
+
+
+ // init OpenGL view
+ // ----------------
+
+ // projection
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(65, 1, 1, 100);
+
+ // model view
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt(0.0, 0.0, 2.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+
+ glShadeModel(GL_SMOOTH);
+
+ // light
+ GLfloat lightPosition[] = {0.0, 0.0, 2.0, 0.0};
+ GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
+ glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
+
+ glEnable(GL_COLOR_MATERIAL);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+
+ glutMainLoop();
+}
+
+
+
+/**
+ * Destructor
+ */
+OpenGLWindow::~OpenGLWindow()
+{
+ // save current l-system to disk
+ _lsystemParameters.save(_lsystem, lsystemParametersPath.c_str());
+
+ delete _lsystem;
+ delete _model;
+}
+
+
+
+/**
+ * Start generation of l-system data
+ */
+void OpenGLWindow::generate()
+{
+ _busyGenerating = true;
+
+ // read GLUI widgets and set corresponding l-system parameters
+
+ // axiom
+ char *axiom = _axiomEditText->get_text();
+ _lsystem->setAxiom(axiom);
+
+ // rules
+ vector<char *> rules;
+ rules.push_back(_ruleEditTexts[0]->get_text());
+ rules.push_back(_ruleEditTexts[1]->get_text());
+ rules.push_back(_ruleEditTexts[2]->get_text());
+ rules.push_back(_ruleEditTexts[3]->get_text());
+ rules.push_back(_ruleEditTexts[4]->get_text());
+
+ for (int i = 0; i < rules.size(); i++)
+ {
+ if (rules[i][0] != '\0')
+ {
+ // non-empty rule
+
+ // separate rule name from the actual rule
+ string ruleName(rules[i], 1);
+ string theRule(rules[i]+2);
+
+ _lsystem->setRule(ruleName, theRule);
+ }
+ }
+
+ // angle
+ float angle = _angleSpinner->get_float_val();
+ _lsystem->setAngle(angle);
+
+ // iterations
+ int iterations = _iterationsSpinner->get_int_val();
+ _lsystem->setNumIterations(iterations);
+
+
+ // start generation
+ _model->begin();
+ _lsystem->generate();
+ _model->end();
+
+ _busyGenerating = false;
+}
+
+
+
+/**
+ * Move camera left
+ */
+void OpenGLWindow::left()
+{
+ _xModel += 0.1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Move camera right
+ */
+void OpenGLWindow::right()
+{
+ _xModel -= 0.1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Move camera up
+ */
+void OpenGLWindow::up()
+{
+ _yModel -= 0.1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Move camera down
+ */
+void OpenGLWindow::down()
+{
+ _yModel += 0.1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Move camera forth
+ */
+void OpenGLWindow::forth()
+{
+ _zModel += 1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Move camera back
+ */
+void OpenGLWindow::back()
+{
+ _zModel -= 1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Zoom in
+ */
+void OpenGLWindow::zoomIn()
+{
+ _scaleModel *= 1.1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Zoom out
+ */
+void OpenGLWindow::zoomOut()
+{
+ _scaleModel /= 1.1;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Rotate around y-axis
+ */
+void OpenGLWindow::rotateY()
+{
+ _yRotationModel = int(_yRotationModel + 5.0) % 360;
+ glutPostRedisplay();
+}
+
+
+
+/**
+ * Draw l-system model to screen
+ */
+void OpenGLWindow::draw()
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (!_busyGenerating)
+ {
+ // ready for rendering
+
+ glPushMatrix();
+ glTranslatef(_xModel, _yModel, _zModel);
+ glScalef(_scaleModel, _scaleModel, _scaleModel);
+ glRotatef(_yRotationModel, 0.0, 1.0, 0.0);
+
+ _model->draw();
+ glPopMatrix();
+ }
+
+ glutSwapBuffers();
+}
+
+
+
+/**
+ * Reset to default view
+ */
+void OpenGLWindow::defaultView()
+{
+ _xModel = _yModel = _zModel = 0.0;
+ _scaleModel = 1.0;
+ _yRotationModel = 0.0;
+}
+
+
+
+/**
+ * Check if live mode is activated
+ * @return true, if activated
+ */
+bool OpenGLWindow::liveActivated()
+{
+ return _liveUpdates;
+}