summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Bergli Heier <snakebite@jvnv.net>2011-07-01 17:20:53 +0200
committerJon Bergli Heier <snakebite@jvnv.net>2011-07-02 18:10:14 +0200
commit75a90df8bf7f38e746e021c23248e1607931132c (patch)
treedf2b4f48d5157f775c719192484188a2b0b8a04a
parent90d570822f85f70c31f80789ad6791cebd904468 (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--SConstruct1
m---------common0
-rw-r--r--game.cpp22
-rw-r--r--game.h1
-rw-r--r--model.cpp104
-rw-r--r--model.h50
-rw-r--r--models/README3
-rw-r--r--models/trees.blendbin0 -> 3827560 bytes
-rw-r--r--scene.cpp89
-rw-r--r--scene.h13
-rw-r--r--shaders/fog_fragment.glsl9
-rw-r--r--shaders/terrain_fragment.glsl7
-rw-r--r--shaders/tree_fragment.glsl22
-rw-r--r--shaders/tree_vertex.glsl14
-rw-r--r--terrain.h5
15 files changed, 337 insertions, 3 deletions
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
-Subproject ff7f9de199213ea6d4832c0b91f2a96f5edc6bb
+Subproject 6e746716d6a5c72fbd42539c6d5d92da8830cb9
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 <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
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 <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
new file mode 100644
index 0000000..108feb6
--- /dev/null
+++ b/models/trees.blend
Binary files differ
diff --git a/scene.cpp b/scene.cpp
index 1fda39e..4e884a6 100644
--- a/scene.cpp
+++ b/scene.cpp
@@ -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();
}
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 <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: */
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 <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();