From 75a90df8bf7f38e746e021c23248e1607931132c Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Fri, 1 Jul 2011 17:20:53 +0200 Subject: Import and render tree models. Trees are loaded from the new trees.blend using assimp. Tree objects are then received from the server and rendered on the given terrain locations. Each chunk now holds a list of objects and coordinates, which can be used to easily add other models as well. Also moded the GLSL fog code to its own shader which can be linked in different programs. --- SConstruct | 1 + common | 2 +- game.cpp | 22 +++++++++ game.h | 1 + model.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++ model.h | 50 ++++++++++++++++++++ models/README | 3 ++ models/trees.blend | Bin 0 -> 3827560 bytes scene.cpp | 89 ++++++++++++++++++++++++++++++++++++ scene.h | 13 ++++++ shaders/fog_fragment.glsl | 9 ++++ shaders/terrain_fragment.glsl | 7 +-- shaders/tree_fragment.glsl | 22 +++++++++ shaders/tree_vertex.glsl | 14 ++++++ terrain.h | 5 ++ 15 files changed, 338 insertions(+), 4 deletions(-) create mode 100644 model.cpp create mode 100644 model.h create mode 100644 models/README create mode 100644 models/trees.blend create mode 100644 shaders/fog_fragment.glsl create mode 100644 shaders/tree_fragment.glsl create mode 100644 shaders/tree_vertex.glsl diff --git a/SConstruct b/SConstruct index 05a7038..eabcf02 100644 --- a/SConstruct +++ b/SConstruct @@ -26,6 +26,7 @@ else: env.ParseConfig('pkg-config --cflags --libs ftgl') env.ParseConfig('pkg-config --cflags --libs CEGUI') env.ParseConfig('pkg-config --cflags --libs CEGUI-OPENGL') + env.ParseConfig('pkg-config --cflags --libs assimp') if not GetOption('release'): env.Append(CPPFLAGS = ['-Wall', '-g']) diff --git a/common b/common index ff7f9de..6e74671 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ff7f9de199213ea6d4832c0b91f2a96f5edc6bb2 +Subproject commit 6e746716d6a5c72fbd42539c6d5d92da8830cb9e diff --git a/game.cpp b/game.cpp index e24bacf..1288b0c 100644 --- a/game.cpp +++ b/game.cpp @@ -88,6 +88,9 @@ void Game::handle_type(const boost::system::error_code& error, std::size_t bytes case message::MSG_TYPE_PLAYER: handle_player(); break; + case message::MSG_TYPE_OBJECT: + handle_object(); + break; default: std::cout << "unknown type: " << (int)*type << std::endl; } @@ -154,6 +157,25 @@ void Game::handle_player() { scene->players.push_back(Player::p(new Player(id, pos, name))); } +void Game::handle_object() { + message::Object m; + + m.recv(socket); + + // type is ignored for now + uint32_t type = m.get_type(); + Vector3 pos(m.get_pos()); + + Terrain::Chunk *chunk = scene->terrain->find_chunk(pos.x, pos.z); + if(!chunk) { + std::cerr << "got object for non-existing chunk, discarding" << std::endl; + return; + } + + pos.y = chunk->find(pos.x, pos.z)->get_height(pos.x, pos.z); + chunk->objects.push_back(Terrain::Chunk::ObjectPair(scene->tree, pos)); +} + Game& Game::get_instance() { if(!game) game = new Game(); diff --git a/game.h b/game.h index 6c58565..76c9413 100644 --- a/game.h +++ b/game.h @@ -30,6 +30,7 @@ class Game { void handle_chunk(); void handle_message(); void handle_player(); + void handle_object(); static Game& get_instance(); }; diff --git a/model.cpp b/model.cpp new file mode 100644 index 0000000..67e4561 --- /dev/null +++ b/model.cpp @@ -0,0 +1,104 @@ +#include "model.h" + +#include +#include + +namespace models { + +Mesh::Mesh(const aiScene *scene, const aiMesh *mesh, std::map& scene_textures) { + aiMaterial *mat = scene->mMaterials[mesh->mMaterialIndex]; + unsigned int texture_count = mat->GetTextureCount(aiTextureType_DIFFUSE); + for(unsigned int j = 0; j < texture_count; j++) { + aiString ai_path; + if(mat->GetTexture(aiTextureType_DIFFUSE, j, &ai_path) == AI_SUCCESS) { + std::string path(ai_path.data, ai_path.length); + GLuint tex = scene_textures[path]; + textures.push_back(tex); + } + } + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + vertices = 0; + + for(unsigned int i = 0; i < mesh->mNumFaces; i++) + vertices += mesh->mFaces[i].mNumIndices; + + glBufferData(GL_ARRAY_BUFFER, vertices*3*2*3 * sizeof(float), NULL, GL_STATIC_DRAW); + + float *buffer = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + + unsigned int index = 0; + + for(unsigned int i = 0; i < mesh->mNumFaces; i++) { + aiFace *face = &mesh->mFaces[i]; + + for(unsigned int j = 0; j < face->mNumIndices; j++) { + aiVector3D *texcoord = &mesh->mTextureCoords[0][face->mIndices[j]]; + aiVector3D *vertex = &mesh->mVertices[face->mIndices[j]]; + aiVector3D *normal = &mesh->mNormals[face->mIndices[j]]; + + buffer[index++] = vertex->x; + buffer[index++] = vertex->y; + buffer[index++] = vertex->z; + + buffer[index++] = texcoord->x; + buffer[index++] = 2 - texcoord->y; + + buffer[index++] = normal->x; + buffer[index++] = normal->y; + buffer[index++] = normal->z; + } + } + + glUnmapBuffer(GL_ARRAY_BUFFER); +} + +Mesh::~Mesh() { + glDeleteBuffers(1, &vbo); +} + +void Mesh::render() { + GLenum tex_i = GL_TEXTURE0; + for(std::vector::iterator it = textures.begin(); it != textures.end(); it++) { + glActiveTexture(tex_i++); + glBindTexture(GL_TEXTURE_2D, *it); + } + glActiveTexture(GL_TEXTURE0); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + glVertexPointer(3, GL_FLOAT, 8*sizeof(float), NULL); + glTexCoordPointer(2, GL_FLOAT, 8*sizeof(float), (const GLvoid*)(sizeof(float)*3)); + glNormalPointer(GL_FLOAT, 8*sizeof(float), (const GLvoid*)(sizeof(float)*5)); + glDrawArrays(GL_TRIANGLES, 0, vertices); + + glPopClientAttrib(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +Tree::Tree(const aiScene *scene, std::map& scene_textures) { + trunk = new Mesh(scene, scene->mMeshes[22], scene_textures); + leaves = new Mesh(scene, scene->mMeshes[3], scene_textures); +} + +Tree::~Tree() { + delete trunk; + delete leaves; +} + +void Tree::render() { + trunk->render(); + glTranslatef(0.786, 2.845, 5.1); + leaves->render(); +} + +} // namespace models diff --git a/model.h b/model.h new file mode 100644 index 0000000..7659d9c --- /dev/null +++ b/model.h @@ -0,0 +1,50 @@ +#ifndef MODEL_H +#define MODEL_H + +#include "gl.h" + +#include +#include + +#include +#include +#include + +namespace models { + +class Mesh { + private: + GLuint vbo; + GLuint vertices; + + public: + Mesh(const aiScene *scene, const aiMesh *mesh, std::map& scene_textures); + virtual ~Mesh(); + + void render(); + + std::vector textures; +}; + +class Model { + public: + virtual ~Model() {}; + + virtual void render() = 0; +}; + +class Tree : public Model { + private: + Mesh *trunk; + Mesh *leaves; + + public: + Tree(const aiScene *scene, std::map& scene_textures); + virtual ~Tree(); + + virtual void render(); +}; + +} + +#endif diff --git a/models/README b/models/README new file mode 100644 index 0000000..5d31225 --- /dev/null +++ b/models/README @@ -0,0 +1,3 @@ +File: trees.blend +License: CC-BY 3.0 +Source: http://opengameart.org/content/low-poly-trees-stumps-branches diff --git a/models/trees.blend b/models/trees.blend new file mode 100644 index 0000000..108feb6 Binary files /dev/null and b/models/trees.blend differ diff --git a/scene.cpp b/scene.cpp index 1fda39e..4e884a6 100644 --- a/scene.cpp +++ b/scene.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "gl.h" @@ -27,10 +28,13 @@ Scene::Scene() { tool = NULL; /* setup shader programs */ + GLFragmentShader fog_fragment("shaders/fog_fragment.glsl"); + GLVertexShader terrain_vertex("shaders/terrain_vertex.glsl"); GLFragmentShader terrain_fragment("shaders/terrain_fragment.glsl"); terrain_program.attach(terrain_vertex); terrain_program.attach(terrain_fragment); + terrain_program.attach(fog_fragment); terrain_program.link(); GLFragmentShader water_fragment("shaders/water_fragment.glsl"); @@ -38,12 +42,24 @@ Scene::Scene() { water_program.attach(water_fragment); water_program.link(); + GLVertexShader tree_vertex("shaders/tree_vertex.glsl"); + GLFragmentShader tree_fragment("shaders/tree_fragment.glsl"); + tree_program.attach(tree_vertex); + tree_program.attach(tree_fragment); + tree_program.attach(fog_fragment); + tree_program.link(); + terrain_program.use(); GLint tex = glGetUniformLocation(terrain_program.get_program(), "tex"); GLint texv[] = {0, 1, 2}; glUniform1iv(tex, 3, texv); GLint markloc = glGetUniformLocation(terrain_program.get_program(), "marktex"); glUniform1i(markloc, 3); + + tree_program.use(); + tex = glGetUniformLocation(tree_program.get_program(), "tex"); + glUniform1iv(tex, 1, texv); + glUseProgram(0); /* load textures */ @@ -54,6 +70,17 @@ Scene::Scene() { marker_texture = load_texture("textures/cross.png"); placeholder_texture = load_texture("textures/placeholder.png"); + tree_scene = ai_importer.ReadFile("models/trees.blend", aiProcess_Triangulate); + + for(unsigned int i = 0; i < tree_scene->mNumTextures; i++) { + aiTexture *texture = tree_scene->mTextures[i]; + GLuint tx = load_texture(texture); + std::string name = (boost::format("*%d") % i).str(); + scene_textures.insert(std::pair(name, tx)); + } + + tree = new models::Tree(tree_scene, scene_textures); + /* init terrain */ terrain = new Terrain(); @@ -78,6 +105,7 @@ Scene::~Scene() { delete lua; if(tool) delete tool; + delete tree; if(terrain) delete terrain; delete font; @@ -380,6 +408,8 @@ void Scene::render() { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); + Terrain::Chunk::ObjectList trees; + // render terrain if(render_terrain) { const float fog_color[4] = {1, 1, 1, 0}; @@ -417,6 +447,9 @@ void Scene::render() { // draw chunk VBOs for(std::list::iterator it = terrain->chunks.begin(); it != terrain->chunks.end(); it++) { Terrain::Chunk *chunk = *it; + + trees.insert(trees.end(), chunk->objects.begin(), chunk->objects.end()); + glPushMatrix(); glTranslatef(-pos.x + chunk->x, -pos.y, -pos.z + chunk->y); glUniform2f(chunk_pos, chunk->x, chunk->y); @@ -548,7 +581,39 @@ void Scene::render() { (*it)->render(font, steps, placeholder_texture); } + // don't pop matrix; use translated player position + + // rotate to point upwards along the y-aksis + glRotatef(-90, 1, 0, 0); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + tree_program.use(); + + GLint player_pos = glGetUniformLocation(tree_program.get_program(), "player_pos"); + glUniform3f(player_pos, pos.x, pos.y, pos.z); + + GLint tree_pos = glGetUniformLocation(tree_program.get_program(), "tree_pos"); + + //tree->render(); + for(Terrain::Chunk::ObjectList::iterator it = trees.begin(); it != trees.end(); it++) { + glPushMatrix(); + Vector3 pos(it->second); + glUniform3f(tree_pos, pos.x, pos.y, pos.z); + // rotated around x-axis; swap y and z, do magic to fix positions + glTranslatef(pos.x, -pos.z, pos.y+.5); + it->first->render(); + glPopMatrix(); + } + glPopMatrix(); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + glUseProgram(0); + + // active texture # must be reset before rendering UI stuff + glActiveTexture(GL_TEXTURE0); // HUD video::ortho(); @@ -608,6 +673,30 @@ GLuint Scene::load_texture(const char *filename) { return texture; } +GLuint Scene::load_texture(aiTexture *texture) { + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if(texture->mHeight == 0) { + SDL_RWops *rw = SDL_RWFromConstMem(texture->pcData, texture->mWidth); + SDL_Surface *image = IMG_Load_RW(rw, 1); + if(image->format->Amask) { + gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image->w, image->h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); + } else { + gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->w, image->h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); + } + + SDL_FreeSurface(image); + } else { + gluBuild2DMipmaps(GL_TEXTURE_2D, 4, texture->mWidth, texture->mHeight, GL_BGRA, GL_UNSIGNED_BYTE, texture->pcData); + } + + return tex; +} + static bool playerlist_sort(Vector3 pos, Player::p a, Player::p b) { return (a->get_pos() - pos).length() > (b->get_pos() - pos).length(); } diff --git a/scene.h b/scene.h index 247b52c..6c75c91 100644 --- a/scene.h +++ b/scene.h @@ -9,8 +9,13 @@ #include "tool.h" #include "scripting.h" #include "player.h" +#include "model.h" #include +#include +#include + +using models::Model; class Scene { public: @@ -46,8 +51,15 @@ class Scene { GLShaderProgram terrain_program; GLShaderProgram water_program; + GLShaderProgram tree_program; GLuint grass_texture, rock_texture, soil_texture, water_texture, marker_texture, placeholder_texture; + std::map scene_textures; + + Assimp::Importer ai_importer; + const aiScene *tree_scene; + + Model *tree; Scene(); ~Scene(); @@ -59,6 +71,7 @@ class Scene { void events(); void render(); GLuint load_texture(const char *filename); + GLuint load_texture(aiTexture *texture); void sort_players(); }; diff --git a/shaders/fog_fragment.glsl b/shaders/fog_fragment.glsl new file mode 100644 index 0000000..90b4591 --- /dev/null +++ b/shaders/fog_fragment.glsl @@ -0,0 +1,9 @@ +#version 120 + +void foggify(vec3 player_pos, vec3 pos) { + gl_FragColor = mix(gl_FragColor, gl_Fog.color, pow(length(player_pos - pos)/100, 5)); + if(player_pos.y < 30 - 1.7) + gl_FragColor = mix(gl_FragColor, vec4(0, .3, .8, 0), .7); +} + +/* vim: set syn=glsl: */ diff --git a/shaders/terrain_fragment.glsl b/shaders/terrain_fragment.glsl index 8b966fd..d1f5892 100644 --- a/shaders/terrain_fragment.glsl +++ b/shaders/terrain_fragment.glsl @@ -7,6 +7,8 @@ uniform sampler2D marktex; uniform vec3 selpos, player_pos; uniform bool show_sel; +void foggify(vec3 player_pos, vec3 pos); + void main() { vec3 n = normalize(normal); float diffuse = max(dot(n, light_pos), 0.5); @@ -27,9 +29,8 @@ void main() { vec2 st = vec2((pos.x + 1 - selpos.x) / 2, (pos.z + 1 - selpos.z) / 2); gl_FragColor += texture2D(marktex, st); } - gl_FragColor = mix(gl_FragColor, gl_Fog.color, pow(length(player_pos - pos)/100, 5)); - if(player_pos.y < 30 - 1.7) - gl_FragColor = mix(gl_FragColor, vec4(0, .3, .8, 0), .7); + + foggify(player_pos, pos); } /* vim: set syn=glsl: */ diff --git a/shaders/tree_fragment.glsl b/shaders/tree_fragment.glsl new file mode 100644 index 0000000..382ab4e --- /dev/null +++ b/shaders/tree_fragment.glsl @@ -0,0 +1,22 @@ +#version 120 + +uniform sampler2D tex; +uniform vec3 player_pos; +varying vec3 normal, pos; + +void foggify(vec3 player_pos, vec3 pos); + +void main() { + vec4 color = texture2D(tex, gl_TexCoord[0].st); + + if(color.a < 0.7) + discard; + + gl_FragColor = color; + float n = clamp(dot(normal, vec3(0, 0, 1)) + .5, .3, 1); + gl_FragColor.rgb *= n; + + foggify(player_pos, pos); +} + +/* vim: set syn=glsl: */ diff --git a/shaders/tree_vertex.glsl b/shaders/tree_vertex.glsl new file mode 100644 index 0000000..4c06bee --- /dev/null +++ b/shaders/tree_vertex.glsl @@ -0,0 +1,14 @@ +#version 120 + +uniform vec3 tree_pos; +varying vec3 normal, pos; + +void main() { + normal = gl_Normal; + pos = gl_Vertex.xyz + tree_pos; + + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} + +/* vim: set syn=glsl: */ diff --git a/terrain.h b/terrain.h index 386f6e3..aff95a4 100644 --- a/terrain.h +++ b/terrain.h @@ -3,6 +3,7 @@ #include "vector.h" #include "terrain_cache.h" +#include "model.h" #include #include @@ -29,6 +30,9 @@ class Terrain { }; struct Chunk { + typedef std::pair ObjectPair; + typedef std::list ObjectList; + Terrain *terrain; Node **nodes; float x, y; @@ -42,6 +46,7 @@ class Terrain { unsigned int vbo_object; unsigned int node_count; unsigned int vertices; + ObjectList objects; Chunk(Terrain *tree, float x, float y); ~Chunk(); -- cgit v1.2.3