Related
I used opengl in qt5 on ubuntu 18.4, i wanted to load a simple cube(store in an .obj file) and render it. I followed the tutorial here http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/, but i got a black window at last. Here is my code, thank you very much:
myopengl.hpp
class MyOpenGL : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
MyOpenGL(const QString& obj_file);
protected:
virtual void initializeGL() Q_DECL_OVERRIDE;
virtual void paintGL() Q_DECL_OVERRIDE;
virtual void resizeGL(int width, int height) Q_DECL_OVERRIDE;
void CheckGLerror();
private:
std::vector< glm::vec3 > vertices;
std::vector< glm::vec2 > uvs;
std::vector< glm::vec3 > normals;
/* Render data */
GLuint VAO, VBO, EBO;
};
myopengl.cpp
MyOpenGL ::MyOpenGL (const QString& obj_file) :
VAO(0),
VBO(0),
EBO(0)
{
std::vector< unsigned int > vertex_indices, uv_indices, normal_indices;
std::vector< glm::vec3 > temp_vertices;
std::vector< glm::vec2 > temp_uvs;
std::vector< glm::vec3 > temp_normals;
//parse obj file
QFile file(obj_file);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&file);
while(!in.atEnd()) {
QString line = in.readLine();
QStringList list = line.replace(",","").split(' ', QString::SkipEmptyParts);
if (list.size() >= 3) {
if (list.at(0) == "v") { //veertex
bool ok1, ok2, ok3;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
float v3 = list.at(3).toFloat(&ok3);
if (ok1 && ok2 && ok3) {
glm::vec3 vertex;
vertex.x = v1;
vertex.y = v2;
vertex.z = v3;
temp_vertices.push_back(vertex);
}
} else if (list.at(0) == "vn") {
bool ok1, ok2, ok3;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
float v3 = list.at(3).toFloat(&ok3);
if (ok1 && ok2 && ok3) {
glm::vec3 normal;
normal.x = v1;
normal.y = v2;
normal.z = v3;
temp_normals.push_back(normal);
}
} else if (list.at(0) == "vt") {
bool ok1, ok2;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
if (ok1 && ok2) {
glm::vec2 uv;
uv.x = v1;
uv.y = v2;
temp_uvs.push_back(uv);
}
} else if (list.at(0) == "f") {
bool f_ok1, f_ok2, f_ok3;
bool t_ok1, t_ok2, t_ok3;
bool n_ok1, n_ok2, n_ok3;
unsigned int v_index1, v_index2, v_index3;
unsigned int t_index1, t_index2, t_index3;
unsigned int n_index1, n_index2, n_index3;
QStringList f_list = list.at(1).split('/');
if (f_list.size() >= 3) {
v_index1 = f_list.at(0).toUInt(&f_ok1);
if (f_ok1) {
v_index1 -= 1;
}
t_index1 = f_list.at(1).toUInt(&t_ok1);
if (t_ok1) {
t_index1 -= 1;
}
n_index1 = f_list.at(2).toUInt(&n_ok1);
if (n_ok1) {
n_index1 -= 1;
}
}
f_list = list.at(2).split('/');
if (f_list.size() >= 3) {
v_index2 = f_list.at(0).toUInt(&f_ok2);
if (f_ok2) {
v_index2 -= 1;
}
t_index2 = f_list.at(1).toUInt(&t_ok2);
if (t_ok2) {
t_index2 -= 1;
}
n_index2 = f_list.at(2).toUInt(&n_ok2);
if (n_ok2) {
n_index2 -= 1;
}
}
f_list = list.at(3).split('/');
if (f_list.size() >= 3) {
v_index3 = f_list.at(0).toUInt(&f_ok3);
if (f_ok3) {
v_index3 -= 1;
}
t_index3 = f_list.at(1).toUInt(&t_ok3);
if (t_ok3) {
t_index3 -= 1;
}
n_index3 = f_list.at(2).toUInt(&n_ok3);
if (n_ok3) {
n_index3 -= 1;
}
}
if (f_ok1 && f_ok2 && f_ok3 && n_ok1 && n_ok2 && n_ok3
&& t_ok1 && t_ok2 && t_ok3) {
vertex_indices.push_back(v_index1);
vertex_indices.push_back(v_index2);
vertex_indices.push_back(v_index3);
uv_indices.push_back(t_index1);
uv_indices.push_back(t_index2);
uv_indices.push_back(t_index3);
normal_indices.push_back(n_index1);
normal_indices.push_back(n_index2);
normal_indices.push_back(n_index3);
}
}
}
}
file.close();
for (unsigned int i = 0; i < vertex_indices.size(); ++i) {
glm::vec3 vertex = temp_vertices.at(vertex_indices.at(i));
vertices.push_back(vertex);
}
for (unsigned int i = 0; i < uv_indices.size(); ++i) {
glm::vec2 uv = temp_uvs.at(uv_indices.at(i));
uvs.push_back(uv);
}
for (unsigned int i = 0; i < normal_indices.size(); ++i) {
glm::vec3 normal = temp_normals.at(normal_indices.at(i));
normals.push_back(normal);
}
}
void MyOpenGL::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0,0.0,0.0,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHT0);
glewExperimental = GL_TRUE;
GLenum status = glewInit();
if (status != GLEW_OK)
{
glfwTerminate();
std::system("pause");
return;
}
glGenVertexArrays(1, &this->VAO);
glBindVertexArray(this->VAO);
glGenBuffers(1, &this->VBO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(glm::vec3), &this->vertices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
}
void MyOpenGL::resizeGL(int width, int height)
{
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-width/2,width/2,-height/2,height/2,-1,1);
glMatrixMode(GL_MODELVIEW);
}
void MyOpenGL::paintGL()
{
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glDisableVertexAttribArray(0);
}
You have to define an array of generic vertex attribute data by glVertexAttribPointer.
Note, the vertex buffer object is only a data store for the vertex data. But you have to specify how to "use" them. The specification and states of the vertex attributes are stored in the vertex array object. It is sufficient to bind the vertex array object when you want to draw the mesh:
specification:
glBindVertexArray(this->VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
draw:
glBindVertexArray(this->VAO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glBindVertexArray(0);
Extension:
Is it essential to use vertex shader and fragment shader? I didn't use them for current.
If you don't have a shader, then you have to use the Fixed Function Pipeline and to define the fixed function attributes, by glVertexPointer respectively glEnableClientState( GL_VERTEX_ARRAY ).
But since you only use one attribute, the vertex coordiante with attribute index 0, you can still define the attribute by glVertexAttribPointer and glEnableVertexAttribArray.
See What are the Attribute locations for fixed function pipeline in OpenGL 4.0++ core profile?
Note, in this case you have to use a compatibility OpenGL Context.
I have a very peculiar problem that I can not figure out. For my scene I have created a spline class which I use to generate points for which my camera traverses. This works fine and dandy when I create an instance of the spline class in my scene and use it. I have some functionality in my spline class for showing the points on the spline that use uniform data to draw their position and give them a color. This all works fine when I create an instance directly in my scene.
HOWEVER, I want to create multiple splines to have my camera traverse so I created a fairly simple management class to handle multiple splines. This works however for some reason I can not puzzle out why when I create an instance of a spline with the manager the uniform variables aren't working. Everything else works fine. When I say they don't work what I mean is that the getLocation functions work (they get the locationIDs as expected), the data to be sent is the same, there are no errors or warnings concerning the functions that are supposed to actually send the data down to their locations. The spline draws properly however they are always black and haven't been translated.
Here is my scene class. The constructors for both my manager and the spline class its self are exactly the same so in my ClothScene.hpp I can switch the type of the mSplineManager object from the manager class to the spline class and the uniform variables work fine.
#include "ClothScene.hpp"
#include <atlas/core/GLFW.hpp>
#include <atlas/core/Log.hpp>
#include <atlas/core/Macros.hpp>
#include <atlas/core/Float.hpp>
#include <iostream>
ClothScene::ClothScene() :
mIsPlaying(false),
mLastTime(0.0f),
mFPS(60.0f),
mTick(1.0f / mFPS),
mAnimTime(0.0f),
mAnimLength(10.0f),
mSplineManager(int(mAnimLength * mFPS)),
ballPosition{ -10.0f, 0.0f, 14.0f }
{
glEnable(GL_DEPTH_TEST);
auto mat = glm::translate(atlas::math::Matrix4(1.0f), ballPosition);
mBall.transformGeometry(mat);
}
ClothScene::~ClothScene() {
}
void ClothScene::mousePressEvent(int button, int action, int modifiers, double xPos, double yPos) {
USING_ATLAS_MATH_NS;
if (button == GLFW_MOUSE_BUTTON_LEFT && modifiers == GLFW_MOD_ALT)
{
if (action == GLFW_PRESS)
{
mIsDragging = true;
//Camera tilt up and down or turn left, right
mCamera.mouseDown(Point2(xPos, yPos), ClothCamera::CameraMovements::TUMBLE);
}
else
{
mIsDragging = false;
mCamera.mouseUp();
}
}
else if (button == GLFW_MOUSE_BUTTON_MIDDLE && modifiers == GLFW_MOD_ALT)
{
if (action == GLFW_PRESS)
{
mIsDragging = true;
//Camera move left, right, up, down
mCamera.mouseDown(Point2(xPos, yPos), ClothCamera::CameraMovements::TRACK);
}
else
{
mIsDragging = false;
mCamera.mouseUp();
}
}
else if (button == GLFW_MOUSE_BUTTON_RIGHT && modifiers == GLFW_MOD_ALT)
{
if (action == GLFW_PRESS)
{
// first click.
mIsDragging = true;
//Camera move back and forth
mCamera.mouseDown(Point2(xPos, yPos), ClothCamera::CameraMovements::DOLLY);
}
else
{
mIsDragging = false;
mCamera.mouseUp();
}
}
else if (action != GLFW_PRESS)
{
mIsDragging = false;
mCamera.mouseUp();
}
}
void ClothScene::mouseMoveEvent(double xPos, double yPos) {
mCamera.mouseUpdate(glm::vec2(xPos, yPos));
}
void ClothScene::keyPressEvent(int key, int scancode, int action, int mods) {
UNUSED(scancode);
UNUSED(mods);
if (action == GLFW_PRESS)
{
switch (key)
{
case GLFW_KEY_T:
mCamera.resetCamera();
break;
case GLFW_KEY_W:
mCamera.strafeCamera(0);
break;
case GLFW_KEY_S:
mCamera.strafeCamera(1);
break;
case GLFW_KEY_A:
mCamera.strafeCamera(2);
break;
case GLFW_KEY_D:
mCamera.strafeCamera(3);
break;
case GLFW_KEY_R:
mCamera.strafeCamera(4);
break;
case GLFW_KEY_F:
mCamera.strafeCamera(5);
break;
case GLFW_KEY_Q:
mCamera.strafeCamera(6);
break;
case GLFW_KEY_E:
mCamera.strafeCamera(7);
break;
case GLFW_KEY_C:
mCamera.newPosition(glm::vec3(0.0f, 3.0f, 0.0f));
break;
case GLFW_KEY_U:
mSplineManager.showSpline();
break;
case GLFW_KEY_I:
mSplineManager.showControlPoints();
break;
case GLFW_KEY_O:
mSplineManager.showCage();
break;
case GLFW_KEY_P:
mSplineManager.showSplinePoints();
break;
case GLFW_KEY_SPACE:
mIsPlaying = !mIsPlaying;
default:
break;
}
}
}
void ClothScene::screenResizeEvent(int width, int height) {
glViewport(0, 0, width, height);
mProjection = glm::perspective(glm::radians(45.0),(double)width / height, 1.0, 1000.0);
}
void ClothScene::renderScene() {
float grey = 161.0f / 255.0f;
glClearColor(grey, grey, grey, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
mView = mCamera.getCameraMatrix();
mGrid.renderGeometry(mProjection, mView);
mSplineManager.renderGeometry(mProjection, mView);
mBall.renderGeometry(mProjection, mView);
}
void ClothScene::updateScene(double time)
{
mTime.currentTime = (float)time;
mTime.totalTime += (float)time;
if (atlas::core::geq(mTime.currentTime - mLastTime, mTick))
{
mLastTime += mTick;
mTime.deltaTime = mTick;
if (mIsPlaying)
{
mAnimTime += mTick;
mSplineManager.updateGeometry(mTime);
if (mSplineManager.doneInterpolation())
{
mIsPlaying = false;
return;
}
auto point = mSplineManager.getSplinePosition();
mCamera.newPosition(point);
mCamera.lookAt(ballPosition);
auto mat = glm::translate(atlas::math::Matrix4(1.0f), ballPosition);
mBall.transformGeometry(mat);
}
}
}
Here is my SplineManger class. It super simple, it just creates a vector of splines and then applies the exact same functionality iterated through all of the splines in the vector. For some reason this prevents the shader from receiving the uniform data.
#include "SplineManager.hpp"
SplineManager::SplineManager(int totalFrames) :
finishedAllSplines(false),
currentSpline(0)
{
mTotalFrames = totalFrames;
addSplines();
}
SplineManager::~SplineManager() {
}
void SplineManager::addSplines() {
mControlPoints = std::vector<Point>
{
{ -20, -5, 0 },
{ -19, 5, -15 },
{ 12.7f, -5, -1.4f },
{ 20, 8.2f, 4.4f }
};
mSplines.push_back(Spline(mTotalFrames, mControlPoints));
}
void SplineManager::renderGeometry(atlas::math::Matrix4 projection, atlas::math::Matrix4 view) {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].renderGeometry(projection, view);
}
}
void SplineManager::updateGeometry(atlas::utils::Time const& t) {
mSplines[currentSpline].updateGeometry(t);
if (mSplines[currentSpline].doneInterpolation()) {
++currentSpline;
if (currentSpline == mSplines.size()) {
finishedAllSplines = true;
}
}
}
atlas::math::Point SplineManager::getSplinePosition() {
return mSplines[currentSpline].getSplinePosition();
}
void SplineManager::showSpline() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showSpline();
}
}
void SplineManager::showControlPoints() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showControlPoints();
}
}
void SplineManager::showCage() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showCage();
}
}
void SplineManager::showSplinePoints() {
for (int i = 0; i < mSplines.size(); ++i) {
mSplines[i].showSplinePoints();
}
}
bool SplineManager::doneInterpolation() {
return finishedAllSplines;
}
Here is my spline class. Again, this all works totally fine when I directly create an instance of it in the scene class. When instances are created in the SplineManager the uniform variables don't work.
#include "Spline.h"
#include "ShaderPaths.hpp"
#include <atlas/core/Macros.hpp>
Spline::Spline(int totalFrames) :
mResolution(500),
mTotalFrames(totalFrames),
mCurrentFrame(0),
mShowControlPoints(false),
mShowCage(false),
mShowSplinePoints(false),
mShowSpline(false),
mIsInterpolationDone(false)
{
USING_ATLAS_MATH_NS;
USING_ATLAS_GL_NS;
//Bezier
mBasisMatrix = Matrix4(
1.0f, 0.0f, 0.0f, 0.0f,
-3.0f, 3.0f, 0.0f, 0.0f,
3.0f, -6.0f, 3.0f, 0.0f,
-1.0f, 3.0f, -3.0f, 1.0f);
mControlPoints = std::vector<Point>
{
{ -20, -5, 0 },
{ -19, 5, -15 },
{ 12.7f, -5, -1.4f },
{ 20, 8.2f, 4.4f }
};
std::vector<Point> splinePoints;
float scale = 1.0f / mResolution;
for (int res = 0; res < mResolution + 1; ++res)
{
splinePoints.push_back(evaluateSpline(scale * res));
}
generateArcLengthTable();
glGenVertexArrays(1, &mVao);
glBindVertexArray(mVao);
glGenBuffers(1, &mControlBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mControlBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * mControlPoints.size(),
mControlPoints.data(), GL_STATIC_DRAW);
glGenBuffers(1, &mSplineBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mSplineBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * splinePoints.size(), splinePoints.data(), GL_STATIC_DRAW);
std::string shaderDir = generated::ShaderPaths::getShaderDirectory();
std::vector<ShaderInfo> shaders
{
{ GL_VERTEX_SHADER, shaderDir + "spline.vs.glsl" },
{ GL_FRAGMENT_SHADER, shaderDir + "spline.fs.glsl" }
};
mShaders.push_back(ShaderPointer(new Shader));
mShaders[0]->compileShaders(shaders);
mShaders[0]->linkShaders();
GLuint var;
var = mShaders[0]->getUniformVariable("uMVP");
mUniforms.insert(UniformKey("uMVP", var));
var = mShaders[0]->getUniformVariable("fColour");
mUniforms.insert(UniformKey("fColour", var));
mShaders[0]->disableShaders();
glBindVertexArray(0);
}
Spline::~Spline()
{
glDeleteVertexArrays(1, &mVao);
glDeleteVertexArrays(1, &mControlBuffer);
glDeleteVertexArrays(1, &mSplineBuffer);
}
void Spline::renderGeometry(atlas::math::Matrix4 projection,
atlas::math::Matrix4 view)
{
USING_ATLAS_MATH_NS;
mShaders[0]->enableShaders();
glBindVertexArray(mVao);
Matrix4 mvp = projection * view * mModel;
glUniformMatrix4fv(mUniforms["uMVP"], 1, GL_FALSE, &mvp[0][0]);
// Draw the control points first.
glUniform3f(mUniforms["fColour"], 1, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mControlBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
if (mShowControlPoints)
{
glPointSize(5.0f);
glDrawArrays(GL_POINTS, 0, GLsizei(mControlPoints.size()));
glPointSize(1.0f);
}
if (mShowCage)
{
glDrawArrays(GL_LINE_STRIP, 0, GLsizei(mControlPoints.size()));
}
// Now draw the spline.
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mSplineBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glUniform3f(mUniforms["fColour"], 0, 1, 0);
if (mShowSpline)
{
glLineWidth(5.0f);
glDrawArrays(GL_LINE_STRIP, 0, mResolution + 1);
glLineWidth(1.0f);
}
if (mShowSplinePoints)
{
glPointSize(8.0f);
glDrawArrays(GL_POINTS, 1, mResolution);
glPointSize(1.0f);
}
glDisableVertexAttribArray(0);
mShaders[0]->disableShaders();
}
void Spline::updateGeometry(atlas::utils::Time const& t)
{
UNUSED(t);
mSplinePosition = interpolateOnSpline();
mCurrentFrame++;
if (mCurrentFrame == mTotalFrames)
{
mIsInterpolationDone = true;
return;
}
}
void Spline::showControlPoints()
{
mShowControlPoints = !mShowControlPoints;
}
void Spline::showCage()
{
mShowCage = !mShowCage;
}
void Spline::showSplinePoints()
{
mShowSplinePoints = !mShowSplinePoints;
}
void Spline::showSpline()
{
mShowSpline = !mShowSpline;
}
bool Spline::doneInterpolation()
{
return mIsInterpolationDone;
}
atlas::math::Point Spline::getSplinePosition()
{
return mSplinePosition;
}
atlas::math::Point Spline::interpolateOnSpline()
{
int n = int(mTable.size());
float totalDistance = mTable[n - 1];
float step = totalDistance / mTotalFrames;
float currDistance = step * mCurrentFrame;
int index = tableLookUp(currDistance);
float t = (1.0f / mResolution) * (index % mResolution);
return evaluateSpline(t);
}
atlas::math::Point Spline::evaluateSpline(float t)
{
USING_ATLAS_MATH_NS;
Vector4 xControls =
{
mControlPoints[0].x, mControlPoints[1].x,
mControlPoints[2].x, mControlPoints[3].x
};
Vector4 yControls =
{
mControlPoints[0].y, mControlPoints[1].y,
mControlPoints[2].y, mControlPoints[3].y
};
Vector4 zControls =
{
mControlPoints[0].z, mControlPoints[1].z,
mControlPoints[2].z, mControlPoints[3].z
};
Vector4 xCoeff = xControls * mBasisMatrix;
Vector4 yCoeff = yControls * mBasisMatrix;
Vector4 zCoeff = zControls * mBasisMatrix;
float xcr, ycr, zcr;
xcr = xCoeff[0] + t * xCoeff[1] + t * t * xCoeff[2] + t * t * t * xCoeff[3];
ycr = yCoeff[0] + t * yCoeff[1] + t * t * yCoeff[2] + t * t * t * yCoeff[3];
zcr = zCoeff[0] + t * zCoeff[1] + t * t * zCoeff[2] + t * t * t * zCoeff[3];
return Point(xcr, ycr, zcr);
}
void Spline::generateArcLengthTable()
{
USING_ATLAS_MATH_NS;
if (!mTable.empty())
{
mTable.clear();
}
float scale = 1.0f / mResolution;
mTable.push_back(0.0f);
for (int i = 1; i < mResolution + 1; ++i)
{
Point p0 = evaluateSpline((i - 1) * scale);
Point p1 = evaluateSpline(i * scale);
Point dist = p0 - p1;
mTable.push_back(mTable[i - 1] + glm::length(dist));
}
}
int Spline::tableLookUp(float distance)
{
// Find the entry in our table that corresponds to the given distance.
float epsilon = chooseEpsilon();
for (int i = 0; i < int(mTable.size()); ++i)
{
if (glm::abs(mTable[i] - distance) < epsilon)
{
return i;
}
}
return -1;
}
float Spline::chooseEpsilon()
{
// Find the largest difference and use that to look up distances
// in our table.
float epsilon = 0.0f;
float diff;
for (int i = 0; i < mTable.size() - 1; ++i)
{
diff = glm::abs(mTable[i] - mTable[i + 1]);
if (diff > epsilon)
{
epsilon = diff;
}
}
return epsilon;
}
void Spline::setSplineCoordinates(std::vector<atlas::math::Point> mControlPoints_) {
mControlPoints = mControlPoints_;
}
The header files for anyone interested.
SplineManager.hpp
#ifndef SPLINEMANAGER_HPP
#define SPLINEMANAGER_HPP
#pragma once
#include <atlas/utils/Geometry.hpp>
#include "Spline.h"
USING_ATLAS_MATH_NS;
USING_ATLAS_GL_NS;
class SplineManager : public atlas::utils::Geometry {
public:
SplineManager(int totalFrames);
~SplineManager();
atlas::math::Point getSplinePosition();
void addSplines();
void showSpline();
void showControlPoints();
void showCage();
void showSplinePoints();
bool doneInterpolation();
void updateGeometry(atlas::utils::Time const& t) override;
void renderGeometry(atlas::math::Matrix4 projection, atlas::math::Matrix4 view) override;
private:
std::vector<Spline> mSplines;
std::vector<atlas::math::Point> mControlPoints;
int mTotalFrames;
int currentSpline;
bool finishedAllSplines;
};
#endif
Spline.h
#ifndef LAB04_INCLUDE_SPLINE_HPP
#define LAB04_INCLUDE_SPLINE_HPP
#pragma once
#include <atlas/utils/Geometry.hpp>
#include <fstream>
class Spline : public atlas::utils::Geometry
{
public:
Spline(int totalFrames, std::vector<atlas::math::Point> mControlPoints_);
Spline(int totalFrames);
~Spline();
void renderGeometry(atlas::math::Matrix4 projection,
atlas::math::Matrix4 view) override;
void updateGeometry(atlas::utils::Time const& t) override;
void showControlPoints();
void showCage();
void showSplinePoints();
void showSpline();
bool doneInterpolation();
atlas::math::Point getSplinePosition();
void setSplineCoordinates(std::vector<atlas::math::Point> mControlPoints_);
private:
atlas::math::Point interpolateOnSpline();
atlas::math::Point evaluateSpline(float t);
void generateArcLengthTable();
int tableLookUp(float distance);
float chooseEpsilon();
atlas::math::Matrix4 mBasisMatrix;
std::vector<atlas::math::Point> mControlPoints;
std::vector<float> mTable;
atlas::math::Point mSplinePosition;
GLuint mVao;
GLuint mControlBuffer;
GLuint mSplineBuffer;
int mResolution;
int mTotalFrames;
int mCurrentFrame;
bool mShowControlPoints;
bool mShowCage;
bool mShowSplinePoints;
bool mShowSpline;
bool mIsInterpolationDone;
};
#endif
ClothScene.hpp
#ifndef SCENE_HPP
#define SCENE_HPP
#pragma once
#include <atlas\utils\Scene.hpp>
#include "ClothCamera.hpp"
#include "SplineManager.hpp"
#include "Grid.hpp"
#include "Spline.h"
#include "Ball.hpp"
//#include "Cloth.hpp"
class ClothScene : public atlas::utils::Scene {
public:
ClothScene();
~ClothScene();
void mousePressEvent(int button, int action, int modifiers, double xPos, double yPos) override;
void mouseMoveEvent(double xPos, double yPos) override;
void keyPressEvent(int key, int scancode, int action, int mods) override;
void screenResizeEvent(int width, int height) override;
void renderScene() override;
void updateScene(double time) override;
private:
bool mIsDragging;
bool mIsPlaying;
float mLastTime;
float mFPS;
float mTick;
float mAnimTime;
float mAnimLength;
glm::vec3 ballPosition;
ClothCamera mCamera;
Grid mGrid;
SplineManager mSplineManager;//, mSpline2;
Ball mBall;
std::vector<atlas::math::Point> mControlPoints;
};
#endif
I have looked through the examples in the Glui package, but I need help with loading (.m) file which is 3D model
this is the sample codes....
#include <iostream>
#include <GL/glut.h>
#include <cstdlib>
using namespace std;
void createMenu(void);
void menu(int value);
void disp(void);
static int win;
static int menid;
static int submenid;
static int primitive = 0; // global variable for the geometric primitive
int main(int argc, char **argv){
// normal initialisation
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
glutInitWindowSize(500,500);
glutInitWindowPosition(100,100);
win = glutCreateWindow("GLUT MENU");
// put all the menu functions in one nice procedure
createMenu();
// set the clearcolor and the callback
glClearColor(0.0,0.0,0.0,0.0);
// register your drawing function
glutDisplayFunc(disp);
// enter the main loop
glutMainLoop();
return 1;
}
//#define USE_SUB_MENU
void createMenu(void){
//#ifdef USE_SUB_MENU
// step 1: Create a submenu, this has to be done first.
submenid = glutCreateMenu(menu); // pass the function "menu" to glutCreateMenu
// Add sub menu entry
glutAddMenuEntry("Teapot", 2);
glutAddMenuEntry("Cube", 3);
glutAddMenuEntry("Torus", 4);
// step 2: Create the main menu, this menu becomes the current menu
menid = glutCreateMenu(menu);
// Create an entry
glutAddMenuEntry("Clear", 1);
glutAddSubMenu("Draw", submenid);
// Create an entry
glutAddMenuEntry("Quit", 0);
// Let the menu respond on the right mouse button
glutAttachMenu(GLUT_RIGHT_BUTTON);
//#else
menid = glutCreateMenu(menu);
// Create an entry
glutAddMenuEntry("Clear", 1);
glutAddMenuEntry("Teapot", 2);
glutAddMenuEntry("Cube", 3);
glutAddMenuEntry("Torus", 4);
// Create an entry
glutAddMenuEntry("Quit", 0);
// Let the menu respond on the right mouse button
glutAttachMenu(GLUT_RIGHT_BUTTON);
//#endif
}
void disp(void){
// Just clean the screen
// this has to be called everytime when you draw sth
glClear(GL_COLOR_BUFFER_BIT);
// draw what the user asked
if(primitive == 1){ // clear the screen
glutPostRedisplay();
}else if(primitive == 2){ // draw the teapot
glutWireTeapot(0.5);
}else if(primitive == 3){ // draw the cube
glutWireCube(0.5);
}else if(primitive == 4){ // draw the torus
glutWireTorus(0.3,0.6,100,100);
}
glFlush();
}
void menu(int value){
if(value == 0){ // the user clicks 'quit'
glutDestroyWindow(win);
exit(0);
}else{
primitive=value; // set the geometric primitive
}
// you would want to redraw now
glutPostRedisplay();
}
I really don't know how to do T_T, requirment is to load .m files using opengl, glut, glui.... So, I need help to load it with opengl without crashing then done.
You'll need a parser for your .M files.
I couldn't find a spec anywhere so I've taken a guess at the format:
#include <glm/glm.hpp>
using namespace glm;
struct Vertex
{
vec3 position;
vec3 normal;
};
vector< Vertex > LoadM( istream& in )
{
vector< Vertex > verts;
map< int, vec3 > positions;
map< int, vec3 > normals;
string lineStr;
while( getline( in, lineStr ) )
{
istringstream lineSS( lineStr );
string lineType;
lineSS >> lineType;
// vertex
if( lineType == "Vertex" )
{
int idx;
float x = 0, y = 0, z = 0;
lineSS >> idx >> x >> y >> z;
positions[ idx ] = vec3( x, y, z );
}
// face
if( lineType == "Face" )
{
int indexes[ 3 ];
int idx;
lineSS >> idx >> indexes[0] >> indexes[1] >> indexes[2];
// http://www.opengl.org/wiki/Calculating_a_Surface_Normal
vec3 U( positions[ indexes[1] ] - positions[ indexes[0] ] );
vec3 V( positions[ indexes[2] ] - positions[ indexes[0] ] );
vec3 faceNormal = normalize( cross( U, V ) );
for( size_t j = 0; j < 3; ++j )
{
Vertex vert;
vert.position = vec3( positions[ indexes[j] ] );
vert.normal = faceNormal;
verts.push_back( vert );
}
}
}
return verts;
}
Used in context:
#include <GL/glut.h>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>
#include <vector>
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
using namespace glm;
struct Vertex
{
vec3 position;
vec3 normal;
};
vector< Vertex > LoadM( istream& in )
{
vector< Vertex > verts;
map< int, vec3 > positions;
string lineStr;
while( getline( in, lineStr ) )
{
istringstream lineSS( lineStr );
string lineType;
lineSS >> lineType;
// parse vertex line
if( lineType == "Vertex" )
{
int idx = 0;
float x = 0, y = 0, z = 0;
lineSS >> idx >> x >> y >> z;
positions.insert( make_pair( idx, vec3( x, y, z ) ) );
}
// parse face line
if( lineType == "Face" )
{
int indexes[ 3 ] = { 0 };
int idx = 0;
lineSS >> idx >> indexes[0] >> indexes[1] >> indexes[2];
// http://www.opengl.org/wiki/Calculating_a_Surface_Normal
vec3 U( positions[ indexes[1] ] - positions[ indexes[0] ] );
vec3 V( positions[ indexes[2] ] - positions[ indexes[0] ] );
vec3 faceNormal = normalize( cross( U, V ) );
for( size_t j = 0; j < 3; ++j )
{
Vertex vert;
vert.position = vec3( positions[ indexes[j] ] );
vert.normal = faceNormal;
verts.push_back( vert );
}
}
}
return verts;
}
// mouse state
int btn;
ivec2 startMouse;
ivec2 startRot, curRot;
ivec2 startTrans, curTrans;
void mouse(int button, int state, int x, int y )
{
y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
{
btn = button;
startMouse = ivec2( x, y );
startRot = curRot;
}
if( button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN )
{
btn = button;
startMouse = ivec2( x, y );
startTrans = curTrans;
}
}
void motion( int x, int y )
{
y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
ivec2 curMouse( x, y );
if( btn == GLUT_LEFT_BUTTON )
{
curRot = startRot + ( curMouse - startMouse );
}
else if( btn == GLUT_RIGHT_BUTTON )
{
curTrans = startTrans + ( curMouse - startMouse );
}
glutPostRedisplay();
}
vector< Vertex > model;
void display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
// "pan"
glTranslatef( curTrans.x / w * 2, curTrans.y / h * 2, 0 );
gluPerspective( 60, ar, 0.1, 20 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -10 );
glPushMatrix();
// apply mouse rotation
glRotatef( curRot.x % 360, 0, 1, 0 );
glRotatef( -curRot.y % 360, 1, 0, 0 );
glColor3ub( 255, 0, 0 );
// draw model
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), &model[0].position );
glNormalPointer( GL_FLOAT, sizeof(Vertex), &model[0].normal );
glDrawArrays( GL_TRIANGLES, 0, model.size() );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
// draw bounding cube
glDisable( GL_LIGHTING );
glColor3ub( 255, 255, 255 );
glutWireCube( 7 );
glEnable( GL_LIGHTING );
glPopMatrix();
glutSwapBuffers();
}
// return the x/y/z min/max of some geometry
template< typename Vec >
pair< Vec, Vec > GetExtents
(
const Vec* pts,
size_t stride,
size_t count
)
{
typedef typename Vec::value_type Scalar;
Vec pmin( std::numeric_limits< Scalar >::max() );
Vec pmax( std::min( std::numeric_limits< Scalar >::min(),
(Scalar)-std::numeric_limits< Scalar >::max() ) );
// find extents
unsigned char* base = (unsigned char*)pts;
for( size_t i = 0; i < count; ++i )
{
const Vec& pt = *(Vec*)base;
pmin = glm::min( pmin, pt );
pmax = glm::max( pmax, pt );
base += stride;
}
return make_pair( pmin, pmax );
}
// centers geometry around the origin
// and scales it to fit in a size^3 box
template< typename Vec >
void CenterAndScale
(
Vec* pts,
size_t stride,
size_t count,
const typename Vec::value_type& size
)
{
typedef typename Vec::value_type Scalar;
// get min/max extents
pair< Vec, Vec > exts = GetExtents( pts, stride, count );
// center and scale
const Vec center = ( exts.first * Scalar( 0.5 ) ) + ( exts.second * Scalar( 0.5f ) );
const Scalar factor = size / glm::compMax( exts.second - exts.first );
unsigned char* base = (unsigned char*)pts;
for( size_t i = 0; i < count; ++i )
{
Vec& pt = *(Vec*)base;
pt = ((pt - center) * factor);
base += stride;
}
}
int main( int argc, char **argv )
{
ifstream ifile( "bunny.m" );
model = LoadM( ifile );
if( model.empty() )
{
cerr << "Empty model!" << endl;
return -1;
}
CenterAndScale( &model[0].position, sizeof( Vertex ), model.size(), 7 );
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMouseFunc( mouse );
glutMotionFunc( motion );
glEnable( GL_DEPTH_TEST );
// set up "headlamp"-like light
glShadeModel( GL_SMOOTH );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
GLfloat position[] = { 0, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, position );
glPolygonMode( GL_FRONT, GL_FILL );
glPolygonMode( GL_BACK, GL_LINE );
glutMainLoop();
return 0;
}
This is basic question about lighting and texturing in OpenGL. I tried to apply a texture in pure OpenGL app, unfortunately, the color is different from the texture. Here is the texture:
and here is what i got after applying the texture:
I used Videotutorialrocks BMP Loader. This coloring problem does not exist if i use their BMP file (i.e. the color is the same as the texture file).
Here's the code:
#include <windows.h>
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "imageloader.h"
using std::stringstream;
using std::cout;
using std::endl;
using std::ends;
using namespace std;
float lpos[4] = {1.0,0.0,0.0,0.0};
void *font = GLUT_BITMAP_8_BY_13;
float color[4] = {0.0, 1.0, 0.0, 1.0};
GLuint _textureId; //The id of the texture
float a = 0;
float eye_x = 5.0;
float eye_y = 5.0;
float eye_z = 5.0;
//Makes the image into a texture, and returns the id of the texture
GLuint loadTexture(Image* image) {
GLuint textureId;
glGenTextures(1, &textureId); //Make room for our texture
glBindTexture(GL_TEXTURE_2D, textureId); //Tell OpenGL which texture to edit
//Map the image to the texture
glTexImage2D(GL_TEXTURE_2D, //Always GL_TEXTURE_2D
0, //0 for now
GL_RGB, //Format OpenGL uses for image
image->width, image->height, //Width and height
0, //The border of the image
GL_RGB, //GL_RGB, because pixels are stored in RGB format
GL_UNSIGNED_BYTE, //GL_UNSIGNED_BYTE, because pixels are stored
//as unsigned numbers
image->pixels); //The actual pixel data
return textureId; //Returns the id of the texture
}
// write 2d text using GLUT
// The projection matrix must be set to orthogonal before call this function.
void drawString(const char *str, int x, int y, float color[4], void *font)
{
glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask
glDisable(GL_LIGHTING); // need to disable lighting for proper text color
glColor4fv(color); // set text color
glRasterPos2i(x, y); // place text position
// loop all characters in the string
while(*str)
{
glutBitmapCharacter(font, *str);
++str;
}
glEnable(GL_LIGHTING);
glPopAttrib();
}
void changeSize(int w, int h) {
// Prevent a divide by zero, when window is too short. (you cant make a window of zero width).
if(h == 0)
h = 1;
float ratio = 1.0* w / h;
// Reset the coordinate system before modifying
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Set the viewport to be the entire window
glViewport(0, 0, w, h);
// Set the correct perspective.
gluPerspective(45,ratio,1,100);
glMatrixMode(GL_MODELVIEW);
}
void initRendering(){
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
glEnable(GL_NORMALIZE);
glEnable(GL_COLOR_MATERIAL);
Image* image = loadBMP("vtr_6.bmp");
_textureId = loadTexture(image);
delete image;
}
void renderScene(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Add ambient light
//GLfloat ambientColor[] = {0.4f, 0.2f, 0.2f, 1.0f}; //Color(0.2, 0.2, 0.2)
GLfloat ambientColor[] = {1.0f, 1.0f, 1.0f, 1.0f}; //Color(0.2, 0.2, 0.2)
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
//Add positioned light
//GLfloat lightColor0[] = {0.5f, 0.5f, 0.5f, 1.0f}; //Color (0.5, 0.5, 0.5)
GLfloat lightColor0[] = {1.0f, 1.0f, 1.0f, 1.0f}; //Color (0.5, 0.5, 0.5)
GLfloat lightPos0[] = {4.0f, 0.0f, 8.0f, 1.0f}; //Positioned at (4, 0, 8)
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);
//Add directed light
//GLfloat lightColor1[] = {0.7f, 0.2f, 0.1f, 1.0f}; //Color (0.5, 0.2, 0.2)
GLfloat lightColor1[] = {1.0f, 1.0f, 1.0f, 1.0f};
//Coming from the direction (-1, 0.5, 0.5)
GLfloat lightPos1[] = {1.0f, 0.5f, 0.5f, 0.0f};
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1);
glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _textureId);
//Bottom
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gluLookAt(eye_x,eye_y,eye_z,
0.0,0.0,0.0,
0.0f,1.0f,0.0f);
stringstream ss;
ss << std::fixed << std::setprecision(2);
ss << "Eye Position : x,y,z = (" << eye_x << ", " << eye_y << ", " << eye_z << ")" << ends;
drawString(ss.str().c_str(), -6, 1, color, font);
ss.str("");
glRotatef(a,0,1,0);
glutSolidTeapot(2);
glDisable(GL_TEXTURE_2D);
a+=0.1;
glutSwapBuffers();
}
void processNormalKeys(unsigned char key, int x, int y) {
switch ( key )
{
case 27:
exit(0);
break;
case '1':
eye_x += 0.1;
break;
case '2':
eye_x -= 0.1;
break;
case '3' :
eye_y += 0.1;
break;
case '4' :
eye_y -= 0.1;
break;
case '5':
eye_z += 0.1;;
break;
case '6':
eye_z -= 0.1;
break;
case '0':
eye_x = 5.0;
eye_y = 5.0;
eye_z = 5.0;
break;
}
}
#define printOpenGLError() printOglError(__FILE__, __LINE__)
int printOglError(char *file, int line)
{
GLenum glErr;
int retCode = 0;
glErr = glGetError();
while (glErr != GL_NO_ERROR)
{
printf("glError in file %s # line %d: %s\n", file, line, gluErrorString(glErr));
retCode = 1;
glErr = glGetError();
}
return retCode;
}
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100,100);
glutInitWindowSize(800,600);
glutCreateWindow("OpenGL Teapot w/ lighting");
initRendering();
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
glutKeyboardFunc(processNormalKeys);
glutIdleFunc(renderScene);
glEnable(GL_DEPTH_TEST);
glClearColor(0.0,0.0,0.0,0.0);
glutMainLoop();
return 0;
}
Here is the code for Image Loader:
#include <assert.h>
#include <fstream>
#include "imageloader.h"
using namespace std;
Image::Image(char* ps, int w, int h) : pixels(ps), width(w), height(h) {
}
Image::~Image() {
delete[] pixels;
}
namespace {
//Converts a four-character array to an integer, using little-endian form
int toInt(const char* bytes) {
return (int)(((unsigned char)bytes[3] << 24) |
((unsigned char)bytes[2] << 16) |
((unsigned char)bytes[1] << 8) |
(unsigned char)bytes[0]);
}
//Converts a two-character array to a short, using little-endian form
short toShort(const char* bytes) {
return (short)(((unsigned char)bytes[1] << 8) |
(unsigned char)bytes[0]);
}
//Reads the next four bytes as an integer, using little-endian form
int readInt(ifstream &input) {
char buffer[4];
input.read(buffer, 4);
return toInt(buffer);
}
//Reads the next two bytes as a short, using little-endian form
short readShort(ifstream &input) {
char buffer[2];
input.read(buffer, 2);
return toShort(buffer);
}
//Just like auto_ptr, but for arrays
template<class T>
class auto_array {
private:
T* array;
mutable bool isReleased;
public:
explicit auto_array(T* array_ = NULL) :
array(array_), isReleased(false) {
}
auto_array(const auto_array<T> &aarray) {
array = aarray.array;
isReleased = aarray.isReleased;
aarray.isReleased = true;
}
~auto_array() {
if (!isReleased && array != NULL) {
delete[] array;
}
}
T* get() const {
return array;
}
T &operator*() const {
return *array;
}
void operator=(const auto_array<T> &aarray) {
if (!isReleased && array != NULL) {
delete[] array;
}
array = aarray.array;
isReleased = aarray.isReleased;
aarray.isReleased = true;
}
T* operator->() const {
return array;
}
T* release() {
isReleased = true;
return array;
}
void reset(T* array_ = NULL) {
if (!isReleased && array != NULL) {
delete[] array;
}
array = array_;
}
T* operator+(int i) {
return array + i;
}
T &operator[](int i) {
return array[i];
}
};
}
Image* loadBMP(const char* filename) {
ifstream input;
input.open(filename, ifstream::binary);
assert(!input.fail() || !"Could not find file");
char buffer[2];
input.read(buffer, 2);
assert(buffer[0] == 'B' && buffer[1] == 'M' || !"Not a bitmap file");
input.ignore(8);
int dataOffset = readInt(input);
//Read the header
int headerSize = readInt(input);
int width;
int height;
switch(headerSize) {
case 40:
//V3
width = readInt(input);
height = readInt(input);
input.ignore(2);
assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
assert(readShort(input) == 0 || !"Image is compressed");
break;
case 12:
//OS/2 V1
width = readShort(input);
height = readShort(input);
input.ignore(2);
assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
break;
case 64:
//OS/2 V2
assert(!"Can't load OS/2 V2 bitmaps");
break;
case 108:
//Windows V4
assert(!"Can't load Windows V4 bitmaps");
break;
case 124:
//Windows V5
assert(!"Can't load Windows V5 bitmaps");
break;
default:
assert(!"Unknown bitmap format");
}
//Read the data
int bytesPerRow = ((width * 3 + 3) / 4) * 4 - (width * 3 % 4);
int size = bytesPerRow * height;
auto_array<char> pixels(new char[size]);
input.seekg(dataOffset, ios_base::beg);
input.read(pixels.get(), size);
//Get the data into the right format
auto_array<char> pixels2(new char[width * height * 3]);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
for(int c = 0; c < 3; c++) {
pixels2[3 * (width * y + x) + c] =
pixels[bytesPerRow * y + 3 * x + (2 - c)];
}
}
}
input.close();
return new Image(pixels2.release(), width, height);
}
And this is the header file:
#ifndef IMAGE_LOADER_H_INCLUDED
#define IMAGE_LOADER_H_INCLUDED
//Represents an image
class Image {
public:
Image(char* ps, int w, int h);
~Image();
/* An array of the form (R1, G1, B1, R2, G2, B2, ...) indicating the
* color of each pixel in image. Color components range from 0 to 255.
* The array starts the bottom-left pixel, then moves right to the end
* of the row, then moves up to the next column, and so on. This is the
* format in which OpenGL likes images.
*/
char* pixels;
int width;
int height;
};
//Reads a bitmap image from file.
Image* loadBMP(const char* filename);
#endif
I have tried to replace the file format for glTexImage2D with GL_BGR_EXT, but no result. Is there any way to correct the texture?
Perhaps your image has an alpha channel, which requires GL_RGBA, not GL_RGB.
Try opening your texture with some sort of image editing software (gimp, photoshop, etc), and save it as a BMP with 24 color bits (I think: 8 for each r/b/g), and just make sure all of the BMP settings are correct. It definitely looks like it's a problem with the format of your texture. There are multiple BMP formats.
See what happens when you disable lighting.
Also this page suggests you should use:
glFrontFace(GL_CW);
glutSolidTeapot(size);
glFrontFace(GL_CCW);
http://pyopengl.sourceforge.net/documentation/manual/glutSolidTeapot.3GLUT.html
In the loadTexture function, in glTexImage2D change the second(format) GL_RGB to GL_BGR.
If you try loading PNG image file, add the alpha, too.
I writing small program in OpenGL, and I have problem ( textures are skew, and I dont know why, this model work in another obj viewer)
What I have:
http://img696.imageshack.us/i/obrazo.png/
What I want
http://img88.imageshack.us/i/obraz2d.jpg/
Code of project (I use devil for images):
#pragma once
#include <windows.h>
#define GLUT_DISABLE_ATEXIT_HACK
#include <glut.h>
#include <GL/glu.h>
#include <GL/gl.h>
#include <GL/GLEXT.h>
#include <iostream>
#include <cmath>
#include <IL/il.h>
#include <vector>
#include <fstream>
#include <string>
#include <cstdlib>
#include <sstream>
#include <clocale>
class TextureManager
{
struct TextureInfo{
std::string name;
GLuint image;
};
private:
std::vector<TextureInfo> textures;
public:
TextureManager(void);
~TextureManager(void);
bool AddTexture(std::string name, std::string fileName);
int GetTexture(std::string name);
};
TextureManager::TextureManager(void)
{
ilInit();
}
TextureManager::~TextureManager(void)
{
}
bool TextureManager::AddTexture(std::string name, std::string fileName)
{
bool success;
ILuint texId;
GLuint image;
ilGenImages(1, &texId);
ilBindImage(texId);
success = ilLoadImage((WCHAR*)fileName.c_str());
if(success)
{
success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);
if(!success)
{
return false;
}
}
else
{
return false;
}
glGenTextures(1, &image);
glBindTexture(GL_TEXTURE_2D, image);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST_MIPMAP_LINEAR);
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT),GL_RGB, GL_UNSIGNED_BYTE, ilGetData());
ilDeleteImages(1, &texId);
TextureInfo ti;
ti.name = name;
ti.image = image;
textures.push_back(ti);
return true;
}
int TextureManager::GetTexture(std::string name)
{
int size = textures.size();
for(int i=0;i<size;i++)
{
if(textures.at(i).name.compare(name) == 0)
{
return textures.at(i).image;
}
}
return -1;
}
using namespace std;
TextureManager tm;
struct Point
{
double x,y,z;
};
struct Normal
{
double x,y,z;
};
struct Triangle
{
int a,b,c;
int at,bt,ct;
int an,bn,cn;
};
struct TexCord
{
float x,y;
};
vector<Point> points;
vector<Normal> normals;
vector<Triangle> triangles;
vector<TexCord> texcords;
int w,h;
double j = 0.0;
double k = 0.0;
int mode = 1;
bool showNormals = false;
void setCamera(double eyex,double eyey, double eyez, double centerx, double centery, double centerz)
{
gluLookAt(eyex,eyey,eyez,centerx,centery,centerz,0,1,0);
}
void DrawPoint(TexCord tc,Point p,Normal n)
{
glTexCoord2f(tc.x,tc.y);
glNormal3f(n.x,n.y,n.z);
glVertex3f(p.x,p.y,p.z);
}
void DrawNormal(Point p,Normal n)
{
glPushMatrix();
glTranslated(p.x,p.y,p.z);
glBegin(GL_LINES);
glVertex3f(0,0,0);
glVertex3f(n.x*2,n.y*2,n.z*2);
glEnd();
glPopMatrix();
}
void processNormalKeys(unsigned char key, int x, int y) {
if (key == 27)
exit(0);
if (key == 'q')
{
mode = 0;
}
if (key == 'w')
{
mode = 1;
}
if (key == 'a')
{
k -= 0.1;
}
if (key == 's')
{
k += 0.1;
}
if (key == 'z')
{
j -= 0.1;
}
if (key == 'x')
{
j += 0.1;
}
if (key == 'n')
{
if(showNormals == true)
{
showNormals = false;
}
else
{
showNormals = true;
}
}
glutPostRedisplay();
}
void renderScene(void) {
//j+=0.0005;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
setCamera(15*sin(j),15*cos(k),15*cos(j)*sin(k),0,0,0);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
//glEnable(GL_LIGHT0);
float ambient[]={ 1.0, 1.0, 1.0, 0.0};
float diffuse[]={1.0, 1.0, 1.0, 1.0};
float position[]={25.0,25.0, 25.0, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, diffuse);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, position);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tm.GetTexture("tex"));
if(mode == 0 )
{
glBegin(GL_TRIANGLES);
}
else
{
glBegin(GL_LINES);
}
for(int i=0;i<triangles.size();i++)
{
//double r = rand()/(double)RAND_MAX;
//glColor3f(r,r,r);
DrawPoint(texcords[triangles[i].ct-1],points[triangles[i].c-1],normals[triangles[i].cn-1]);
DrawPoint(texcords[triangles[i].bt-1],points[triangles[i].b-1],normals[triangles[i].bn-1]);
DrawPoint(texcords[triangles[i].at-1],points[triangles[i].a-1],normals[triangles[i].an-1]);
}
glEnd();
glDisable(GL_TEXTURE_2D);
if(showNormals == true)
{
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glColor3f(1,0.5f,0.25f);
for(int i = 0;i<points.size();i++)
{
DrawNormal(points[i],normals[i]);
}
}
glFlush();
glutSwapBuffers();
}
void reshape (int width, int height) {
w = width; h = height;
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
}
void update(int value) {
j += 0.1f;
glutPostRedisplay(); //Tell GLUT that the scene has changed
//Tell GLUT to call update again in 25 milliseconds
// glutTimerFunc(100, update, 0);
}
int main(int argc, char **argv) {
vector<Normal> *nn = &normals;
vector<Point> *pp = &points;
vector<Triangle> *tt = &triangles;
vector<TexCord> *ttcc = &texcords;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100,100);
glutInitWindowSize(320,320);
glutCreateWindow("Model view");
glutKeyboardFunc(processNormalKeys);
glutDisplayFunc(renderScene);
//glutIdleFunc(renderScene);
glutReshapeFunc(reshape);
ifstream f;
string cmd;
f.open ("model.obj");
if (f.is_open())
{
while(!f.eof())
{
f>>cmd;
if(cmd=="v")
{
Point p;
f>>p.x;
f>>p.y;
f>>p.z;
points.push_back(p);
}
if(cmd=="vn")
{
Normal n;
f>>n.x;
f>>n.y;
f>>n.z;
normals.push_back(n);
}
if(cmd=="vt")
{
TexCord tc;
f>>tc.x;
f>>tc.y;
texcords.push_back(tc);
}
if(cmd=="f")
{
Triangle t;
string str;
string pointStr,normalStr,cordStr;
string delimeter("/");
int pos,pos2;
stringstream ss (stringstream::in | stringstream::out);
f>>str;
pos = str.find(delimeter);
pointStr = str.substr(0,pos);
cordStr = str.substr(pos+delimeter.length());
pos2 = cordStr.find(delimeter);
normalStr = cordStr.substr(pos2+delimeter.length());
cordStr = cordStr.substr(0,pos2);
ss<<pointStr;
ss>>t.a;
ss.clear();
ss<<normalStr;
ss>>t.an;
ss.clear();
ss<<cordStr;
ss>>t.at;
ss.clear();
f>>str;
pos = str.find(delimeter);
pointStr = str.substr(0,pos);
cordStr = str.substr(pos+delimeter.length());
pos2 = cordStr.find(delimeter);
normalStr = cordStr.substr(pos2+delimeter.length());
cordStr = cordStr.substr(0,pos2);
ss<<pointStr;
ss>>t.b;
ss.clear();
ss<<normalStr;
ss>>t.bn;
ss.clear();
ss<<cordStr;
ss>>t.bt;
ss.clear();
f>>str;
pos = str.find(delimeter);
pointStr = str.substr(0,pos);
cordStr = str.substr(pos+delimeter.length());
pos2 = cordStr.find(delimeter);
normalStr = cordStr.substr(pos2+delimeter.length());
cordStr = cordStr.substr(0,pos2);
ss<<pointStr;
ss>>t.c;
ss.clear();
ss<<normalStr;
ss>>t.cn;
ss.clear();
ss<<cordStr;
ss>>t.ct;
ss.clear();
triangles.push_back(t);
}
cmd = "";
}
f.close(); }
tm.AddTexture("tex","texture.png");
//glutTimerFunc(100, update, 0);
glutMainLoop();
}
I think I'd just push a dummy point on the beginning of your points vector, and leave the point references 1-based. I'd also get rid of the while (!in.eof()), which will normally read the last line twice. Finally, I'd use a few overloads of operator>> to read most of the data, giving a result that looked something like this:
#include <locale>
#include <vector>
#include <sstream>
#include <string>
#include <fstream>
/* change to "#if 0" for normal compilation: */
#if 1
struct Triangle { float a, an, atc, b, bn, btc, c, cn, ctc; };
struct Vector2d { float x, y; };
struct Vector3d { float x, y, z; };
std::vector<Triangle> triangles;
std::vector<Vector3d> points, normals;
std::vector<Vector2d> texcords;
#endif
namespace {
struct slashsep: std::ctype<char> {
slashsep(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::mask());
rc['/'] = std::ctype_base::space; // Treat '/' as a separator between numbers.
rc[' '] = std::ctype_base::space;
rc['\n'] = std::ctype_base::space;
rc['\t'] = std::ctype_base::space;
return &rc[0];
}
};
std::istream &operator>>(std::istream &in, Triangle &triangle) {
std::string str;
std::getline(in, str);
std::istringstream temp(str);
slashsep loc;
temp.imbue(std::locale(std::locale(), &loc));
temp >> triangle.a >> triangle.an >> triangle.atc;
temp >> triangle.b >> triangle.bn >> triangle.btc;
temp >> triangle.c >> triangle.cn >> triangle.ctc;
return in;
}
std::istream &operator>>(std::istream &in, Vector3d &v) {
return in >> v.x >> v.y >> v.z;
}
std::istream &operator>>(std::istream &in, Vector2d &v) {
return in >> v.x >> v.y;
}
}
bool read_obj(std::string const &fileName) {
points.clear();
points.push_back(Vector3d());
triangles.clear();
std::ifstream in(fileName.c_str());
std::string cmd;
if (!in.is_open())
return false;
while(in>>cmd) {
if(cmd=="v") {
Vector3d vector;
in >> vector;
points.push_back(vector);
}
if(cmd=="vt") {
Vector2d texcord;
in >> texcord;
texcords.push_back(texcord);
}
if(cmd=="vn"){
Vector3d normal;
in >> normal;
normals.push_back(normal);
}
if(cmd=="f") {
Triangle triangle;
in >> triangle;
triangles.push_back(triangle);
}
}
return true;
}
One minor point: while using the locale to treat '/' as a separator between numbers works for the specific variant of OBJ that you're looking at, it will not work for files that contain lines like:
f a//b c//d e//f
Nonetheless, the general idea (most reading with operator>>) will be fine when/if you decide to enhance it to handle this variant of the format.
Edit: I think I just realized part of the problem. The code to read a face should be like this:
temp >> triangle.a >> triangle.atc >> triangle.an;
temp >> triangle.b >> triangle.btc >> triangle.bn;
temp >> triangle.c >> triangle.ctc >> triangle.cn;
I.e., in the file, it's arranged as vertex/texcoord/normal, but your code and my previous version above tried to read it as vertex/normal/texcoord). Once the code was arranged this way, checking against a reference made the problem fairly obvious.
Ok i fix all :D:D
What I do:
change
glTexCoord2f(tc.x,tc.y);
to
glTexCoord2f(tc.x,1-tc.y);
and the most important thing I change image resolution to 1024x1024 ( I useing mipmaping so I think correct image resolution is important)
Now:
Can sameone explain me why lTexCoord2f(tc.x,1-tc.y); work ?
and is it important to make every image 2^x resolution ?