I'm trying to modify the sample code for loading 3d model included in ASSIMP's sample code, by using GLUT instead of WGL. However, i got a problem with the texture, as illustrated below:
while it is supposed to be as illustrated below:
and the code for drawing the 3d model listed below:
void recursive_render (const struct aiScene *sc, const struct aiNode* nd, float scale){
unsigned int i;
unsigned int n=0, t;
struct aiMatrix4x4 m = nd->mTransformation;
m.Scaling(aiVector3D(scale, scale, scale), m);
// update transform
m.Transpose();
glPushMatrix();
glMultMatrixf((float*)&m);
// draw all meshes assigned to this node
for (; n < nd->mNumMeshes; ++n){
const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
apply_material(sc->mMaterials[mesh->mMaterialIndex]);
if(mesh->mNormals == NULL){
glDisable(GL_LIGHTING);
}
else {
glEnable(GL_LIGHTING);
}
if(mesh->mColors[0] != NULL) {
glEnable(GL_COLOR_MATERIAL);
}
else {
glDisable(GL_COLOR_MATERIAL);
}
for (t = 0; t < mesh->mNumFaces; ++t) {
const struct aiFace* face = &mesh->mFaces[t];
GLenum face_mode;
switch(face->mNumIndices) {
case 1: face_mode = GL_POINTS; break;
case 2: face_mode = GL_LINES; break;
case 3: face_mode = GL_TRIANGLES; break;
default: face_mode = GL_POLYGON; break;
}
glBegin(face_mode);
for(i = 0; i < face->mNumIndices; i++){
int vertexIndex = face->mIndices[i]; // get group index for current index
if(mesh->mColors[0] != NULL)
Color4f(&mesh->mColors[0][vertexIndex]);
if(mesh->mNormals != NULL)
if(mesh->HasTextureCoords(0)){
glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x, 1- mesh->mTextureCoords[0][vertexIndex].y);
}
glNormal3fv(&mesh->mNormals[vertexIndex].x);
glVertex3fv(&mesh->mVertices[vertexIndex].x);
}
glEnd();
}
}
// draw all children
for (n = 0; n < nd->mNumChildren; ++n) {
recursive_render(sc, nd->mChildren[n], scale);
}
glPopMatrix();
}
apply_material function, almost exactly the same as ASSIMP provided sample
void apply_material(const struct aiMaterial *mtl)
{
float c[4];
GLenum fill_mode;
int ret1, ret2;
struct aiColor4D diffuse;
struct aiColor4D specular;
struct aiColor4D ambient;
struct aiColor4D emission;
float shininess, strength;
int two_sided;
int wireframe;
unsigned int max; // changed: to unsigned
int texIndex = 0;
aiString texPath; //contains filename of texture
if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath)) {
unsigned int texId = textureIdMap[texPath.data];
glBindTexture(GL_TEXTURE_2D, texId);
}
set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
color4_to_float4(&diffuse, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, c);
set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
color4_to_float4(&ambient, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, c);
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
color4_to_float4(&specular, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
color4_to_float4(&emission, c);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, c);
max = 1;
ret1 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
max = 1;
ret2 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);
else {
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);
set_float4(c, 0.0f, 0.0f, 0.0f, 0.0f);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
}
max = 1;
if(AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
fill_mode = wireframe ? GL_LINE : GL_FILL;
else
fill_mode = GL_FILL;
glPolygonMode(GL_FRONT_AND_BACK, fill_mode);
max = 1;
if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
}
and also loadGLtextures function, i don't think it is related with culling tough.
int LoadGLTextures(const aiScene* scene) {
ILboolean success;
/* initialization of DevIL */
ilInit();
/* scan scene's materials for textures */
for (unsigned int m=0; m<scene->mNumMaterials; ++m) {
int texIndex = 0;
aiString path; // filename
aiReturn texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
while (texFound == AI_SUCCESS) {
//fill map with textures, OpenGL image ids set to 0
textureIdMap[path.data] = 0;
// more textures?
texIndex++;
texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
}
}
int numTextures = textureIdMap.size();
/* create and fill array with DevIL texture ids */
ILuint* imageIds = new ILuint[numTextures];
ilGenImages(numTextures, imageIds);
/* create and fill array with GL texture ids */
GLuint* textureIds = new GLuint[numTextures];
glGenTextures(numTextures, textureIds); /* Texture name generation */
/* get iterator */
std::map<std::string, GLuint>::iterator itr = textureIdMap.begin();
printf("TextureIDMap Begin %i\n", textureIdMap.begin());
int i=0;
for (; itr != textureIdMap.end(); ++i, ++itr) {
//save IL image ID
std::string filename = (*itr).first; // get filename
(*itr).second = textureIds[i]; // save texture id for filename in map
printf("Texture loaded: %s\n",filename.c_str());
printf("Texture ID Map End: %i\n",textureIdMap.end());
ilBindImage(imageIds[i]); /* Binding of DevIL image name */
ilEnable(IL_ORIGIN_SET);
ilOriginFunc(IL_ORIGIN_LOWER_LEFT);
success = ilLoadImage((ILstring)filename.c_str());
if (success) {
/* Convert image to RGBA */
ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
/* Create and load textures to OpenGL */
glBindTexture(GL_TEXTURE_2D, textureIds[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ilGetInteger(IL_IMAGE_WIDTH),
ilGetInteger(IL_IMAGE_HEIGHT), 0, GL_RGBA, GL_UNSIGNED_BYTE,
ilGetData());
}
else
printf("Couldn't load Image: %s\n", filename.c_str());
}
/* Because we have already copied image data into texture data we can release memory used by image. */
ilDeleteImages(numTextures, imageIds);
//Cleanup
delete [] imageIds;
delete [] textureIds;
//return success;
return true;
}
Lighthouse 3D has given an example for doing this, however, at the moment i can not implement GLSL and VAO to my program. Any solution? Thanks in advance.
I've found the workaround. I changed how to access textures in recursive_render function using the following code :
glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x, mesh->mTextureCoords[0][vertexIndex].y);
instead of:
glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x, 1-mesh->mTextureCoords[0][vertexIndex].y);
This is not a problem with textures. Your problem is coming from backface culling (at least it seems like it since you can kind of see inside the duck). Either your polygons are wound in the wrong order, or your backface culling is set up incorrectly. If you post the code where you set the backface culling, we can see exactly what is wrong.
There could also be the possibility that some of your normals are facing inwards (which can also result from polygon winding). That would explain why your duck's beak is pitch black.
I'm pretty sure the problem is the texture is being 'flipped' along Y axis. That's why your '1-y' works. It can be fixed by flipping the texture along Y while loading. Though I'm not yet sure why 'cause only stumbled upon this problem today.
I'm not sure if this helps but you can flip UV coordinates when importing a model
const aiScene* scene = importer.ReadFile(path, aiProcess_FlipUVs);
Related
I am using the OpenGL v.3.3. and the full source code example from learnopengl.com. I am able to run the textured model from the tutorial (nanosuit.obj), but when i am trying to load another .obj models without textures, but with .mtl files it shows a dark color. I think the problem is maybe in shaders that i am using to load the models, but i dont know how to redo the shader's code to fix my issue.
here is my main functions i am using to render the model:
void COpenGLControl::oglInitialize(void)
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // bit depth
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, // z-buffer depth
0, 0, 0, 0, 0, 0, 0,
};
// Get device context only once.
hdc = GetDC()->m_hDC;
// Pixel format.
m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, m_nPixelFormat, &pfd);
// Create the OpenGL Rendering Context.
hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hrc);
if (!gladLoadGL())
{
AfxMessageBox(L"Error loading glad");
return;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
// build and compile shaders
ourShader = Shader("resource/shaders/modelLoading.vs", "resource/shaders/modelLoading.frag");
// load models
ourModel = Model("resource/models/car12/LaFerrari.obj");
// Send draw request
OnDraw(NULL);
}
void COpenGLControl::oglDrawScene(void)
{
// render
// ------
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// don't forget to enable shader before setting uniforms
ourShader.use();
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)900 / (float)900, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
ourShader.setMat4("projection", projection);
ourShader.setMat4("view", view);
// render the loaded model
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.010f, 0.010f, 0.010f));
model = glm::rotate(model, glm::radians(-30.0f), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(model, glm::radians(20.0f), glm::vec3(1.0f, 0.0f, 0.0f));
ourShader.setMat4("model", model);
ourModel.Draw(ourShader);
}
model.cpp
#include "pch.h"
#include "model.h"
Model::Model(void)
{
}
Model::Model(string const& path, bool gamma)
{
loadModel(path);
}
void Model::Draw(Shader shader)
{
for (unsigned int i = 0; i < meshes.size(); i++)
meshes[i].Draw(shader);
}
void Model::loadModel(string const& path)
{
// read file via ASSIMP
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
// check for errors
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
{
cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
return;
}
// retrieve the directory path of the filepath
directory = path.substr(0, path.find_last_of('/'));
// process ASSIMP's root node recursively
processNode(scene->mRootNode, scene);
}
void Model::processNode(aiNode* node, const aiScene* scene)
{
// process each mesh located at the current node
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
// the node object only contains indices to index the actual objects in the scene.
// the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene));
}
// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene);
}
}
Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene)
{
// data to fill
vector<Vertex> vertices;
vector<unsigned int> indices;
vector<Texture> textures;
// Walk through each of the mesh's vertices
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
// positions
if (!mesh->mVertices == NULL)
{
vector.x = mesh->mVertices[i].x;
vector.y = mesh->mVertices[i].y;
vector.z = mesh->mVertices[i].z;
vertex.Position = vector;
}
else
{
vertex.Position = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"Positions is NULL");
}
// normals
if (!mesh->mNormals == NULL)
{
vector.x = mesh->mNormals[i].x;
vector.y = mesh->mNormals[i].y;
vector.z = mesh->mNormals[i].z;
vertex.Normal = vector;
}
else
{
vertex.Normal = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"Normals is NULL");
}
// texture coordinates
if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
{
glm::vec2 vec;
// a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't
// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
vertex.TexCoords = vec;
}
else
{
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
//AfxMessageBox(L"TextCoords is NULL");
}
// tangent
if (!mesh->mTangents == NULL)
{
vector.x = mesh->mTangents[i].x;
vector.y = mesh->mTangents[i].y;
vector.z = mesh->mTangents[i].z;
vertex.Tangent = vector;
}
else
{
vertex.Tangent = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"TextCoords is NULL");
}
// bitangent
if (!mesh->mBitangents == NULL)
{
vector.x = mesh->mBitangents[i].x;
vector.y = mesh->mBitangents[i].y;
vector.z = mesh->mBitangents[i].z;
vertex.Bitangent = vector;
}
else
{
vertex.Bitangent = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"Bitangent is NULL");
}
vertices.push_back(vertex);
}
// now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
// retrieve all indices of the face and store them in the indices vector
for (unsigned int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
// process materials
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
aiColor3D color;
Material mat;
// Read mtl file vertex data
material->Get(AI_MATKEY_COLOR_AMBIENT, color);
mat.Ka = glm::vec4(color.r, color.g, color.b, 1.0);
material->Get(AI_MATKEY_COLOR_DIFFUSE, color);
mat.Kd = glm::vec4(color.r, color.g, color.b, 1.0);
material->Get(AI_MATKEY_COLOR_SPECULAR, color);
mat.Ks = glm::vec4(color.r, color.g, color.b, 1.0);
// we assume a convention for sampler names in the shaders. Each diffuse texture should be named
// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER.
// Same applies to other texture as the following list summarizes:
// diffuse: texture_diffuseN
// specular: texture_specularN
// normal: texture_normalN
// 1. diffuse maps
vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// 2. specular maps
vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// 3. normal maps
std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
// 4. height maps
std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());
// return a mesh object created from the extracted mesh data
return Mesh(vertices, indices, textures, mat);
}
vector<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
{
vector<Texture> textures;
for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
// check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
bool skip = false;
for (unsigned int j = 0; j < textures_loaded.size(); j++)
{
if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0)
{
textures.push_back(textures_loaded[j]);
skip = true; // a texture with the same filepath has already been loaded, continue to next one. (optimization)
break;
}
}
if (!skip)
{ // if texture hasn't been loaded already, load it
Texture texture;
texture.id = TextureFromFile(str.C_Str(), this->directory, false);
texture.type = typeName;
texture.path = str.C_Str();
textures.push_back(texture);
textures_loaded.push_back(texture); // store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
}
}
return textures;
}
unsigned int Model::TextureFromFile(const char* path, const string& directory, bool gamma)
{
//string filename = string(path);
//filename = directory + '/' + filename;
string filename = string(path);
if (directory.find(R"(/)") != std::string::npos)
{
filename = directory + R"(/)" + filename;
}
else if (directory.find(R"(\\)") != std::string::npos)
{
filename = directory + R"(\\)" + filename;
}
stbi_set_flip_vertically_on_load(false);
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
Shaders:
frag
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D texture_diffuse;
void main( )
{
color = vec4( texture( texture_diffuse, TexCoords ));
}
vs
#version 330 core
layout ( location = 0 ) in vec3 position;
layout ( location = 1 ) in vec3 normal;
layout ( location = 2 ) in vec2 texCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main( )
{
gl_Position = projection * view * model * vec4( position, 1.0f );
TexCoords = texCoords;
}
Also attached pictures (dark picture is the result of my program, color picture is AbViewer free obj viewer result). Please help me to find a solution, what am i doing wrong?
You need to bind the current texture before your draw-calls. You have no code to set any color information for your vertices for your vertexbuffer.
So without having any lights, no color, and no bonded texture your model will be black :-).
I'm trying to implement a selection-outline feature. This is what I get up to now.
As you can see, the objects are selected correctly when the mouse hovers and a contour is drawn around the selected object.
What I would like to do now is to outline the visible edges of the object in this way
In the image on the left is what I have now, and in the right image is what I want to achieve.
This is the procedure I use now.
void paintGL()
{
/* ... */
int w = geometry().width();
int h = geometry().height();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);
setClearColor(Qt::GlobalColor::darkGray);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilMask(0x00);
DrawGeometry();
if (HoveredSphere != RgbFromColorToString(Qt::GlobalColor::black))
{
glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::OUTLINE));
{
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
DrawOutline(HoveredSphere, 1.0f - 0.025f);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
glBindFramebuffer(GL_READ_FRAMEBUFFER, addFBO(FBOIndex::OUTLINE));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebufferObject());
{
// copy stencil buffer
GLbitfield mask = GL_STENCIL_BUFFER_BIT;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
glDepthFunc(GL_LEQUAL);
DrawOutline(HoveredSphere, 1.0f);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
}
update();
}
Where DrawGeometry draws all the objects, and DrawOutline draws the selected object scaled by the factor passed as the second parameter.
Thanks for any suggestions.
By following the tips of #MichaelMahn, I found a solution.
First of all, I draw the silhouette of the visible parts of the selected object in a texture.
And then I use this texture to calculate the outline by checking the neighboring pixels to figure out whether or not I stand on the edge of the silhouette.
outline fragment shader
#version 450
uniform sampler2D silhouette;
in FragData
{
smooth vec2 coords;
} frag;
out vec4 PixelColor;
void main()
{
// if the pixel is black (we are on the silhouette)
if (texture(silhouette, frag.coords).xyz == vec3(0.0f))
{
vec2 size = 1.0f / textureSize(silhouette, 0);
for (int i = -1; i <= +1; i++)
{
for (int j = -1; j <= +1; j++)
{
if (i == 0 && j == 0)
{
continue;
}
vec2 offset = vec2(i, j) * size;
// and if one of the neighboring pixels is white (we are on the border)
if (texture(silhouette, frag.coords + offset).xyz == vec3(1.0f))
{
PixelColor = vec4(vec3(1.0f), 1.0f);
return;
}
}
}
}
discard;
}
paintgl
void paintGL()
{
int w = geometry().width();
int h = geometry().height();
setClearColor(Qt::GlobalColor::darkGray);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawGeometry();
// if we hover a sphere
if (HoveredSphere != RgbFromColorToString(Qt::GlobalColor::black))
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, addFBO(FBOIndex::SILHOUETTE));
{
// copy depth buffer
GLbitfield mask = GL_DEPTH_BUFFER_BIT;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);
// set clear color
setClearColor(Qt::GlobalColor::white);
// enable depth test
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// clear color buffer
glClear(GL_COLOR_BUFFER_BIT);
// draw silhouette
DrawSilhouette(HoveredSphere);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
// clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
// draw outline
DrawOutline();
}
}
PROBLEM :: Now I'd like to parameterize the width of the contour, whose thickness is currently fixed at 1 pixel.
Thank you so much for any suggestion!
Thanks to the advice of #Andrea I found the following solution.
outline fragment shader
#version 450
uniform sampler2D silhouette;
in FragData
{
smooth vec2 coords;
} frag;
out vec4 PixelColor;
void main()
{
// outline thickness
int w = 3;
// if the pixel is black (we are on the silhouette)
if (texture(silhouette, frag.coords).xyz == vec3(0.0f))
{
vec2 size = 1.0f / textureSize(silhouette, 0);
for (int i = -w; i <= +w; i++)
{
for (int j = -w; j <= +w; j++)
{
if (i == 0 && j == 0)
{
continue;
}
vec2 offset = vec2(i, j) * size;
// and if one of the pixel-neighbor is white (we are on the border)
if (texture(silhouette, frag.coords + offset).xyz == vec3(1.0f))
{
PixelColor = vec4(vec3(1.0f), 1.0f);
return;
}
}
}
}
discard;
}
Now I still have a small problem when the selected object is at the edge of the window.
As you can see, the outline is cut sharply.
I tried to "play" with glTexParameter on the parameters GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T.
In the image above you can see the effect I get with
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
while in the image below you can see the effect I get with
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &(QVector4D (1.0f, 1.0f, 1.0f, 1.0f)[0]));
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
I would like the outline to show up at the edge of the window, but only where necessary.
Thanks a lot!
I have a slight issue with my OpenGL lighting.
It's rendering the model (Utah Teapot) just fine, but I have some weird lighting patterns. The model should be flat shaded with a single ambient light illuminating the scene, but I end up with splotchy light all over the teapot:
This is just a simple exercise to build a model loader in C++ with SFML.
Code is as follows:
bool modelLoader(string fileName, vector<GLfloat>& vertices, vector<GLushort>& indices, vector<GLfloat>& normals)
{
fstream object;
string face;
string dataLine;
string lineChar;
GLfloat point;
GLfloat uvs;
GLfloat storedNorm;
GLushort ind;
vector<GLfloat> storedNormals;
vector<GLfloat> storedUVs;
vector<GLfloat> localNormals , localVertices;
vertices.clear();
indices.clear();
object.open(fileName);
if(!object.is_open())
{
printf("Cannot open file: %f \n", fileName);
return false;
}
while(object>>lineChar)
{
if(lineChar == "v" /*|| dataLine.find("vt ") == 0*/ || lineChar == "f" || lineChar == "vn")
{
if(lineChar == "v")
{
//cout<<"v ";
for(int i=0;i<3;++i)
{
object >> point;
//cout << point << " ";
localVertices.push_back(point);
}
//cout<<endl;
}
else if(lineChar == "vn")
{
//cout<<"vn";
for(int j=0;j<3;++j)
{
object >> point;
//cout<<point<<" ";
localNormals.push_back(point);
}
//cout<<endl;
}
else if(lineChar == "f")
{
for(int k=0;k<3;++k)
{
getline(object, face, '/');
ind = atoi(face.c_str());
indices.push_back(ind-1);
object.ignore(2);
//getline(object, face, '/');
//uvs = atoi(face.c_str());
//storedUVs.push_back(uvs);
getline(object, face, ' ');
storedNorm = atoi(face.c_str());
storedNormals.push_back(storedNorm);
}
}
}
}
for (unsigned int i=0; i<indices.size(); ++i )
{
vertices.push_back(localVertices[indices[i]*3]);
vertices.push_back(localVertices[indices[i]*3 + 1]);
vertices.push_back(localVertices[indices[i]*3 + 2]);
normals.push_back(localNormals[(unsigned int) storedNormals[i]*3]);
normals.push_back(localNormals[(unsigned int) storedNormals[i]*3 + 1]);
normals.push_back(localNormals[(unsigned int) storedNormals[i]*3 + 2]);
}
return true;
}
Main loop:
int main()
{
// Create the main window
sf::Window App(sf::VideoMode(SC_WIDTH, SC_HEIGHT, 32), "SFML OpenGL");
// Create a clock for measuring time elapsed
sf::Clock Clock;
//output version of OpenGL to the console
cout<<"OpenGL version: "<<glGetString(GL_VERSION)<<endl;
// Create the vectors to hold the object data
vector<GLfloat> vertices;
vector<GLushort> indices;
vector<GLfloat> normals;
//Load model
if(!modelLoader("teapot2.obj", vertices, indices, normals))
{
printf("Failed to load model. Make sure the .obj file has no errors.");
system("pause");
return 0;
}
//enable the use of vertex arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
// tell OpenGL where the vertices are with glVertexPointer()
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
//*************************************************************
// Set color and depth clear value
glClearDepth(1.f);
glClearColor(0.f, 0.f, 0.f, 0.f);
// Enable Z-buffer read and write
glDepthMask(GL_TRUE);
// Set up lighting for the scene
GLfloat ambient[4] = {0.f,0.5f,0.5f, 1.f};
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
// Setup a perspective projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.f, 1.f, 1.f, 500.f);
// Start game loop
while (App.IsOpened())
{
// Process events
sf::Event Event;
while (App.GetEvent(Event))
{
// Close window : exit
if (Event.Type == sf::Event::Closed)
App.Close();
// Escape key : exit
if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
App.Close();
// Resize event : adjust viewport
if (Event.Type == sf::Event::Resized)
glViewport(0, 0, Event.Size.Width, Event.Size.Height);
}
// Set the active window before using OpenGL commands
// It's useless here because active window is always the same,
// but don't forget it if you use multiple windows or controls
App.SetActive();
if((float)Clock.GetElapsedTime()>REFRESH_RATE){
// Clear colour and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Apply some transformations
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.f, 0.f, -5.f);
glRotatef(45.f, 1.f, 1.f, 1.f);
glPushMatrix();
//draw the triangles with glDrawArrays() and then with glDrawElements()
glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3);
//glDrawElements(GL_TRIANGLES, vertices.size(), GL_UNSIGNED_SHORT, &indices[0]);
//glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();
Clock.Reset();
}
// Finally, display rendered frame on screen
App.Display();
}
//delete the vertex arrays using glDisableClientState()
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
return EXIT_SUCCESS;
}
Anyone got any thoughts?
OpenGL lights have default values for all properties (e.g., GL_AMBIENT, GL_DIFFUSE, etc.). In the case of GL_LIGHT0, the diffuse property is not set to zero (which is what you'd need to get only ambient lighting). To remedy that, you'd need to do
GLfloat black[] = { 0.0f, 0.0f, 0.0f, 1.0f };
glLightfv( GL_LIGHT0, GL_DIFFUSE, black );
Since, IIRC, the rest of the light properties are zero already, that should give you ambient-only lighting, which removes the directional component of your lighting. Also, the default mode for glColorMaterial is GL_AMBIENT_AND_DIFFUSE, which sets both the ambient and diffuse material properties to the incoming vertex color. You might consider switching that to ambient only (glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT );) as well.
However, the root cause of the oddity of your shading, I think is likely due to non-unit-length normals. You might try adding glEnable( GL_NORMALIZE ); before doing lighting computations.
//
// This code was created by Lionel Brits / Jeff Molofee '99
//
// If you've found this code useful, please let me know.
//
// Visit NeHe Productions at www.demonews.com/hosted/nehe
//
/**************************************************************/
// This code was ported to MacOS by Tony Parker.
// I'd also appreciate it if you could drop me a line if you found
// this code useful.
//
// Tony Parker - asp#usc.edu
//
// Have a nice day.
#include <stdio.h> // Header File For Standard Input / Output
#include <stdarg.h> // Header File For Variable Argument Routines
#include <string.h> // Header File For String Management
#include <stdlib.h>
#include <stdbool.h>
#include <OpenGL/gl.h> // Header File For The OpenGL32 Library
#include <OpenGL/glu.h> // Header File For The GLu32 Library
#include <GLUT/glut.h> // Header File For The GLUT Library
#include "math.h"
#include "model.h"
// Constants ----------------------------------------------------------------------
#define kWindowHeight 400
#define kWindowWidth 400
// Structures ----------------------------------------------------------------
typedef struct // Create A Structure
{
GLubyte *imageData; // Image Data (Up To 32 Bits)
GLuint bpp; // Image Color Depth In Bits Per Pixel.
GLuint width; // Image Width
GLuint height; // Image Height
GLuint texID; // Texture ID Used To Select A Texture
} TextureImage; // Structure Name
// Function Prototypes -------------------------------------------------------
bool LoadTGA(TextureImage *texture, char *filename);
float rad(float angle);
void readstr(FILE *f,char *string);
void SetupWorld(void);
GLvoid InitGL(GLvoid);
GLvoid DrawGLScene(GLvoid);
GLvoid ReSizeGLScene(int Width, int Height);
GLvoid Idle(GLvoid);
GLvoid LoadGLTextures(void);
GLvoid Keyboard(unsigned char key, int x, int y);
// Global Variables ----------------------------------------------------------
char *worldfile = "world.txt";
bool light; // Lighting ON/OFF
bool gBlend; // Blending ON/OFF
GLfloat xrot; // X Rotation
GLfloat yrot; // Y Rotation
GLfloat xspeed; // X Rotation Speed
GLfloat yspeed; // Y Rotation Speed
GLfloat walkbias = 0;
GLfloat walkbiasangle = 0;
GLfloat lookupdown = 0.0f;
const float piover180 = 0.0174532925f;
float heading, xpos, zpos;
GLfloat camx=0, camy=0, camz=0; // Camera Location
GLfloat therotate;
GLfloat z=0.0f; // Depth Into The Screen
GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f }; // Ambient Light
GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // Diffuse Light
GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f, 1.0f }; // Light Position
GLuint filter; // Which Filter To Use
TextureImage texture[3]; // Storage for 3 textures
// Our Model Goes Here:
SECTOR sector1;
// rad -----------------------------------------------------------------------
// Converts Degrees To Radians. There Are 2 PI Radians In 360 Degrees.
float rad(float angle)
{
return angle * piover180;
}
// readstr -------------------------------------------------------------------
void readstr(FILE *f,char *string)
{
do
{
fgets(string, 255, f);
} while ((string[0] == '/') || (string[0] == '\n'));
return;
}
// SetupWorld ----------------------------------------------------------------
void SetupWorld(void)
{
float x, y, z, u, v;
int numtriangles;
FILE *filein;
char oneline[255];
filein = fopen(worldfile, "rt");
readstr(filein,oneline);
sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles);
sector1.triangle = new TRIANGLE[numtriangles];
sector1.numtriangles = numtriangles;
int loop;
for ( loop = 0; loop < numtriangles; loop++)
{
int vert;
for ( vert = 0; vert < 3; vert++)
{
readstr(filein,oneline);
sscanf(oneline, "%f %f %f %f %f", &x, &y, &z, &u, &v);
sector1.triangle[loop].vertex[vert].x = x;
sector1.triangle[loop].vertex[vert].y = y;
sector1.triangle[loop].vertex[vert].z = z;
sector1.triangle[loop].vertex[vert].u = u;
sector1.triangle[loop].vertex[vert].v = v;
}
}
fclose(filein);
return;
}
#pragma mark -
// Main ----------------------------------------------------------------------
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(kWindowWidth, kWindowHeight);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
SetupWorld();
InitGL();
glutDisplayFunc(DrawGLScene);
glutReshapeFunc(ReSizeGLScene);
glutKeyboardFunc(Keyboard);
glutMainLoop();
return 0;
}
// InitGL ---------------------------------------------------------------------
GLvoid InitGL(GLvoid)
{
LoadGLTextures(); // Load The Texture ( ADD )
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping ( ADD )
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black
glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
gluPerspective(45.0f, (GLfloat) kWindowWidth / (GLfloat) kWindowHeight, 0.1f, 100.0f);
// Calculate The Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
glEnable(GL_LIGHT1);
}
// Idle ---------------------------------------------------------------------
GLvoid Idle(GLvoid)
{
glutPostRedisplay();
}
// Keyboard -----------------------------------------------------------------
void Keyboard(unsigned char key, int x, int y)
{
#pragma unused (x, y)
switch(key)
{
case 'b': // turn blending on/off
gBlend = !gBlend;
if (!gBlend)
{
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
else
{
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
}
break;
case 'f':
filter+=1;
if (filter > 2)
{
filter = 0;
}
break;
case 'l':
light = !light;
if (!light)
glDisable(GL_LIGHTING);
else
glEnable(GL_LIGHTING);
break;
case 'w': // walk forward
xpos -= (float)sin(heading*piover180) * 0.05f;
zpos -= (float)cos(heading*piover180) * 0.05f;
if (walkbiasangle >= 359.0f)
walkbiasangle = 0.0f;
else
walkbiasangle+= 10;
walkbias = (float)sin(walkbiasangle * piover180)/20.0f;
//lookupdown -= 1.0f;
break;
case 'x': // walk back
xpos += (float)sin(heading*piover180) * 0.05f;
zpos += (float)cos(heading*piover180) * 0.05f;
if (walkbiasangle <= 1.0f)
walkbiasangle = 359.0f;
else
walkbiasangle-= 10;
walkbias = (float)sin(walkbiasangle * piover180)/20.0f;
//lookupdown += 1.0f;
break;
case 'd': // turn right
heading -= 1.0f;
yrot = heading;
break;
case 'a': // turn left
heading += 1.0f;
yrot = heading;
break;
case 'q':
z += 0.02f;
break;
case 'z':
z += 0.02f;
break;
default:
break;
}
glutPostRedisplay();
}
// DrawGLScene -------------------------------------------------------------
GLvoid DrawGLScene(GLvoid)
{
GLfloat x_m, y_m, z_m, u_m, v_m;
GLfloat xtrans, ztrans, ytrans;
GLfloat sceneroty;
xtrans = -xpos;
ztrans = -zpos;
ytrans = -walkbias-0.25f;
sceneroty = 360.0f- yrot;
int numtriangles;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glRotatef(lookupdown,1.0f,0,0);
glRotatef(sceneroty,0,1.0f,0);
glTranslatef(xtrans, ytrans, ztrans);
glBindTexture(GL_TEXTURE_2D, texture[filter].texID);
numtriangles = sector1.numtriangles;
// Process Each Triangle
int loop_m;
for ( loop_m = 0; loop_m < numtriangles; loop_m++)
{
glBegin(GL_TRIANGLES);
glNormal3f( 0.0f, 0.0f, 1.0f);
x_m = sector1.triangle[loop_m].vertex[0].x;
y_m = sector1.triangle[loop_m].vertex[0].y;
z_m = sector1.triangle[loop_m].vertex[0].z;
u_m = sector1.triangle[loop_m].vertex[0].u;
v_m = sector1.triangle[loop_m].vertex[0].v;
glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
x_m = sector1.triangle[loop_m].vertex[1].x;
y_m = sector1.triangle[loop_m].vertex[1].y;
z_m = sector1.triangle[loop_m].vertex[1].z;
u_m = sector1.triangle[loop_m].vertex[1].u;
v_m = sector1.triangle[loop_m].vertex[1].v;
glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
x_m = sector1.triangle[loop_m].vertex[2].x;
y_m = sector1.triangle[loop_m].vertex[2].y;
z_m = sector1.triangle[loop_m].vertex[2].z;
u_m = sector1.triangle[loop_m].vertex[2].u;
v_m = sector1.triangle[loop_m].vertex[2].v;
glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
glEnd();
}
glutSwapBuffers();
glFlush();
}
// ReSizeGLScene ------------------------------------------------------------
GLvoid ReSizeGLScene(int Width, int Height)
{
glViewport (0, 0, (GLsizei) Width, (GLsizei) Height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (GLfloat) Width / (GLfloat) Height, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// LoadGLTextures ------------------------------------------------------------
GLvoid LoadGLTextures(GLvoid)
{
//load texture
LoadTGA(&texture[0], "mud.tga");
LoadTGA(&texture[1], "mud.tga");
LoadTGA(&texture[2], "mud.tga");
// Create Nearest Filtered Texture
glBindTexture(GL_TEXTURE_2D, texture[0].texID);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
//glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1->sizeX, texture1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1->data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture[0].width, texture[0].height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture[0].imageData);
// Create Linear Filtered Texture
glBindTexture(GL_TEXTURE_2D, texture[1].texID);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
//glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1->sizeX, texture1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1->data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture[1].width, texture[1].height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture[1].imageData);
// Create MipMapped Texture
glBindTexture(GL_TEXTURE_2D, texture[2].texID);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texture[2].width, texture[2].height, GL_RGB, GL_UNSIGNED_BYTE, texture[2].imageData);
}
/********************> LoadTGA() <*****/
bool LoadTGA(TextureImage *texture, char *filename) // Loads A TGA File Into Memory
{
GLubyte TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0}; // Uncompressed TGA Header
GLubyte TGAcompare[12]; // Used To Compare TGA Header
GLubyte header[6]; // First 6 Useful Bytes From The Header
GLuint bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File
GLuint imageSize; // Used To Store The Image Size When Setting Aside Ram
GLuint temp; // Temporary Variable
GLuint type=GL_RGBA; // Set The Default GL Mode To RBGA (32 BPP)
FILE *file = fopen(filename, "rb"); // Open The TGA File
if( file==NULL || // Does File Even Exist?
fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) || // Are There 12 Bytes To Read?
memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0 || // Does The Header Match What We Want?
fread(header,1,sizeof(header),file)!=sizeof(header)) // If So Read Next 6 Header Bytes
{
fclose(file); // If Anything Failed, Close The File
return false; // Return False
}
texture->width = header[1] * 256 + header[0]; // Determine The TGA Width (highbyte*256+lowbyte)
texture->height = header[3] * 256 + header[2]; // Determine The TGA Height (highbyte*256+lowbyte)
if( texture->width <=0 || // Is The Width Less Than Or Equal To Zero
texture->height <=0 || // Is The Height Less Than Or Equal To Zero
(header[4]!=24 && header[4]!=32)) // Is The TGA 24 or 32 Bit?
{
fclose(file); // If Anything Failed, Close The File
return false; // Return False
}
texture->bpp = header[4]; // Grab The TGA's Bits Per Pixel (24 or 32)
bytesPerPixel = texture->bpp/8; // Divide By 8 To Get The Bytes Per Pixel
imageSize = texture->width*texture->height*bytesPerPixel; // Calculate The Memory Required For The TGA Data
texture->imageData=(GLubyte *)malloc(imageSize); // Reserve Memory To Hold The TGA Data
if( texture->imageData==NULL || // Does The Storage Memory Exist?
fread(texture->imageData, 1, imageSize, file)!=imageSize) // Does The Image Size Match The Memory Reserved?
{
if(texture->imageData!=NULL) // Was Image Data Loaded
free(texture->imageData); // If So, Release The Image Data
fclose(file); // Close The File
return false; // Return False
}
GLuint i;
for( i=0; i<imageSize; i= i + bytesPerPixel) // Loop Through The Image Data
{ // Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
temp=texture->imageData[i]; // Temporarily Store The Value At Image Data 'i'
texture->imageData[i] = texture->imageData[i + 2]; // Set The 1st Byte To The Value Of The 3rd Byte
texture->imageData[i + 2] = temp; // Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
}
fclose (file); // Close The File
if (texture[0].bpp==24) // Was The TGA 24 Bits
{
type=GL_RGB; // If So Set The 'type' To GL_RGB
}
// Build A Texture From The Data
// We're doing this in a different function in this tutorial
glGenTextures(1, &texture[0].texID); // Generate OpenGL texture IDs
/*
glBindTexture(GL_TEXTURE_2D, texture[0].texID); // Bind Our Texture
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Linear Filtered
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filtered
glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);
*/
return true; // Texture Building Went Ok, Return True
}
NEED HELP.
I got this error:
/Users//Desktop/XcodeGLUT/../gora.cs.illinois.edu:display:cs418sp11:Home/Lesson
10 Folder/main.c:126:0
/Users//Desktop/XcodeGLUT/../gora.cs.illinois.edu:display:cs418sp11:Home/Lesson
10 Folder/main.c:126: error: 'new'
undeclared (first use in this
function)
rename the file to main.cpp, seems the file is compiled using the c-compiler and not the C++ compiler where new is a keyword for allocating on the heap (instead of malloc/calloc)
I am currently using the Code::Blocks IDE, c++, opengl and SDL to make a game- and I have hit a severe block in road, if you will. I am using SDL_image to load these jpg images for the textures, but they just will not load. Currently, I have tried placing them in every folder in the project, even places where it would supposedly make no difference. I have tried running both the debug and release versions from the IDE, by double-clicking the executable, and by running the executable from the command line. They all produce the same error: Could not load image: "the path to my image". I have tried using full paths, I have tried using relative paths, I have tried just about everything. I have tried various image formats, to no success. Other useful information may be: I am using ubuntu 11.04, I am using GIMP to create the images, and this is my code in complete (I'm sure most of it is irrelevant, but I am just putting it all down just-in-case):
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <Cg/cg.h>// NEW: Cg Header
#include <Cg/cgGL.h>// NEW: Cg OpenGL Specific Header
#include <iostream>
#include <math.h>
#include <string>
#include "../include/Camera.h"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Keydown booleans
bool key[321];
Uint8 *keystate;
float fogDensity = 0.02f;
static float fog_color[] = { 0.8f, 0.8f, 0.8f, 1.0f };
// Process pending events
bool events()
{
SDL_Event event;
if( !SDL_PollEvent(&event) ){
return true;
}
switch( event.type )
{
case SDL_KEYDOWN : key[ event.key.keysym.sym ]=true; break;
case SDL_KEYUP : key[ event.key.keysym.sym ]=false; break;
case SDL_QUIT : return false; break; //one-per-keypress
}
keystate = SDL_GetKeyState(NULL); //continuous
return true;
}
// Initialze OpenGL perspective matrix
void setup(int width, int height)
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glEnable( GL_DEPTH_TEST );
gluPerspective( 45, (float)width/height, 0.1, 100 );
glMatrixMode( GL_MODELVIEW );
}
Camera* cam = new Camera();
GLuint rocktexture; // This is a handle to our texture object
GLuint earthtexture;
//add some default materials here
const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 0.0f, 1.0f, 0.0f, 0.0f };
const GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };
void MouseUpdate() {
int x, y;
SDL_GetMouseState(&x,&y);
SDL_WarpMouse(WINDOW_WIDTH/2,WINDOW_HEIGHT/2);
cam->yaw+=(x-WINDOW_WIDTH/2)*0.2;
cam->pitch+=(y-WINDOW_HEIGHT/2)*0.2;
if(cam->pitch<-90) {
cam->pitch=-90;
}
if(cam->pitch>90) {
cam->pitch=90;
}
}
static void display(void)
{
while(events())
{
MouseUpdate();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(cam->pitch,1.0,0.0,0.0); //rotate our camera on the x-axis (left and right)
glRotatef(cam->yaw,0.0,1.0,0.0); //rotate our camera on the y-axis (up and down)
glTranslated(cam->camx,cam->camy,cam->camz); //translate the screen to the position of our camera
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0);
//glColor4f(1,1,1,0.5);
glPushMatrix();
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
//glDisable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, rocktexture);
glBegin(GL_TRIANGLE_FAN);
glTexCoord2i(0,0);glVertex3f(-32,0,32);
glTexCoord2i(1,0);glVertex3f(32,0,32);
glTexCoord2i(1,1);glVertex3f(32,0,-32);
glTexCoord2i(0,1);glVertex3f(-32,0,-32);
glEnd();
//glEnable(GL_CULL_FACE);
glPopMatrix();
/*glPushMatrix();
GLUquadric* earth = gluNewQuadric();
gluQuadricTexture(earth, true);
gluQuadricDrawStyle(earth, GLU_FILL);
gluQuadricNormals(earth, GLU_SMOOTH);
gluQuadricOrientation(earth, GLU_OUTSIDE);
gluSphere(earth, 1, 16, 16);
gluDeleteQuadric(earth);
glPopMatrix();*/
glFlush();
SDL_GL_SwapBuffers();
if (key[SDLK_ESCAPE] || key[SDLK_q])
{
std::cout<<"Game Ended."<<std::endl;
exit(0);
}
if (keystate[SDLK_w])
{
cam->camx-=sin(cam->yaw*M_PI/180)/4;
cam->camz+=cos(cam->yaw*M_PI/180)/4;
}
if (keystate[SDLK_s])
{
cam->camx+=sin(cam->yaw*M_PI/180)/4;
cam->camz-=cos(cam->yaw*M_PI/180)/4;
}
if (keystate[SDLK_a])
{
cam->camx+=cos(cam->yaw*M_PI/180)/4;
cam->camz+=sin(cam->yaw*M_PI/180)/4;
}
if (keystate[SDLK_d])
{
cam->camx-=cos(cam->yaw*M_PI/180)/4;
cam->camz-=sin(cam->yaw*M_PI/180)/4;
}
}
}
// Load the OpenGL texture
GLuint loadImage(std::string path, GLuint tx,SDL_Surface* sur)
{
GLenum texture_format;
GLint nOfColors;
if ((sur = SDL_LoadBMP(path.c_str())))
{
// Check that the image's width is a power of 2
if ((sur->w & (sur->w - 1)) != 0)
{
printf("warning: image's width is not a power of 2\n");
}
// Also check if the height is a power of 2
if ((sur->h & (sur->h - 1)) != 0)
{
printf("warning: image's height is not a power of 2\n");
}
// get the number of channels in the SDL surface
nOfColors = sur->format->BytesPerPixel;
if (nOfColors == 4) // contains an alpha channel
{
if (sur->format->Rmask == 0x000000ff)
{
texture_format = GL_RGBA;
}
else
{
texture_format = GL_BGRA;
}
}
else if (nOfColors == 3) // no alpha channel
{
if (sur->format->Rmask == 0x000000ff)
{
texture_format = GL_RGB;
}
else
{
texture_format = GL_BGR;
}
}
else
{
printf("warning: the image is not truecolor.. this will probably break\n");
// this error should not go unhandled
}
// Have OpenGL generate a texture object handle for us
glGenTextures( 1, &tx );
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, tx );
// Set the texture's stretching properties
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// Edit the texture object's image data using the information SDL_Surface gives us
glTexImage2D( GL_TEXTURE_2D, 0, 3, sur->w, sur->h, 0, GL_BGR, GL_UNSIGNED_BYTE, sur->pixels);
//build mip-maps
gluBuild2DMipmaps(tx, 3, sur->w, sur->h, texture_format, GL_UNSIGNED_BYTE, sur->pixels);
}
else
{
printf("SDL could not load image: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
// Free the SDL_Surface only if it was successfully created
if (sur)
{
SDL_FreeSurface(sur);
}
delete &texture_format;
delete &nOfColors;
return tx;
}
/* Program entry point */
int main(int argc, char *argv[])
{
cam->camz=-5;
cam->camy=-1;
if ( SDL_Init(SDL_INIT_VIDEO) != 0 )
{
std::cout<<"Unable to initialize SDL: %s\n"<<SDL_GetError()<<std::endl;
return 1;
}
const SDL_VideoInfo* info = SDL_GetVideoInfo();
int vidFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER;
if (info->hw_available) {vidFlags |= SDL_HWSURFACE;}
else {vidFlags |= SDL_SWSURFACE;}
//int bpp = info->vfmt->BitsPerPixel;
SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 32, vidFlags);
SDL_WM_SetCaption( "Treason", NULL);
SDL_WarpMouse(WINDOW_WIDTH/2, WINDOW_HEIGHT/2);
gluLookAt(cam->camx,cam->camy,cam->camz,0,0,0,0,1,0);
glClearColor(0, 0, 0, 0);//background color white
float lmodel_ambient[] = { 0.4f, 0.4f, 0.4f, 1.0f };
float local_view[] = { 0.0f };
glEnable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);
glLightModelf(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glEnable(GL_DITHER);
//glHint(GL_PHONG_HINT_WIN, GL_NICEST);
glShadeModel(GL_SMOOTH);//GL_PHONG_WIN
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
/* glEnable(GL_FOG);
glHint(GL_FOG_HINT, GL_NICEST);
glEnable(GL_FOG);
glFogi(GL_FOG_MODE, GL_EXP);
glFogf(GL_FOG_DENSITY, fogDensity);
glFogfv(GL_FOG_COLOR, fog_color);*/
// glEnable(GL_STENCIL_TEST);
//glStencilFunc(GL_KEEP, GL_KEEP, GL_INCR);
SDL_Surface *rocktexsur;
rocktexture = loadImage("rock.jpg", rocktexture, rocktexsur);
SDL_Surface *earthtexsur;
earthtexture = loadImage("earth.jpg", earthtexture, earthtexsur);
setup(WINDOW_WIDTH, WINDOW_HEIGHT);
display();
glDeleteTextures(1, &rocktexture);
glDeleteTextures(1, &earthtexture);
return 0;
}
So if you find anything or know any reason why it would return an "SDL could not load image" Error, please respond! I can't continue on my project until this is resolved because it produces a segfault.
SDL by itself can only load BMP images, and it will return an error if you load anything but a BMP in using the function SDL_LoadBMP(). In order to load JPEGs, you need to use the supplemental library SDL_image, which is available at http://www.libsdl.org/projects/SDL_image/ . As JJG answered, the prototype for the function you have to use is:
SDL_Surface *IMG_Load( const char* );
rather than SDL_LoadBMP().
Not an expert on SDL or C++. But I just spent a minute looking over my tiny program I made a while back. I used:
some_image = IMG_Load( "someimage.jpg" );