1119 lines
30 KiB
C++
1119 lines
30 KiB
C++
#include "ViewBase.h"
|
|
|
|
#include "ModelBase.h"
|
|
#include "ControllerBase.h"
|
|
#include "CameraBase.h"
|
|
#include "OverlayBase.h"
|
|
|
|
#include "SimpleConsoleOverlay.h"
|
|
#include "OGLFT.h"
|
|
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
|
|
#include "OBJModel.h"
|
|
|
|
#include "DrawingsGL.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace Engine {
|
|
|
|
static ViewBase* ViewInstance = NULL;
|
|
|
|
void InitGL () {
|
|
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
|
|
glClearDepth(1.0);
|
|
glDepthFunc(GL_LEQUAL);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glShadeModel(GL_SMOOTH);
|
|
glEnable (GL_CULL_FACE);
|
|
glDisable (GL_FOG);
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glLoadIdentity ();
|
|
}
|
|
|
|
/*
|
|
* Inherited Module functions
|
|
*/
|
|
int ViewBase::OnInit (int argc, char* argv[]) {
|
|
LogMessage ("View Init");
|
|
|
|
mWindowHeight = VIEW_DEFAULT_HEIGHT;
|
|
mWindowWidth = VIEW_DEFAULT_WIDTH;
|
|
|
|
// get and save the screen resolution
|
|
mScreenHeight = SDL_GetVideoInfo()->current_h;
|
|
mScreenWidth = SDL_GetVideoInfo()->current_w;
|
|
|
|
mDrawFullscreen = false;
|
|
|
|
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
|
|
|
|
if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL ) == 0 ) {
|
|
LogError ("Video mode set failed: %s", SDL_GetError ());
|
|
exit (-1);
|
|
}
|
|
|
|
LogMessage ("Resolution is %dx%d", SDL_GetVideoInfo()->current_w, SDL_GetVideoInfo()->current_h);
|
|
|
|
InitGL ();
|
|
Resize (mWindowWidth, mWindowHeight);
|
|
|
|
// Initialize GLEW and check for anything we need
|
|
GLenum err = glewInit();
|
|
if (GLEW_OK != err) {
|
|
LogError ("Could not init GLEW properly: %s", glewGetErrorString(err));
|
|
}
|
|
|
|
LogDebug ("Using GLEW version %s", glewGetString(GLEW_VERSION));
|
|
if (glewIsExtensionSupported("GL_ARB_vertex_buffer_object")) {
|
|
} else {
|
|
LogError ("VBOs are not supported!");
|
|
}
|
|
|
|
// read OpenGL version
|
|
std::string gl_version (reinterpret_cast<const char*>(glGetString(GL_VERSION)));
|
|
if (gl_version.size() == 0)
|
|
LogError ("Could not retrieve GL version!");
|
|
|
|
std::string major_number_str = gl_version.substr(0, gl_version.find('.'));
|
|
std::string minor_number_str = gl_version.substr(major_number_str.size() + 1, gl_version.find_first_of(". ", major_number_str.size()));
|
|
|
|
mGLVersionInfo.GLMajor = atoi (major_number_str.c_str());
|
|
mGLVersionInfo.GLMinor = atoi (minor_number_str.c_str());
|
|
|
|
// read GLSL version
|
|
std::string glsl_version (reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
|
|
if (glsl_version.size() == 0)
|
|
LogError ("Could not retrieve GLSL version!");
|
|
|
|
major_number_str = glsl_version.substr(0, glsl_version.find('.'));
|
|
minor_number_str = glsl_version.substr(major_number_str.size() + 1, glsl_version.find_first_of(". ", major_number_str.size()));
|
|
|
|
mGLVersionInfo.GLSLMajor = atoi (major_number_str.c_str());
|
|
mGLVersionInfo.GLSLMinor = atoi (minor_number_str.c_str());
|
|
|
|
LogMessage ("GL Version = %s, gl_major = %d, gl_minor = %d", gl_version.c_str(), mGLVersionInfo.GLMajor, mGLVersionInfo.GLMinor);
|
|
LogMessage ("GLSL Version = %s, gl_major = %d, gl_minor = %d", gl_version.c_str(), mGLVersionInfo.GLSLMajor, mGLVersionInfo.GLSLMinor);
|
|
|
|
if (mGLVersionInfo.GLMajor >= 2 && mGLVersionInfo.GLMinor >= 0)
|
|
mUseShaders = true;
|
|
else
|
|
mUseShaders = false;
|
|
|
|
// load the shaders if possible
|
|
if (mUseShaders) {
|
|
mBlinnPhongShader = LoadShaderProgram (GetResourceFullPath("/data/shaders/blinn_phong.glsl"));
|
|
mNormalMappingShader = LoadShaderProgram (GetResourceFullPath("/data/shaders/normal_mapping.glsl"));
|
|
} else {
|
|
mBlinnPhongShader = 0;
|
|
mNormalMappingShader = 0;
|
|
}
|
|
|
|
// Create a null texture that can be used for objects without textures
|
|
// taken from http://www.dhpoware.com/demos/glslNormalMapping.html
|
|
int pitch = ((2 * 32 + 31) & ~31) >> 3; // align to 4-byte boundaries
|
|
std::vector<GLubyte> pixels(pitch * 2, 255);
|
|
mNullTexture = 0;
|
|
|
|
glGenTextures(1, &mNullTexture);
|
|
glBindTexture(GL_TEXTURE_2D, mNullTexture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_BGRA,
|
|
GL_UNSIGNED_BYTE, &pixels[0]);
|
|
|
|
// Load fonts etc.
|
|
LoadFont ("console.ttf");
|
|
mCurrentFont = mFonts["console.ttf"];
|
|
|
|
// Overlays
|
|
OverlayBasePtr console_overlay(new SimpleConsoleOverlay);
|
|
mOverlayManager.Register (console_overlay, 0);
|
|
//AddOverlay (console_overlay);
|
|
|
|
mDrawGrid = false;
|
|
mGridSizeX = 8;
|
|
mGridSizeZ = 8;
|
|
|
|
ViewInstance = this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ViewBase::OnDestroy () {
|
|
std::map<std::string, OGLFT::Monochrome*>::iterator iter;
|
|
|
|
for (iter = mFonts.begin(); iter != mFonts.end(); ++iter) {
|
|
delete iter->second;
|
|
}
|
|
mFonts.clear();
|
|
|
|
ViewInstance = NULL;
|
|
|
|
LogDebug ("View Destroy");
|
|
}
|
|
|
|
void ViewBase::CalcWorldCoordinates (int screen_x, int screen_y, float world_y, float *pos_out) {
|
|
GLdouble modelMatrix[16], projMatrix[16];
|
|
GLint viewport[4];
|
|
GLdouble wx, wy, wz;
|
|
|
|
glGetIntegerv (GL_VIEWPORT, viewport);
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
|
|
glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
|
|
|
|
int realy = viewport[3] - screen_y - 1;
|
|
|
|
gluUnProject ((GLdouble) screen_x, (GLdouble) realy, 1.,
|
|
modelMatrix, projMatrix, viewport, &wx, &wy, &wz);
|
|
|
|
GLdouble t;
|
|
GLdouble d[3];
|
|
float eye[3];
|
|
|
|
mCamera->GetEye (&eye[0]);
|
|
|
|
d[0] = wx - eye[0];
|
|
d[1] = wy - eye[1];
|
|
d[2] = wz - eye[2];
|
|
|
|
assert (fabs (d[1]) >= 1.0e-3);
|
|
t = -eye[1]/d[1] + world_y;
|
|
|
|
pos_out[0] = eye[0] + t * d[0];
|
|
pos_out[1] = eye[1] + t * d[1];
|
|
pos_out[2] = eye[2] + t * d[2];
|
|
}
|
|
|
|
/*
|
|
* Module specific functions
|
|
*/
|
|
void ViewBase::UpdateCamera () {
|
|
EntityPhysicState* player_ent = GetEntityPhysicState (GetPlayerEntityId());
|
|
|
|
if (!player_ent) {
|
|
LogError ("Could not call Model::PositionCamera(): player entity not found!");
|
|
exit (-1);
|
|
}
|
|
vector3d entity_camera_distance (-2, 3, 0);
|
|
vector3d entity_position = player_ent->GetPosition();
|
|
player_ent->Globalize (entity_camera_distance);
|
|
|
|
mCamera->SetEye (
|
|
entity_camera_distance[0],
|
|
entity_camera_distance[1],
|
|
entity_camera_distance[2]
|
|
);
|
|
mCamera->SetPointOfIntrest (
|
|
entity_position[0],
|
|
entity_position[1],
|
|
entity_position[2]
|
|
);
|
|
|
|
mCamera->Update ();
|
|
}
|
|
|
|
void ViewBase::PreDraw() {
|
|
// Clear the screen
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// update the frame rate counter
|
|
static Uint32 this_frame_ticks;
|
|
static Uint32 last_frame_ticks = 0;
|
|
static Uint32 last_fps_update = 0;
|
|
static int frame_counter = 0;
|
|
|
|
this_frame_ticks = SDL_GetTicks ();
|
|
last_fps_update += this_frame_ticks - last_frame_ticks;
|
|
last_frame_ticks = this_frame_ticks;
|
|
frame_counter++;
|
|
|
|
if (last_fps_update > 1000) {
|
|
mFrameRate = frame_counter;
|
|
last_fps_update = 0;
|
|
frame_counter = 0;
|
|
}
|
|
}
|
|
|
|
void ViewBase::Draw () {
|
|
// Perform pre-Draw actions
|
|
PreDraw ();
|
|
|
|
// Actual Drawing
|
|
UpdateCamera ();
|
|
|
|
if (mDrawGrid)
|
|
DrawGrid ();
|
|
|
|
DrawWorld ();
|
|
|
|
mOverlayManager.Draw();
|
|
|
|
// Perform post-Draw actions
|
|
PostDraw();
|
|
}
|
|
|
|
void ViewBase::PostDraw() {
|
|
SDL_GL_SwapBuffers ();
|
|
}
|
|
|
|
void ViewBase::SetFullscreen (bool fullscreen) {
|
|
if (fullscreen && mDrawFullscreen)
|
|
return;
|
|
|
|
if (!fullscreen && !mDrawFullscreen)
|
|
return;
|
|
|
|
if (mDrawFullscreen) {
|
|
mWindowHeight = VIEW_DEFAULT_HEIGHT;
|
|
mWindowWidth = VIEW_DEFAULT_WIDTH;
|
|
|
|
mDrawFullscreen = false;
|
|
} else {
|
|
mWindowWidth = mScreenWidth;
|
|
mWindowHeight = mScreenHeight;
|
|
|
|
mDrawFullscreen = true;
|
|
}
|
|
|
|
Resize (mWindowWidth, mWindowHeight);
|
|
}
|
|
|
|
/* Fonts */
|
|
|
|
/** \brief Parses font specifications strings into its values
|
|
*
|
|
* Fonts can be specified by strings such as
|
|
* name=default.ttf color=#ff0000 size=12
|
|
* color=#ff00ff size=12 otherfont.ttf
|
|
* myfont.ttf
|
|
* for which the appropriate values will be parsed. Default size is 12 and
|
|
* default color is white (#ffffff)
|
|
*/
|
|
void parse_font_spec_string (const std::string &spec_string,
|
|
std::string &font_name,
|
|
float font_color[3],
|
|
float *font_size) {
|
|
// set default values
|
|
font_name = "default.ttf";
|
|
font_color[0] = 1.f;
|
|
font_color[1] = 1.f;
|
|
font_color[2] = 1.f;
|
|
*font_size = 12.f;
|
|
|
|
// perform the actual parsing
|
|
std::string::size_type pos = 0;
|
|
|
|
while (pos < spec_string.size()) {
|
|
std::string::size_type next_start = spec_string.find_first_not_of(" \t", pos);
|
|
// if we don't find anything we break out of this loop
|
|
if (next_start == std::string::npos)
|
|
break;
|
|
|
|
// find the next token
|
|
std::string::size_type next_end = spec_string.find_first_of("\t ", next_start);
|
|
std::string token (spec_string.substr(next_start, next_end - next_start));
|
|
|
|
// If no id was given it is automatically set to name
|
|
std::string id("name");
|
|
std::string value(token);
|
|
|
|
// Split the token into id and value if there is an '=' sign.
|
|
if (token.find('=', 0) != std::string::npos) {
|
|
id = token.substr(0, token.find('=',0));
|
|
value = token.substr(id.size() + 1, token.size());
|
|
}
|
|
|
|
// std::cout << "id = " << id << " value = " << value << std::endl;
|
|
|
|
if (id == "name")
|
|
font_name = value;
|
|
else if (id == "size") {
|
|
std::istringstream istr(value);
|
|
istr >> *font_size;
|
|
} else if (id == "color") {
|
|
// we assume #RRGGBB specification
|
|
if (value[0] != '#' || value.size() != 7)
|
|
LogError ("Invalid font color specification '%s'. Please specify as #rrggbb!", value.c_str());
|
|
|
|
// convert the color string to float values
|
|
int num;
|
|
std::istringstream istr(value.substr(1, 2));
|
|
istr >> std::hex >> num;
|
|
font_color[0] = static_cast<float>(num) / 255.;
|
|
|
|
istr.clear();
|
|
istr.str(value.substr(3,2));
|
|
istr >> std::hex >> num;
|
|
font_color[1] = static_cast<float>(num) / 255.;
|
|
|
|
istr.clear();
|
|
istr.str(value.substr(5,2));
|
|
istr >> std::hex >> num;
|
|
font_color[2] = static_cast<float>(num) / 255.;
|
|
}
|
|
|
|
pos = next_end;
|
|
}
|
|
}
|
|
|
|
bool ViewBase::LoadFont (const std::string &font_spec_string) {
|
|
// font_spec_string can be of the form
|
|
// (name=)<name> (color=<color>) (size=<size>)
|
|
|
|
std::string font_name;
|
|
float font_size = 12.;
|
|
float font_color[3] = {1., 1., 1.};
|
|
|
|
parse_font_spec_string(font_spec_string, font_name, font_color, &font_size);
|
|
|
|
std::string font_path (GetResourceFullPath("/data/fonts/"));
|
|
font_path += font_name;
|
|
|
|
LogDebug ("Loading font %s color (%1.2f, %1.2f, %1.2f) size %f from %s", font_name.c_str(), font_color[0], font_color[1], font_color[2], font_size, font_path.c_str());
|
|
|
|
OGLFT::Monochrome *font = new OGLFT::Monochrome (font_path.c_str(), font_size);
|
|
if ( font == 0 || !font->isValid() ) {
|
|
LogError ("Could not load font %s! (ptr=%x", font_path.c_str(), (void*)font);
|
|
return false;
|
|
}
|
|
|
|
font->setForegroundColor(font_color[0], font_color[1], font_color[2], 1.);
|
|
font->setBackgroundColor(0., 0., 0., 0.);
|
|
|
|
mFonts.insert(std::make_pair<std::string, OGLFT::Monochrome*>(font_spec_string, font));
|
|
LogDebug ("Loading font %s successful!", font_name.c_str());
|
|
return true;
|
|
}
|
|
|
|
void ViewBase::SelectFont (const char *font) {
|
|
std::map<std::string, OGLFT::Monochrome*>::iterator font_iter;
|
|
|
|
font_iter = mFonts.find(font);
|
|
|
|
if (font_iter != mFonts.end()) {
|
|
mCurrentFont = font_iter->second;
|
|
return;
|
|
}
|
|
|
|
LogDebug ("Selecting font %s failed, trying to load it", font);
|
|
|
|
if (LoadFont (font)) {
|
|
LogDebug ("font count = %d", mFonts.size());
|
|
font_iter = mFonts.find(font);
|
|
|
|
assert (mFonts.find(font) != mFonts.end());
|
|
|
|
mCurrentFont = font_iter->second;
|
|
|
|
return;
|
|
}
|
|
|
|
LogError("Error trying to load font %s", font);
|
|
}
|
|
|
|
float ViewBase::GetCurrentFontSize() {
|
|
if (mCurrentFont == NULL) {
|
|
LogError ("Could not get current font size: no font selected!");
|
|
return 0.;
|
|
}
|
|
|
|
return mCurrentFont->pointSize();
|
|
}
|
|
|
|
void ViewBase::SetFontJustification (FontJustification justification) {
|
|
assert (mCurrentFont != NULL);
|
|
|
|
if (justification == FontJustificationRight)
|
|
mCurrentFont->setHorizontalJustification(OGLFT::Face::RIGHT);
|
|
else if (justification == FontJustificationCenter)
|
|
mCurrentFont->setHorizontalJustification(OGLFT::Face::CENTER);
|
|
else mCurrentFont->setHorizontalJustification(OGLFT::Face::LEFT);
|
|
}
|
|
|
|
void ViewBase::DrawGLString (float x, float y, const char* str) {
|
|
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
|
|
mCurrentFont->draw (x, y, str);
|
|
}
|
|
|
|
void ViewBase::DrawGLStringMeasure (const char* str, float *width, float *height) {
|
|
OGLFT::BBox bbox = mCurrentFont->measure (str);
|
|
*width = bbox.x_max_ - bbox.x_min_;
|
|
*height = bbox.y_max_ - bbox.y_min_;
|
|
// LogDebug ("measure bbox '%s' = %f,%f %f,%f",str, bbox.x_min_, bbox.y_min_, bbox.x_max_, bbox.y_max_);
|
|
}
|
|
|
|
GLuint ViewBase::LoadTextureFromPNG (const std::string &filename) {
|
|
std::map<std::string, GLuint>::iterator texture_iterator = mGLTextures.find(filename);
|
|
|
|
if (texture_iterator != mGLTextures.end()) {
|
|
return texture_iterator->second;
|
|
}
|
|
|
|
Sprite temp_sprite;
|
|
temp_sprite.LoadFromPNG (filename.c_str());
|
|
|
|
// register the texture in the View
|
|
mGLTextures[filename] = temp_sprite.GetGLTextureName();
|
|
|
|
return temp_sprite.GetGLTextureName();
|
|
}
|
|
|
|
/*
|
|
* OBJModel loading and drawing
|
|
*/
|
|
|
|
|
|
OBJModelPtr ViewBase::LoadOBJModel (const std::string &model_filename) {
|
|
LogDebug ("Loading OBJ model %s", model_filename.c_str());
|
|
|
|
OBJModelPtr result_model (new OBJModel);
|
|
|
|
if(!result_model->import (model_filename.c_str(), true)) {
|
|
LogError("Could not load model %s", model_filename.c_str());
|
|
return result_model;
|
|
}
|
|
|
|
result_model->normalize();
|
|
result_model->reverseWinding();
|
|
|
|
// Load any associated textures.
|
|
// Note the path where the textures are assumed to be located.
|
|
|
|
const OBJModel::Material *pMaterial = 0;
|
|
GLuint textureId = 0;
|
|
std::string::size_type offset = 0;
|
|
std::string filename;
|
|
|
|
for (int i = 0; i < result_model->getNumberOfMaterials(); ++i)
|
|
{
|
|
pMaterial = &result_model->getMaterial(i);
|
|
|
|
// Look for and load any diffuse color map textures.
|
|
if (!pMaterial->colorMapFilename.empty()) {
|
|
// Try load the texture using the path in the .MTL file.
|
|
textureId = LoadTextureFromPNG(pMaterial->colorMapFilename.c_str());
|
|
|
|
if (!textureId)
|
|
{
|
|
offset = pMaterial->colorMapFilename.find_last_of('\\');
|
|
|
|
if (offset != std::string::npos)
|
|
filename = pMaterial->colorMapFilename.substr(++offset);
|
|
else
|
|
filename = pMaterial->colorMapFilename;
|
|
|
|
// Try loading the texture from the same directory as the OBJ file.
|
|
textureId = LoadTextureFromPNG((filename).c_str());
|
|
}
|
|
} else {
|
|
LogMessage ("No diffuse color map found!");
|
|
}
|
|
|
|
// Look for and load any normal map textures.
|
|
if (!pMaterial->bumpMapFilename.empty()) {
|
|
|
|
// Try load the texture using the path in the .MTL file.
|
|
textureId = LoadTextureFromPNG((pMaterial->bumpMapFilename).c_str());
|
|
|
|
if (!textureId)
|
|
{
|
|
offset = pMaterial->bumpMapFilename.find_last_of('\\');
|
|
|
|
if (offset != std::string::npos)
|
|
filename = pMaterial->bumpMapFilename.substr(++offset);
|
|
else
|
|
filename = pMaterial->bumpMapFilename;
|
|
|
|
// Try loading the texture from the same directory as the OBJ file.
|
|
textureId = LoadTextureFromPNG((result_model->getPath() + filename).c_str());
|
|
}
|
|
} else {
|
|
LogMessage ("Material has no bumpmap!");
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
// Update the window caption.
|
|
|
|
LogMessage ("Loaded model %s successful, dimensions (whl): %f, %f, %f meshes %d vertices %d triangles: %d",
|
|
model_filename.c_str(),
|
|
result_model->getWidth(),
|
|
result_model->getHeight(),
|
|
result_model->getLength(),
|
|
result_model->getNumberOfMeshes(),
|
|
result_model->getNumberOfVertices(),
|
|
result_model->getNumberOfTriangles()
|
|
);
|
|
|
|
return result_model;
|
|
}
|
|
|
|
void ViewBase::DrawOBJModel (OBJModelPtr obj_model) {
|
|
if (mUseShaders) {
|
|
DrawOBJModelShaded(obj_model);
|
|
} else {
|
|
DrawOBJModelSolid (obj_model);
|
|
}
|
|
}
|
|
|
|
void ViewBase::DrawOBJModelSolid (OBJModelPtr obj_model) {
|
|
const OBJModel::Mesh *pMesh = 0;
|
|
const OBJModel::Material *pMaterial = 0;
|
|
const OBJModel::Vertex *pVertices = 0;
|
|
std::map<std::string, GLuint>::const_iterator iter;
|
|
|
|
for (int i = 0; i < obj_model->getNumberOfMeshes(); ++i)
|
|
{
|
|
pMesh = &obj_model->getMesh(i);
|
|
pMaterial = pMesh->pMaterial;
|
|
pVertices = obj_model->getVertexBuffer();
|
|
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
|
|
|
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);
|
|
|
|
if (pMaterial->colorMapFilename.size() > 0)
|
|
{
|
|
iter = mGLTextures.find(pMaterial->colorMapFilename);
|
|
|
|
if (iter == mGLTextures.end())
|
|
{
|
|
LogError ("Could not find required colormap '%s'", pMaterial->colorMapFilename.c_str());
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
else
|
|
{
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, iter->second);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
if (obj_model->hasPositions())
|
|
{
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glVertexPointer(3, GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->position);
|
|
}
|
|
|
|
if (obj_model->hasTextureCoords())
|
|
{
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glTexCoordPointer(2, GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->texCoord);
|
|
}
|
|
|
|
if (obj_model->hasNormals())
|
|
{
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
glNormalPointer(GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->normal);
|
|
}
|
|
|
|
glDrawElements(GL_TRIANGLES, pMesh->triangleCount * 3, GL_UNSIGNED_INT,
|
|
obj_model->getIndexBuffer() + pMesh->startIndex);
|
|
|
|
if (obj_model->hasNormals())
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
if (obj_model->hasTextureCoords())
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
if (obj_model->hasPositions())
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
// disable texture drawing if active
|
|
if (pMaterial->colorMapFilename.size() > 0)
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
}
|
|
|
|
void ViewBase::DrawOBJModelShaded (OBJModelPtr obj_model) {
|
|
const OBJModel::Mesh *pMesh = 0;
|
|
const OBJModel::Material *pMaterial = 0;
|
|
const OBJModel::Vertex *pVertices = 0;
|
|
std::map<std::string, GLuint>::const_iterator iter;
|
|
GLuint texture = 0;
|
|
|
|
for (int i = 0; i < obj_model->getNumberOfMeshes(); ++i)
|
|
{
|
|
pMesh = &obj_model->getMesh(i);
|
|
pMaterial = pMesh->pMaterial;
|
|
pVertices = obj_model->getVertexBuffer();
|
|
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
|
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);
|
|
|
|
// if there is no bumpmap we will simply use a blinn phong shader and draw
|
|
// the color map onto our model
|
|
if (pMaterial->bumpMapFilename.empty())
|
|
{
|
|
// LogMessage ("using Blinn Phong shader");
|
|
// Per fragment Blinn-Phong code path.
|
|
glUseProgram(mBlinnPhongShader);
|
|
|
|
// Bind the color map texture.
|
|
|
|
texture = mNullTexture;
|
|
|
|
if (pMaterial->colorMapFilename.size() > 0) {
|
|
iter = mGLTextures.find(pMaterial->colorMapFilename);
|
|
|
|
if (iter != mGLTextures.end())
|
|
texture = iter->second;
|
|
|
|
} else {
|
|
// LogMessage ("Disabling textures");
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
|
// Update shader parameters.
|
|
assert (glIsProgram (mBlinnPhongShader) == GL_TRUE);
|
|
assert (glGetUniformLocation(mBlinnPhongShader, "materialAlpha") != -1);
|
|
assert (glGetUniformLocation(mBlinnPhongShader, "colorMap") != -1);
|
|
glUniform1i(glGetUniformLocation(
|
|
mBlinnPhongShader, "colorMap"), 0);
|
|
glUniform1f(glGetUniformLocation(
|
|
mBlinnPhongShader, "materialAlpha"), pMaterial->alpha);
|
|
}
|
|
else
|
|
{
|
|
// if there is a bumpmap we use both the bump map and the color map and
|
|
// apply it on our model
|
|
|
|
// LogMessage ("using Normal Mapping Shader");
|
|
// Normal mapping code path.
|
|
|
|
glUseProgram(mNormalMappingShader);
|
|
|
|
// Bind the normal map texture.
|
|
|
|
iter = mGLTextures.find(pMaterial->bumpMapFilename);
|
|
|
|
if (iter != mGLTextures.end()) {
|
|
texture = iter->second;
|
|
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
} else {
|
|
LogError ("bumpmap %s not found", pMaterial->bumpMapFilename.c_str());
|
|
}
|
|
|
|
// Bind the color map texture.
|
|
|
|
texture = mNullTexture;
|
|
|
|
if (pMaterial->colorMapFilename.size() > 0)
|
|
{
|
|
iter = mGLTextures.find(pMaterial->colorMapFilename);
|
|
|
|
if (iter != mGLTextures.end()) {
|
|
texture = iter->second;
|
|
} else {
|
|
LogError ("color map %s not found", pMaterial->colorMapFilename.c_str());
|
|
}
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
|
// Update shader parameters.
|
|
assert (glGetUniformLocation(mNormalMappingShader, "colorMap") != -1);
|
|
assert (glGetUniformLocation(mNormalMappingShader, "normalMap") != -1);
|
|
assert (glGetUniformLocation(mNormalMappingShader, "materialAlpha") != -1);
|
|
|
|
glUniform1i(glGetUniformLocation(
|
|
mNormalMappingShader, "colorMap"), 0);
|
|
glUniform1i(glGetUniformLocation(
|
|
mNormalMappingShader, "normalMap"), 1);
|
|
glUniform1f(glGetUniformLocation(
|
|
mNormalMappingShader, "materialAlpha"), pMaterial->alpha);
|
|
}
|
|
|
|
// Render mesh.
|
|
|
|
if (obj_model->hasPositions())
|
|
{
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glVertexPointer(3, GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->position);
|
|
}
|
|
|
|
if (obj_model->hasTextureCoords())
|
|
{
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glTexCoordPointer(2, GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->texCoord);
|
|
}
|
|
|
|
if (obj_model->hasNormals())
|
|
{
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
glNormalPointer(GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->normal);
|
|
}
|
|
|
|
if (obj_model->hasTangents())
|
|
{
|
|
glClientActiveTexture(GL_TEXTURE1);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glTexCoordPointer(4, GL_FLOAT, obj_model->getVertexSize(),
|
|
obj_model->getVertexBuffer()->tangent);
|
|
}
|
|
|
|
glDrawElements(GL_TRIANGLES, pMesh->triangleCount * 3, GL_UNSIGNED_INT,
|
|
obj_model->getIndexBuffer() + pMesh->startIndex);
|
|
|
|
if (obj_model->hasTangents())
|
|
{
|
|
glClientActiveTexture(GL_TEXTURE1);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
if (obj_model->hasNormals())
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
if (obj_model->hasTextureCoords())
|
|
{
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
if (obj_model->hasPositions())
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
}
|
|
|
|
// as we might be using multiple textures (at least for the normal mapping
|
|
// case) we have to disable both textures
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glUseProgram(0);
|
|
}
|
|
|
|
/*
|
|
* Shader loading, compiling, linking
|
|
*/
|
|
/** Compiles a shader program
|
|
*
|
|
* This function contains code mainly taken from dhpowares excellent
|
|
* objviewer example (see http://www.dhpoware.com/demos/gl3NormalMapping.html ).
|
|
*/
|
|
GLuint ViewBase::CompileShader (GLenum type, const GLchar *source, GLint length) {
|
|
GLuint shader = glCreateShader(type);
|
|
|
|
if (shader) {
|
|
GLint compiled = 0;
|
|
|
|
glShaderSource (shader, 1, &source, &length);
|
|
glCompileShader(shader);
|
|
glGetShaderiv (shader, GL_COMPILE_STATUS, &compiled);
|
|
|
|
if (!compiled) {
|
|
GLsizei info_log_size = 0;
|
|
std::string info_log;
|
|
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_size);
|
|
info_log.resize (info_log_size);
|
|
glGetShaderInfoLog (shader, info_log_size, &info_log_size, &info_log[0]);
|
|
|
|
LogError ("Error compiling shader: %s", info_log.c_str());
|
|
}
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
/** Links the given shader programs
|
|
*
|
|
* This function contains code mainly taken from dhpowares excellent
|
|
* objviewer example (see http://www.dhpoware.com/demos/gl3NormalMapping.html ).
|
|
*/
|
|
GLuint ViewBase::LinkShaders (GLuint vertex_shader, GLuint fragment_shader) {
|
|
GLuint program = glCreateProgram();
|
|
|
|
if (program) {
|
|
GLint linked = 0;
|
|
|
|
if (vertex_shader)
|
|
glAttachShader (program, vertex_shader);
|
|
|
|
if (fragment_shader)
|
|
glAttachShader (program, fragment_shader);
|
|
|
|
glLinkProgram (program);
|
|
glGetProgramiv (program, GL_LINK_STATUS, &linked);
|
|
|
|
if (!linked) {
|
|
GLsizei info_log_size = 0;
|
|
std::string info_log;
|
|
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_size);
|
|
info_log.resize (info_log_size);
|
|
glGetProgramInfoLog (program, info_log_size, &info_log_size, &info_log[0]);
|
|
|
|
LogError ("Error linking shaders vert: %d, frag: %d", vertex_shader, fragment_shader);
|
|
}
|
|
|
|
if (vertex_shader)
|
|
glDeleteShader (vertex_shader);
|
|
|
|
if (fragment_shader)
|
|
glDeleteShader (fragment_shader);
|
|
}
|
|
|
|
return program;
|
|
}
|
|
|
|
|
|
/** Loads a vertex or fragment shader program
|
|
*
|
|
* This function contains code mainly taken from dhpowares excellent
|
|
* objviewer example (see http://www.dhpoware.com/demos/gl3NormalMapping.html ).
|
|
*/
|
|
GLuint ViewBase::LoadShaderProgram (const std::string &filename) {
|
|
LogDebug ("Loading shader program %s", filename.c_str());
|
|
|
|
std::ifstream program_file (filename.c_str());
|
|
if (!program_file) {
|
|
LogError ("Could not open file '%s' while loading shader program", filename.c_str());
|
|
}
|
|
|
|
// read the whole file into a string
|
|
std::string shader_program ((std::istreambuf_iterator<char>(program_file)), std::istreambuf_iterator<char>());
|
|
|
|
program_file.close();
|
|
|
|
GLuint program = 0;
|
|
|
|
if (shader_program.size() > 0) {
|
|
const GLchar *source;
|
|
GLint length = 0;
|
|
GLuint vertex_shader = 0;
|
|
GLuint fragment_shader = 0;
|
|
|
|
std::string::size_type vertex_shader_offset = shader_program.find("[vert]");
|
|
std::string::size_type fragment_shader_offset = shader_program.find("[frag]");
|
|
|
|
if (vertex_shader_offset != std::string::npos) {
|
|
// skip over the [vert] tag
|
|
vertex_shader_offset += 6;
|
|
source = reinterpret_cast<const GLchar *> (&shader_program[vertex_shader_offset]);
|
|
length = static_cast<GLint>(fragment_shader_offset - vertex_shader_offset);
|
|
vertex_shader = CompileShader (GL_VERTEX_SHADER, source, length);
|
|
|
|
LogMessage ("Compiled vertex shader with id %d", vertex_shader);
|
|
}
|
|
|
|
if (fragment_shader_offset != std::string::npos) {
|
|
// skip over the [vert] tag
|
|
fragment_shader_offset += 6;
|
|
source = reinterpret_cast<const GLchar *> (&shader_program[fragment_shader_offset]);
|
|
length = static_cast<GLint>(shader_program.length() - fragment_shader_offset - 1);
|
|
fragment_shader = CompileShader (GL_FRAGMENT_SHADER, source, length);
|
|
|
|
LogMessage ("Compiled fragment shader with id %d", fragment_shader);
|
|
}
|
|
|
|
program = LinkShaders (vertex_shader, fragment_shader);
|
|
|
|
LogMessage ("Successfully linked shaders vert: %d frag: %d from file %s into program %d", vertex_shader, fragment_shader, filename.c_str(), program);
|
|
}
|
|
|
|
mShaderPrograms[filename] = program;
|
|
|
|
return program;
|
|
}
|
|
|
|
/*
|
|
* Camera and other auxillary functions
|
|
*/
|
|
|
|
void ViewBase::GetCamereEye (float *eye_out) {
|
|
assert (mCamera);
|
|
mCamera->GetEye (eye_out);
|
|
}
|
|
|
|
void ViewBase::DrawGrid () {
|
|
float xmin, xmax, xstep, zmin, zmax, zstep;
|
|
int i, count_x, count_z;
|
|
|
|
xmin = -mGridSizeX;
|
|
xmax = mGridSizeX;
|
|
zmin = -mGridSizeZ;
|
|
zmax = mGridSizeZ;
|
|
|
|
count_x = mGridSizeX * 2;
|
|
count_z = mGridSizeZ * 2;
|
|
|
|
xstep = 1.;
|
|
zstep = 1.;
|
|
|
|
glColor3f (0.8, 0.8, 0.8);
|
|
glLineWidth(1.);
|
|
glBegin (GL_LINES);
|
|
for (i = 0; i <= count_x; i++) {
|
|
glVertex3f (i * xstep + xmin, 0., zmin);
|
|
glVertex3f (i * xstep + xmin, 0., zmax);
|
|
}
|
|
for (i = 0; i <= count_z; i++) {
|
|
glVertex3f (xmin, 0, i * zstep + zmin);
|
|
glVertex3f (xmax, 0, i * zstep + zmin);
|
|
}
|
|
glEnd ();
|
|
}
|
|
|
|
void ViewBase::DrawWorld () {
|
|
}
|
|
|
|
void ViewBase::Resize (int width, int height) {
|
|
if (height == 0)
|
|
height = 1;
|
|
|
|
mWindowWidth = static_cast<unsigned int> (width);
|
|
mWindowHeight = static_cast<unsigned int> (height);
|
|
|
|
glViewport(0, 0, width, height);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(mCamera->GetFOVY (), float (width) / float (height), 0., 100);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity ();
|
|
|
|
LogDebug ("Resize to: %d x %d", mWindowWidth,mWindowHeight);
|
|
|
|
/** \warning
|
|
* This call has to be made for SDL 1.2 for 1.3 there seems to be a
|
|
* workaround, however since I do not yet run SDL 1.3 I hold on to this.
|
|
* See http://lists.libsdl.org/pipermail/sdl-libsdl.org/2008-November/067306.html
|
|
*/
|
|
Uint32 video_flags = SDL_OPENGL;
|
|
|
|
if (mDrawFullscreen)
|
|
video_flags = video_flags | SDL_FULLSCREEN;
|
|
|
|
if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, video_flags) == 0 ) {
|
|
LogError ("Video mode set failed: %s", SDL_GetError ());
|
|
exit (-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
bool ViewBase::SendKeyDown (const SDL_keysym &keysym) {
|
|
std::vector<OverlayBasePtr>::iterator overlay_iter;
|
|
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
|
|
if ( (*overlay_iter)->OnKeyDown (keysym))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewBase::SendKeyUp (const SDL_keysym &keysym) {
|
|
std::vector<OverlayBasePtr>::iterator overlay_iter;
|
|
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
|
|
if ( (*overlay_iter)->OnKeyUp (keysym))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewBase::SendMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) {
|
|
std::vector<OverlayBasePtr>::iterator overlay_iter;
|
|
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
|
|
if ( (*overlay_iter)->OnMouseButtonUp (button, xpos, ypos))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewBase::SendMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) {
|
|
std::vector<OverlayBasePtr>::iterator overlay_iter;
|
|
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
|
|
if ( (*overlay_iter)->OnMouseButtonDown (button, xpos, ypos))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* Global functions
|
|
*/
|
|
void DrawGLString (float x, float y, const char* str) {
|
|
if (!ViewInstance) {
|
|
LogError ("Cannot Draw GL String: View not yet initialized!");
|
|
return;
|
|
}
|
|
ViewInstance->DrawGLString (x, y, str);
|
|
}
|
|
|
|
void SelectFont (const char* font) {
|
|
if (!ViewInstance) {
|
|
LogError ("Cannot select font: View not yet initialized!");
|
|
return;
|
|
}
|
|
ViewInstance->SelectFont(font);
|
|
}
|
|
|
|
void SetFontJustification (FontJustification justification) {
|
|
if (!ViewInstance) {
|
|
LogError ("Cannot select font: View not yet initialized!");
|
|
return;
|
|
}
|
|
ViewInstance->SetFontJustification (justification);
|
|
}
|
|
|
|
float GetCurrentFontSize() {
|
|
if (!ViewInstance) {
|
|
LogError ("Cannot get font size: View not yet initialized!");
|
|
return 0.;
|
|
}
|
|
return ViewInstance->GetCurrentFontSize();
|
|
}
|
|
|
|
unsigned int GetWindowWidth() {
|
|
return ViewInstance->GetWindowWidth ();
|
|
}
|
|
|
|
unsigned int GetWindowHeight() {
|
|
return ViewInstance->GetWindowHeight ();
|
|
}
|
|
|
|
int GetFrameRate () {
|
|
return ViewInstance->GetFrameRate ();
|
|
}
|
|
|
|
}
|