diff options
author | Jon Bergli Heier <snakebite@jvnv.net> | 2011-07-01 17:20:53 +0200 |
---|---|---|
committer | Jon Bergli Heier <snakebite@jvnv.net> | 2011-07-02 18:10:14 +0200 |
commit | 75a90df8bf7f38e746e021c23248e1607931132c (patch) | |
tree | df2b4f48d5157f775c719192484188a2b0b8a04a | |
parent | 90d570822f85f70c31f80789ad6791cebd904468 (diff) |
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.
-rw-r--r-- | SConstruct | 1 | ||||
m--------- | common | 0 | ||||
-rw-r--r-- | game.cpp | 22 | ||||
-rw-r--r-- | game.h | 1 | ||||
-rw-r--r-- | model.cpp | 104 | ||||
-rw-r--r-- | model.h | 50 | ||||
-rw-r--r-- | models/README | 3 | ||||
-rw-r--r-- | models/trees.blend | bin | 0 -> 3827560 bytes | |||
-rw-r--r-- | scene.cpp | 89 | ||||
-rw-r--r-- | scene.h | 13 | ||||
-rw-r--r-- | shaders/fog_fragment.glsl | 9 | ||||
-rw-r--r-- | shaders/terrain_fragment.glsl | 7 | ||||
-rw-r--r-- | shaders/tree_fragment.glsl | 22 | ||||
-rw-r--r-- | shaders/tree_vertex.glsl | 14 | ||||
-rw-r--r-- | terrain.h | 5 |
15 files changed, 337 insertions, 3 deletions
@@ -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 -Subproject ff7f9de199213ea6d4832c0b91f2a96f5edc6bb +Subproject 6e746716d6a5c72fbd42539c6d5d92da8830cb9 @@ -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(); @@ -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 <iostream> +#include <stdexcept> + +namespace models { + +Mesh::Mesh(const aiScene *scene, const aiMesh *mesh, std::map<std::string, GLuint>& 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<GLuint>::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<std::string, GLuint>& 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 @@ -0,0 +1,50 @@ +#ifndef MODEL_H +#define MODEL_H + +#include "gl.h" + +#include <assimp/assimp.hpp> +#include <assimp/aiScene.h> + +#include <map> +#include <string> +#include <vector> + +namespace models { + +class Mesh { + private: + GLuint vbo; + GLuint vertices; + + public: + Mesh(const aiScene *scene, const aiMesh *mesh, std::map<std::string, GLuint>& scene_textures); + virtual ~Mesh(); + + void render(); + + std::vector<GLuint> 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<std::string, GLuint>& 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 Binary files differnew file mode 100644 index 0000000..108feb6 --- /dev/null +++ b/models/trees.blend @@ -5,6 +5,7 @@ #include <SDL_image.h> #include <boost/format.hpp> #include <boost/bind.hpp> +#include <assimp/aiPostProcess.h> #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<std::string, GLuint>(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<Terrain::Chunk*>::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(); } @@ -9,8 +9,13 @@ #include "tool.h" #include "scripting.h" #include "player.h" +#include "model.h" #include <FTGL/ftgl.h> +#include <assimp/assimp.hpp> +#include <assimp/aiScene.h> + +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<std::string, GLuint> 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: */ @@ -3,6 +3,7 @@ #include "vector.h" #include "terrain_cache.h" +#include "model.h" #include <list> #include <queue> @@ -29,6 +30,9 @@ class Terrain { }; struct Chunk { + typedef std::pair<models::Model*, Vector3> ObjectPair; + typedef std::list<ObjectPair> 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(); |