/*************************************************************************** * Copyright (C) 2004 - 2005 by Raphael Langerhorst * * raphael-langerhorst@gmx.at * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * ***************************************************************************/ #include "GOpenGLFrame.h" #include #include #include #include #include #include #include #include #include #include // @todo GOpenGLForm is depracted, thus the client engine needs to be converted as well. using namespace std; using namespace GCS; using namespace GWE; namespace GCE { GOpenGLFrame::GOpenGLFrame(const GweController* gwe, QWidget* parent, const char* name) : QGLWidget(parent,name), Gwe(gwe), CameraParent(0), FramesPerSecond(0), TimeSinceFpsUpdate(0) { connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(stopRendering())); connect(&RedrawTimer,SIGNAL(timeout()),this,SLOT(update())); } void GOpenGLFrame::transform(const GForm* form) const //please make sure this is called in a valid OpenGL context!! { glTranslated(form->Position.x,form->Position.y,form->Position.z); if (form->Rotation.x) glRotated(form->Rotation.x,1,0,0); if (form->Rotation.y) glRotated(form->Rotation.y,0,1,0); if (form->Rotation.z) glRotated(form->Rotation.z,0,0,1); } void GOpenGLFrame::render(const GForm* form, float* RGBA) const { GLUquadric* QuadricObject = gluNewQuadric(); Q_CHECK_PTR(QuadricObject); if (QuadricObject) { glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,RGBA); //@todo draw an ellipsoid, not a sphere gluSphere(QuadricObject,form->getRadiusMax(),16,16); gluQuadricNormals(QuadricObject,GLU_SMOOTH); gluDeleteQuadric(QuadricObject); } } void GOpenGLFrame::renderElement(const GElement* element) { // qDebug(QString("drawing child element: ") + QString::number(element->getElementID().getID())); const GDataController* data = Gwe->getDataController(); GWE::GXmlDataController* data_xml = NULL; if (data->inherits("GWE::GXmlDataController")) { data_xml = (GWE::GXmlDataController*)data; } const GObject* object = element->getObject(); if (object->hasForm()) { const GForm* f = object->getForm(); // const GEnergy* e = object->getEnergy(); float RGBA[4]; const QDomDocument* element_data = object->getElementData(); QDomElement dom_child = element_data->elementsByTagName("appearance").item(0).toElement(); if (dom_child.isNull()) { // qWarning("element data has no appearance data, using half opaque white colour by default"); RGBA[0]=1; RGBA[1]=1; RGBA[2]=1; RGBA[3]=0.5; } dom_child = dom_child.elementsByTagName("colour").item(0).toElement(); if (dom_child.isNull()) { // qWarning("element data has no colour data, using half opaque white colour by default"); RGBA[0]=1; RGBA[1]=1; RGBA[2]=1; RGBA[3]=0.5; } bool ok; RGBA[0] = dom_child.elementsByTagName("r").item(0).toElement().text().toDouble(&ok); if (!ok) { // qWarning("element data has no red colour data, using default 1"); RGBA[0] = 1; } RGBA[1] = dom_child.elementsByTagName("g").item(0).toElement().text().toDouble(&ok); if (!ok) { // qWarning("element data has no green colour data, using default 1"); RGBA[1] = 1; } RGBA[2] = dom_child.elementsByTagName("b").item(0).toElement().text().toDouble(&ok); if (!ok) { // qWarning("element data has no blue colour data, using default 1"); RGBA[2] = 1; } RGBA[3] = dom_child.elementsByTagName("a").item(0).toElement().text().toDouble(&ok); if (!ok) { // qWarning("element data has no alpha colour data, using default 0.5"); RGBA[3] = 0.5; } // qDebug("element position: " + QString::number(f.Position.x) + ", " + QString::number(f.Position.y) + ", " + QString::number(f.Position.z)); glPushMatrix(); transform(f); //render children QValueList children = element->getObject()->getChildren(); for ( QValueListIterator it = children.begin(); it != children.end(); it++) { const GElement* element = data->read((*it)); Q_CHECK_PTR(element); if (element) { renderElement(element); } } if (RGBA[3] > 0.001) { //in case we have a networked environment, let's also see the server if (data_xml!=NULL && CameraParent == element->getObject()->getParent()) { QString s = data_xml->getManagingServerOfElement(element->getElementID()); if (!s.isEmpty()) { this->qglColor(Qt::white); this->renderText(f->Position.x-this->ViewProperties.ViewPosition.x,f->Position.y-this->ViewProperties.ViewPosition.y,f->Position.z-this->ViewProperties.ViewPosition.z,s); } } render(f,RGBA); } glPopMatrix(); } else { //render children QValueList children = element->getObject()->getChildren(); for ( QValueListIterator it = children.begin(); it != children.end(); it++) { const GElement* element = data->read((*it)); Q_CHECK_PTR(element); if (element) { renderElement(element); } } } } void GOpenGLFrame::startRendering(int sleep_ms_between_updates_min) { this->StopRendering = false; this->RedrawSleepTime_ms = sleep_ms_between_updates_min; this->RedrawTimer.start(this->RedrawSleepTime_ms); this->FpsTimer.start(); } void GOpenGLFrame::stopRendering() { this->StopRendering = true; } void GOpenGLFrame::initializeGL() { this->makeCurrent(); glClearColor(0,0,0,0); //black background glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); //start off with an empty background glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); //enable depth test glEnable(GL_BLEND); //enable blending - needed for alpha blending glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //alpha blending glEnable(GL_LIGHTING); //globally enable lighting // glEnable(GL_POLYGON_SMOOTH); //@todo: the light sources are ... a bit outdated, they were useful until 0.3.x versions. A new lighting model should be created. // create a light source glLightf(GL_LIGHT0,GL_CONSTANT_ATTENUATION,1); float v[] = {-1,5,-3,1}; glLightfv(GL_LIGHT0,GL_POSITION,v); v[0] = v[1] = v[2] = 0.5; v[3] = 1; glLightfv(GL_LIGHT0,GL_AMBIENT,v); v[0] = 0.5; v[1] = 0.5; v[2] = 0.5; v[3] = 1; glLightfv(GL_LIGHT0,GL_DIFFUSE,v); glLightfv(GL_LIGHT0,GL_SPECULAR,v); glEnable(GL_LIGHT0); //another one: // glLightf(GL_LIGHT1,GL_CONSTANT_ATTENUATION,1); // v[0] = 0; // v[1] = -5; // v[2] = 3; // v[3] = 1; // glLightfv(GL_LIGHT1,GL_POSITION,v); // v[0] = v[1] = v[2] = 0; // v[3] = 0; // glLightfv(GL_LIGHT1,GL_AMBIENT,v); // v[0] = 0.5; // v[1] = 0.5; // v[2] = 0.5; // v[3] = 1; // glLightfv(GL_LIGHT1,GL_DIFFUSE,v); // glLightfv(GL_LIGHT1,GL_SPECULAR,v); // glEnable(GL_LIGHT1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void GOpenGLFrame::resizeGL(int w, int h) { ViewProperties.width = w; ViewProperties.height = h; ViewProperties.update(); } void GOpenGLFrame::paintGL() { emit this->beforeRendering(); // qDebug("rendering OpenGL frame"); this->lock(); Q_CHECK_PTR(Gwe); const GDataController* data = Gwe->getDataController(); Q_CHECK_PTR(data); if (TopElements.isEmpty() || Gwe == NULL) { // qWarning("No top element given for rendering!"); } else { //@todo we DO get a problem here if we do not have double buffering // because the screen gets cleared all the time (so nothing to // be seen...) glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // glClear(GL_DEPTH_BUFFER_BIT ); glMatrixMode(GL_MODELVIEW); //draw top forms: for ( QValueListIterator it = TopElements.begin(); it != TopElements.end(); it++) { const GElement* element = data->read((*it)); if (element == NULL) { qWarning(QString("top element with ID %1 not found!").arg((*it).getID())); continue; //well, nothing to render here } glLoadIdentity(); //start with default values: GVector3 view_position(ViewProperties.ViewPosition); GVector3 view_target(ViewProperties.ViewTarget); GVector3 view_up(ViewProperties.ViewUp); //load element to which the camera is set: const GElement* camera_element = data->read(ViewProperties.CameraElement); if (camera_element) { this->CameraParent = camera_element->getObject()->getParent(); QPtrStack camera_element_hierarchy; camera_element_hierarchy.push(camera_element); //push parent elements onto stack until we either reach the current top element or there is no further parent while (camera_element && camera_element->getElementID() != camera_element->getObject()->getParent() && camera_element->getElementID() != (*it)) { camera_element = data->read(camera_element->getObject()->getParent()); if (camera_element) camera_element_hierarchy.push(camera_element); } //now we have the whole element hierarchy that holds the camera element in one stack //so we can apply the camera transformations now to get final view positions and directions for (const GElement* el = camera_element_hierarchy.pop(); camera_element_hierarchy.isEmpty() == false; el = camera_element_hierarchy.pop()) { const GObject* object = el->getObject(); if (object->hasForm()) { const GForm* f = object->getForm(); //@todo we need to build translation and rotation matrix functionality into GMatrix44!! //first rotate view_position.turnAroundAxis(GVector3(1,0,0),f->Rotation.x); view_position.turnAroundAxis(GVector3(0,1,0),f->Rotation.y); view_position.turnAroundAxis(GVector3(0,0,1),f->Rotation.z); view_target.turnAroundAxis(GVector3(1,0,0),f->Rotation.x); view_target.turnAroundAxis(GVector3(0,1,0),f->Rotation.y); view_target.turnAroundAxis(GVector3(0,0,1),f->Rotation.z); view_up.turnAroundAxis(GVector3(1,0,0),f->Rotation.x); view_up.turnAroundAxis(GVector3(0,1,0),f->Rotation.y); view_up.turnAroundAxis(GVector3(0,0,1),f->Rotation.z); //then translate view_position.add(f->Position); view_target.add(f->Position); } } } else { // qWarning("No camera element set for the view properties or element not available!"); } gluLookAt(view_position.x,view_position.y,view_position.z, view_target.x,view_target.y,view_target.z, view_up.x,view_up.y,view_up.z); //do the actual hierarchical rendering renderElement(element); } //show some position and direction information: this->qglColor(Qt::white); this->renderText(10,20,QString("Camera Position: %1").arg(this->ViewProperties.ViewPosition.toString())); this->renderText(10,40,QString("User Position: %1").arg(this->ViewProperties.ViewTarget.toString())); this->renderText(10,60,QString("Direction: %1").arg((this->ViewProperties.ViewTarget - this->ViewProperties.ViewPosition).toString())); //@todo, remove navigation instructions! They should be shown on startup in a messagebox... this->renderText(10,100,"Navigation:"); this->renderText(20,120,"spacebar: hold down to move forward"); this->renderText(20,140,"enter: full stop"); this->renderText(20,160,"arrow keys: turn camera around"); if (!this->hasFocus()) { this->renderText(10,this->height()-40,"OpenGL Frame does not have focus, click here to enable navigation!"); } // FPS calculations double delta_t = FpsTimer.restart(); this->TimeSinceFpsUpdate += delta_t; this->RenderingPeriods.append(delta_t); if (this->TimeSinceFpsUpdate >= 1000) { //recalculate fps: int n = this->RenderingPeriods.count(); double sum_t = 0; QValueList::iterator it; for (it = this->RenderingPeriods.begin(); it != this->RenderingPeriods.end(); ++it) { sum_t += *it; } // qDebug(QString("fps: n = %1, sum_t = %2").arg(QString::number(n)).arg(QString::number(sum_t))); if (sum_t > 0) this->FramesPerSecond = n/sum_t*1000; else this->FramesPerSecond = 0; this->RenderingPeriods.clear(); this->TimeSinceFpsUpdate = 0; } //show FPS this->renderText(this->width()-100,20,QString("FPS: ") + QString::number(this->FramesPerSecond,'f',2)); //at the end load an identitiy matrix glLoadIdentity(); glFlush(); //make sure everything is drawn } if (!this->StopRendering) { // qDebug("Restaring rendering timer."); this->RedrawTimer.start(this->RedrawSleepTime_ms,TRUE); } this->unlock(); emit this->afterRendering(); } void GOpenGLFrame::addTopElement(const GElementID& element) { QMutexLocker m(this); this->TopElements.append(element); } void GOpenGLFrame::removeTopElement(const GElementID& element) { QMutexLocker m(this); this->TopElements.remove(element); } }