Related
I am trying to load gltf 2.0 models in openGl. For 1st model it is works fine. But for 2nd model, Model is loading well but have same texture as previous one. I don't know how to apply different textures on different models and changing position of one model changes position of all models same way. I searched much online but very few examples of loading gltf in openGL. They also not show how to load 2nd or 3rd gltf model in same project (screen).
My code is using libraries to load gltf and to compile shaders. That are few line of code libraries have simple openGl code for loading gltf and compiling shaders.
Main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
#define GL_SILENCE_DEPRECATION
#include <glad/glad.h> // Extend OpenGL Specfication to version 4.5 for WIN64 and versions 4.1 for Apple (note: two different files).
#include <imgui/imgui.h> // Load GUI library - Dear ImGui - https://github.com/ocornut/imgui
#include <imgui/imgui_impl_glfw.h> // Platform ImGui using GLFW
#include <imgui/imgui_impl_opengl3.h> // Platform new OpenGL - aka better than 3.3 core version.
#include <GLFW/glfw3.h> // Add GLFW - library to launch a window and callback mouse and keyboard - https://www.glfw.org
//#include <tinygltf/tiny_gltf.h> // Model loading library - tiny gltf - https://github.com/syoyo/tinygltf
//#include "src/stb_image.hpp" // Add library to load images for textures
#define GLM_ENABLE_EXPERIMENTAL // Enable all functions include non LTS
#include <glm/glm.hpp> // Add helper maths library - GLM 0.9.9.9 - https://github.com/g-truc/glm - for example variables vec3, mat and operators.
#include <glm/gtx/transform.hpp> // Help us with transforms
using namespace glm;
//#include "src/Mesh.hpp" // Simplest mesh holder and OBJ loader - can update more - from https://github.com/BennyQBD/ModernOpenGLTutorial
#include "src/Pipeline.hpp" // Setup pipeline and load shaders.
#include "src/Content.hpp" // Setup content loader and drawing functions - https://github.com/KhronosGroup/glTF - https://github.com/syoyo/tinygltf
#include "src/Debugger.hpp" // Setup debugger functions.
// Main fuctions
void startup();
void update();
void render();
void ui();
void endProgram();
// HELPER FUNCTIONS OPENGL
void hintsGLFW();
//string readShader(string name);
//void checkErrorShader(GLuint shader);
inline void errorCallbackGLFW(int error, const char *description){cout << "Error GLFW: " << description << "\n";};
void debugGL();
void APIENTRY openGLDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const GLvoid *userParam);
GLenum glCheckError_(const char *file, int line);
#define glCheckError() glCheckError_(__FILE__, __LINE__)
// Setup all the message loop callbacks - do this before Dear ImGui
// Callback functions for the window and interface devices
void onResizeCallback(GLFWwindow *window, int w, int h);
void onKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
void onMouseButtonCallback(GLFWwindow *window, int button, int action, int mods);
void onMouseMoveCallback(GLFWwindow *window, double x, double y);
void onMouseWheelCallback(GLFWwindow *window, double xoffset, double yoffset);
// VARIABLES
GLFWwindow *window; // Keep track of the window
auto windowWidth = 800; // Window width
auto windowHeight =800; // Window height
auto running(true); // Are we still running our main loop
mat4 projMatrix; // Our Projection Matrix
vec3 cameraPosition = vec3(0.0f, 0.0f, 5.0f); // Where is our camera
vec3 cameraFront = vec3(0.0f, 0.0f, -0.5f); // Camera front vector
vec3 cameraUp = vec3(0.0f, 1.0f, 0.0f); // Camera up vector
auto aspect = (float)windowWidth / (float)windowHeight; // Window aspect ration
auto fovy = 45.0f; // Field of view (y axis)
bool keyStatus[1024]; // Track key strokes
auto currentTime = 0.0f; // Framerate
auto deltaTime = 0.0f; // time passed
auto lastTime = 0.0f; // Used to calculate Frame rate
Pipeline pipeline; // Add one pipeline plus some shaders.
Pipeline pipelinelegs; // Add one pipeline plus some shaders.
Content content; // Add one content loader (+drawing).
Content contentlegs;
GLuint texture, texture2;
//Pipeline pipelineClegs; // Add one pipeline plus some shaders.
//Content contentClegs;
Debugger debugger; // Add one debugger to use for callbacks ( Win64 - openGLDebugCallback() ) or manual calls ( Apple - glCheckError() )
vec3 modelPosition; // Model position
vec3 modelRotation; // Model rotation
int main()
{
cout << endl << "===" << endl << "3D Graphics and Animation - Running..." << endl;
if (!glfwInit()) // Check if GLFW is working, if not then stop all
{
cout << "Could not initialise GLFW...";
return -1;
}
glfwSetErrorCallback(errorCallbackGLFW); // Setup a function callback to catch and display all GLFW errors.
hintsGLFW(); // Setup glfw with various hints.
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); // Grab reference to monitor
// windowWidth = mode->width; windowHeight = mode->height; //fullscreen
// window = glfwCreateWindow(windowWidth, windowHeight, title.c_str(), glfwGetPrimaryMonitor(), NULL); // fullscreen
// Create our Window
const auto windowTitle = "My 3D Graphics and Animation OpenGL Application"s;
window = glfwCreateWindow(windowWidth, windowHeight, windowTitle.c_str(), NULL, NULL);
if (!window) // Test if Window or OpenGL context creation failed
{
cout << "Could not initialise GLFW...";
glfwTerminate();
return -1;
}
glfwSetWindowPos(window, 10, 10); // Place it in top corner for easy debugging.
glfwMakeContextCurrent(window); // making the OpenGL context current
// GLAD: Load OpenGL function pointers - aka update specs to newest versions - plus test for errors.
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD...";
glfwMakeContextCurrent(NULL);
glfwTerminate();
return -1;
}
glfwSetWindowSizeCallback(window, onResizeCallback); // Set callback for resize
glfwSetKeyCallback(window, onKeyCallback); // Set Callback for keys
glfwSetMouseButtonCallback(window, onMouseButtonCallback); // Set callback for mouse click
glfwSetCursorPosCallback(window, onMouseMoveCallback); // Set callback for mouse move
glfwSetScrollCallback(window, onMouseWheelCallback); // Set callback for mouse wheel.
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // Set mouse cursor Fullscreen
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Set mouse cursor FPS fullscreen.
// Setup Dear ImGui and add context - https://blog.conan.io/2019/06/26/An-introduction-to-the-Dear-ImGui-library.html
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO(); //(void)io;
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
ImGui::StyleColorsLight(); // ImGui::StyleColorsDark(); // Setup Dear ImGui style
// Setup Platform/Renderer ImGui backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
const auto glsl_version = "#version 410";
ImGui_ImplOpenGL3_Init(glsl_version);
#if defined(__WIN64__)
debugGL(); // Setup callback to catch openGL errors. V4.2 or newer
#elif(__APPLE__)
glCheckError(); // Old way of checking for errors. Newest not implemented by Apple. Manually call function anywhere in code to check for errors.
#endif
glfwSwapInterval(1); // Ony render when synced (V SYNC) - https://www.tomsguide.com/features/what-is-vsync-and-should-you-turn-it-on-or-off
glfwWindowHint(GLFW_SAMPLES, 2); // Multisampling - covered in lectures - https://www.khronos.org/opengl/wiki/Multisampling
startup(); // Setup all necessary information for startup (aka. load texture, shaders, models, etc).
cout << endl << "Starting main loop and rendering..." << endl;
do{ // run until the window is closed
auto currentTime = (float)glfwGetTime(); // retrieve timelapse
deltaTime = currentTime - lastTime; // Calculate delta time
lastTime = currentTime; // Save for next frame calculations.
glfwPollEvents(); // poll callbacks
update(); // update (physics, animation, structures, etc)
render(); // call render function.
ui(); // call function to render ui.
#if defined(__APPLE__)
glCheckError(); // Manually checking for errors for MacOS, Windows has a callback.
#endif
glfwSwapBuffers(window); // swap buffers (avoid flickering and tearing)
running &= (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_RELEASE); // exit if escape key pressed
running &= (glfwWindowShouldClose(window) != GL_TRUE);
} while (running);
endProgram(); // Close and clean everything up...
// cout << "\nPress any key to continue...\n";
// cin.ignore(); cin.get(); // delay closing console to read debugging errors.
return 0;
}
void hintsGLFW(){
auto majorVersion = 3; auto minorVersion = 3; // define OpenGL version - at least 3.3 for bare basic NEW OpenGL
#if defined(__WIN64__)
majorVersion = 4; minorVersion = 5; // Recommended for Windows 4.5, but latest is 4.6 (not all drivers support 4.6 version).
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); // Create context in debug mode - for debug message callback
#elif(__APPLE__)
majorVersion = 4; minorVersion = 1; // Max support for OpenGL in MacOS
#endif
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // https://www.glfw.org/docs/3.3/window_guide.html
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, majorVersion);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minorVersion);
}
void endProgram()
{
// Clean ImGui
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwMakeContextCurrent(NULL); // destroys window handler
glfwTerminate(); // destroys all windows and releases resources.
}
void startup()
{
// string name = "assets/Fabric01_4K_BaseColor.png";
// int iWidth, iHeight, iChannels;
// unsigned char *iData = stbi_load(name.c_str(), &iWidth, &iHeight, &iChannels, 0);
// glGenTextures(1, &texture);
// //glActiveTexture(GL_TEXTURE0);
// glBindTexture(GL_TEXTURE_2D, texture);
// // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, iWidth, iHeight);
// // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, iWidth, iHeight, GL_RGB, GL_UNSIGNED_BYTE, iData);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// if (iData)
// {
// glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
// glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iWidth, iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, iData);
// }else
// {
// std::cout << "Failed to load texture" << std::endl;
// }
// glGenerateMipmap(GL_TEXTURE_2D);
// stbi_image_free(iData);
// //glBindTexture(GL_TEXTURE_2D, 0);
// string namea = "assets/metal.png";
// int Width, Height, Channels;
// unsigned char *Data = stbi_load(namea.c_str(), &Width, &Height, &Channels, 0);
// glGenTextures(1, &texture2);
// glBindTexture(GL_TEXTURE_2D, texture2);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// if (Data)
// {
// glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
// glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data);
// }else
// {
// std::cout << "Failed to load texture" << std::endl;
// }
// glGenerateMipmap(GL_TEXTURE_2D);
// stbi_image_free(Data);
//glBindTexture(GL_TEXTURE_2D, 0);
cout << "VENDOR: " << (char *)glGetString(GL_VENDOR) << endl;
cout << "VERSION: " << (char *)glGetString(GL_VERSION) << endl;
cout << "RENDERER: " << (char *)glGetString(GL_RENDERER) << endl;
cout << endl << "Loading content..." << endl;
content.LoadGLTF("assets/Model.gltf");
content2.LoadGLTF("assets/Model2.gltf");
pipeline.CreatePipeline();
pipeline.LoadShaders("shaders/vs_model.glsl", "shaders/fs_model.glsl");
modelPosition = glm::vec3(0.0f, 0.0f, 0.0f);
modelRotation = glm::vec3(0.0f, 0.0f, 0.0f);
// A few optimizations.
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// Get the correct size in pixels - E.g. if retina display or monitor scaling
glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
// Calculate proj_matrix for the first time.
aspect = (float)windowWidth / (float)windowHeight;
projMatrix = glm::perspective(glm::radians(fovy), aspect, 0.1f, 1000.0f);
}
void update()
{
// modelRotation.y += 0.05f;
if (keyStatus[GLFW_KEY_LEFT]) modelRotation.y += 0.05f;
if (keyStatus[GLFW_KEY_RIGHT]) modelRotation.y -= 0.05f;
if (keyStatus[GLFW_KEY_UP]) modelRotation.x += 0.05f;
if (keyStatus[GLFW_KEY_DOWN]) modelRotation.x -= 0.05f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_UP]) cameraPosition.y += 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_DOWN]) cameraPosition.y -= 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_RIGHT]) cameraPosition.x += 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_LEFT]) cameraPosition.x -= 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_I]) cameraPosition.z += 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_O]) cameraPosition.z -= 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_UP]) cameraUp.y += 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_DOWN]) cameraUp.y -= 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_RIGHT]) cameraUp.x += 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_LEFT]) cameraUp.x -= 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_I]) cameraUp.z += 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_O]) cameraUp.z -= 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_UP]) cameraFront.y += 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_DOWN]) cameraFront.y -= 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_RIGHT]) cameraFront.x += 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_LEFT]) cameraFront.x -= 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_I]) cameraFront.z += 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_O]) cameraFront.z -= 0.10f;
if (keyStatus[GLFW_KEY_R]) pipeline.ReloadShaders();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void render()
{
glViewport(0, 0, windowWidth, windowHeight);
// Clear colour buffer
glm::vec4 inchyraBlue = glm::vec4(0.345f, 0.404f, 0.408f, 1.0f);
glm::vec4 backgroundColor = inchyraBlue;
glClearBufferfv(GL_COLOR, 0, &backgroundColor[0]);
// Clear deep buffer
static const GLfloat one = 1.0f;
glClearBufferfv(GL_DEPTH, 0, &one);
// Enable blend
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Use our shader programs
glUseProgram(pipeline.pipe.program);
//glUniform1i(glGetUniformLocation(pipeline.pipe.program, "texture1"), 0);
// Setup camera
glm::mat4 viewMatrix = glm::lookAt(cameraPosition, // eye
cameraPosition + cameraFront, // centre
cameraUp); // up
// Do some translations, rotations and scaling
// glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(modelPosition.x+rX, modelPosition.y+rY, modelPosition.z+rZ));
glm::mat4 modelMatrix = glm::translate(glm::mat4(5.0f), glm::vec3(0.0f, 0.4f, 0.0f));
modelMatrix = glm::rotate(modelMatrix, modelRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
modelMatrix = glm::rotate(modelMatrix, modelRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
modelMatrix = glm::scale(modelMatrix, glm::vec3(0.5f, 0.5f, 0.5f));
glm::mat4 modelMatrix2 = glm::translate(glm::mat4(5.0f), glm::vec3(0.0f, -0.0f, 0.0f));
modelMatrix2 = glm::rotate(modelMatrix2, modelRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
modelMatrix2 = glm::rotate(modelMatrix2, modelRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
modelMatrix2 = glm::scale(modelMatrix2, glm::vec3(0.5f, 0.5f, 0.5f));
glm::mat4 mv_matrix = viewMatrix * modelMatrix;
//glm::mat4 mv_matrixlegs = viewMatrix * modelMatrixlegs;
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "model_matrix"), 1, GL_FALSE, &modelMatrix[0][0]);
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "modelMatrix2"), 1, GL_FALSE, &modelMatrix2[0][0]);
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "view_matrix"), 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "proj_matrix"), 1, GL_FALSE, &projMatrix[0][0]);
// glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "modelMatrixlegs"), 1, GL_FALSE, &modelMatrixlegs[0][0]);
// glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "view_matrix"), 1, GL_FALSE, &viewMatrix[0][0]);
// glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "proj_matrix"), 1, GL_FALSE, &projMatrix[0][0]);
content.DrawModel(content.vaoAndEbos, content.model);
content2.DrawModel(content2.vaoAndEbos, content2.model);
#if defined(__APPLE__)
glCheckError();
#endif
}
void ui()
{
ImGuiIO &io = ImGui::GetIO();
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration;
window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
window_flags |= ImGuiWindowFlags_NoSavedSettings;
window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
window_flags |= ImGuiWindowFlags_NoNav;
const auto PAD = 10.0f;
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
ImVec2 work_size = viewport->WorkSize;
ImVec2 window_pos, window_pos_pivot;
window_pos.x = work_pos.x + work_size.x - PAD;
window_pos.y = work_pos.y + work_size.y - PAD;
window_pos_pivot.x = 1.0f;
window_pos_pivot.y = 1.0f;
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
window_flags |= ImGuiWindowFlags_NoMove;
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
bool *p_open = NULL;
if (ImGui::Begin("Info", nullptr, window_flags)) {
ImGui::Text("About: 3D Graphics and Animation 2022"); // ImGui::Separator();
ImGui::Text("Performance: %.3fms/Frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Text("Pipeline: %s", pipeline.pipe.error?"ERROR":"OK");
}
ImGui::End();
// Rendering imgui
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void onResizeCallback(GLFWwindow *window, int w, int h)
{
windowWidth = w;
windowHeight = h;
// Get the correct size in pixels
glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
if (windowWidth > 0 && windowHeight > 0)
{ // Avoid issues when minimising window - it gives size of 0 which fails division.
aspect = (float)w / (float)h;
projMatrix = glm::perspective(glm::radians(fovy), aspect, 0.1f, 1000.0f);
}
}
void onKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS)
keyStatus[key] = true;
else if (action == GLFW_RELEASE)
keyStatus[key] = false;
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
void onMouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
{
}
void onMouseMoveCallback(GLFWwindow *window, double x, double y)
{
int mouseX = static_cast<int>(x);
int mouseY = static_cast<int>(y);
}
void onMouseWheelCallback(GLFWwindow *window, double xoffset, double yoffset)
{
int yoffsetInt = static_cast<int>(yoffset);
}
void APIENTRY openGLDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const GLvoid *userParam) // Debugger callback for Win64 - OpenGL versions 4.2 or better.
{
debugger.OpenGLDebugCallback(source, type, id, severity, length, message, userParam);
}
void debugGL() // Debugger function for Win64 - OpenGL versions 4.2 or better.
{
// Enable Opengl Debug
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback((GLDEBUGPROC)openGLDebugCallback, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
}
GLenum glCheckError_(const char *file, int line) // Debugger manual function for Apple - OpenGL versions 3.3 to 4.1.
{
GLenum errorCode;
while ((errorCode = glGetError()) != GL_NO_ERROR) debugger.GlGetError(errorCode, file, line);
return errorCode;
}
FragmentShader.glsl
#version 430 core
out vec4 color;
in VS_OUT
{
vec3 vertex;
vec3 normals;
vec2 tc;
} fs_in;
uniform sampler2D tex;
uniform sampler2D model_matrix;
uniform sampler2D modelMatrix2;
void main(void){
color = texture(tex, fs_in.tc);
}
VertexShader.glsl
#version 410 core
layout (location = 0) in vec3 in_vertex;
layout (location = 1) in vec3 in_normals;
layout (location = 2) in vec2 in_tc;
out VS_OUT
{
vec3 vertex;
vec3 normals;
vec2 tc;
} vs_out;
uniform mat4 model_matrix;
uniform mat4 modelMatrixlegs;
uniform mat4 view_matrix;
uniform mat4 proj_matrix;
void main(void)
{
gl_Position = proj_matrix * view_matrix * model_matrix * vec4(in_vertex, 1.0);
vs_out.vertex = in_vertex;
vs_out.normals = in_normals;
vs_out.tc = in_tc;
}
Models don't have positions or rotations or scaling values.
When you call glDrawArrays (or glDrawElements or glDrawElementsIndirect or whatever), then OpenGL renders the current model with the current texture and the current position and rotation and scaling value. Until that point they are separate. You have to realize this - you can draw the same model with different position/rotation/scaling, or the same model with different textures, or different models with the same texture, or so on. There's no need to link them together until you actually draw them and you shouldn't think they are linked together.
I don't see where DrawModel is defined, but I assume it calls glDrawArrays or glDrawElements. So it uses the texture and position and rotation and scaling value (a.k.a. the matrix) that are current when you call it.
So all you have to do is make model 1's matrix current before drawing model 1, and make model 2's matrix current before drawing model 2. Same for the textures.
i.e. probably something like this
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "model_matrix"),
1, GL_FALSE, &modelMatrix[0][0]);
// ^^^^^^^^^^^ model 1 matrix
glBindTexture(GL_TEXTURE_2D, texture);
// ^^^^^^^ model 1 texture
content.DrawModel(content.vaoAndEbos, content.model);
//^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ model 1
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "model_matrix"),
1, GL_FALSE, &modelMatrix2[0][0]);
// ^^^^^^^^^^^^ model 2 matrix
glBindTexture(GL_TEXTURE_2D, texture2);
// ^^^^^^^^ model 2 texture
content2.DrawModel(content2.vaoAndEbos, content2.model);
//^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^ model 2
You can delete the modelMatrixlegs variable from the shader because it's not actually doing anything.
I've loaded a .obj cube in my scene and I want to draw a sphere on every vertices (vertices + origin).
##
## Three-D Library generated .obj file
## cube
##
mtllib cube.mtl
usemtl red
# 0 materials
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
# 8 vertices
In my draw() function:
if(spheres){
i=0, j=0;
while (1){
printf("%d : %f %f %f\n", j, model->vertices[i], model->vertices[i+1], model->vertices[i+2]);
glTranslatef(model->vertices[i], model->vertices[i+1], model->vertices[i+2]);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.4, 5, 5);
if(j >= model->numvertices)
break;
i=i+3;
j++;
}
}
where 'model' is
GLMmodel* model; /* glm model data structure */
http://www.teina.org/TER_3DModeler/glm_8h-source.html
BUT when I draw them, they are not in correct positions.
In the picture above I draw them "manually":
glTranslatef(-1.000000, -1.000000, 1.000000);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.3, 5, 5);
glTranslatef(-1.000000, 1.000000, 1.000000);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.3, 5, 5);
with no success.
All code:
/*
gc_smooth.c
Nate Robins, 1998
Giulio Casciola, 2008
Model viewer program. Exercises the glm library.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <GL/glut.h>
#include "gltb.h"
#include "glm.h"
#include "dirent32.h"
#pragma comment( linker, "/entry:\"mainCRTStartup\"" ) // set the entry point to be main()
#define DATA_DIR "data/"
#define NUM_FRAMES 5
char* model_file = NULL; /* name of the obect file */
GLuint model_list = 0; /* display list for object */
GLMmodel* model; /* glm model data structure */
GLfloat scale; /* original scale factor */
GLfloat smoothing_angle = 90.0; /* smoothing angle */
GLfloat weld_distance = 0.00001; /* epsilon for welding vertices */
GLboolean facet_normal = GL_FALSE; /* draw with facet normal? */
GLboolean bounding_box = GL_FALSE; /* bounding box on? */
GLboolean spheres = GL_FALSE;
GLboolean performance = GL_FALSE; /* performance counter on? */
GLboolean stats = GL_FALSE; /* statistics on? */
GLuint material_mode = 0; /* 0=none, 1=color, 2=material, 3=texture */
GLint entries = 0; /* entries in model menu */
GLdouble pan_x = 0.0;
GLdouble pan_y = 0.0;
GLdouble pan_z = 0.0;
char texnames[1][64] = {"foto_rgb.ppm"};
//char texnames[1][64] = {"grid.ppm"};
GLint w,h;
GLubyte* texture;
#define CLK_TCK 1000
#if defined(_WIN32)
#include <sys/timeb.h>
#else
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/times.h>
#endif
float elapsed(void){
static long begin = 0;
static long finish, difference;
#if defined(_WIN32)
static struct timeb tb;
ftime(&tb);
finish = tb.time*1000+tb.millitm;
#else
static struct tms tb;
finish = times(&tb);
#endif
difference = finish - begin;
begin = finish;
return (float)difference/(float)CLK_TCK;
}
void shadowtext(int x, int y, char* s) {
int lines;
char* p;
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, glutGet(GLUT_WINDOW_WIDTH),
0, glutGet(GLUT_WINDOW_HEIGHT), -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glColor3ub(0, 0, 0);
glRasterPos2i(x+1, y-1);
for(p = s, lines = 0; *p; p++) {
if (*p == '\n') {
lines++;
glRasterPos2i(x+1, y-1-(lines*18));
}
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *p);
}
glColor3ub(0, 128, 255);
glRasterPos2i(x, y);
for(p = s, lines = 0; *p; p++) {
if (*p == '\n') {
lines++;
glRasterPos2i(x, y-(lines*18));
}
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *p);
}
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
void lists(void){
GLfloat ambient[] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat diffuse[] = { 0.8, 0.8, 0.8, 1.0 };
GLfloat specular[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat shininess = 65.0;
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glMaterialf(GL_FRONT, GL_SHININESS, shininess);
if (model_list)
glDeleteLists(model_list, 1);
glDisable(GL_TEXTURE_2D);
/* generate a list */
switch (material_mode)
{
case 0:
if (facet_normal)
model_list = glmList(model, GLM_FLAT);
else
model_list = glmList(model, GLM_SMOOTH);
break;
case 1:
if (facet_normal)
model_list = glmList(model, GLM_FLAT | GLM_COLOR);
else
model_list = glmList(model, GLM_SMOOTH | GLM_COLOR);
break;
case 2:
if (facet_normal)
model_list = glmList(model, GLM_FLAT | GLM_MATERIAL);
else
model_list = glmList(model, GLM_SMOOTH | GLM_MATERIAL);
break;
case 3:
glEnable(GL_TEXTURE_2D);
model_list = glmList(model, GLM_TEXTURE);
// glDisable(GL_TEXTURE_2D);
break;
}
}
void init(void){
gltbInit(GLUT_LEFT_BUTTON);
/* read in the model */
model = glmReadOBJ(model_file);
scale = glmUnitize(model);
glmFacetNormals(model);
glmVertexNormals(model, smoothing_angle);
if (model->nummaterials > 0)
material_mode = 2;
/* create new display lists */
lists();
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
}
void reshape(int width, int height){
gltbReshape(width, height);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (GLfloat)height / (GLfloat)width, 1.0, 128.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -3.0);
}
void display(void){
static char s[256], t[32];
static char* p;
static int frames = 0;
int i=0,j=0;
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(pan_x, pan_y, 0.0);
gltbMatrix();
#if 0 /* glmDraw() performance test */
swith(material_mode)
{
case 0:
if (facet_normal)
glmDraw(model, GLM_FLAT);
else
glmDraw(model, GLM_SMOOTH);
break;
case 1:
if (facet_normal)
glmDraw(model, GLM_FLAT | GLM_COLOR);
else
glmDraw(model, GLM_SMOOTH | GLM_COLOR);
break;
case 2:
if (facet_normal)
glmDraw(model, GLM_FLAT | GLM_MATERIAL);
else
glmDraw(model, GLM_SMOOTH | GLM_MATERIAL);
break;
case 3:
glEnable(GL_TEXTURE_2D);
glmDraw(model, GLM_TEXTURE );
// glDisable(GL_TEXTURE_2D);
break;
}
#else
glCallList(model_list);
#endif
glDisable(GL_LIGHTING);
if (bounding_box) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_CULL_FACE);
glColor4f(1.0, 0.0, 0.0, 0.25);
glutSolidCube(2.0);
glDisable(GL_BLEND);
}
if(spheres){
//i=0, j=0;
//while (1){
printf("%d : %f %f %f\n", j, model->vertices[i], model->vertices[i+1], model->vertices[i+2]);
glTranslatef(-1.000000, -1.000000, 1.000000);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.3, 5, 5);
glTranslatef(-1.000000, 1.000000, 1.000000);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.3, 5, 5);
//if(j >= model->numvertices)
// break;
//i=i+3;
//j++;
//}
}
glPopMatrix();
if (stats) {
/* XXX - this could be done a _whole lot_ faster... */
int height = glutGet(GLUT_WINDOW_HEIGHT);
glColor3ub(0, 0, 0);
sprintf(s, "%s\n%d vertices\n%d triangles\n%d normals\n"
"%d texcoords\n%d groups\n%d materials",
model->pathname, model->numvertices, model->numtriangles,
model->numnormals, model->numtexcoords, model->numgroups,
model->nummaterials);
shadowtext(5, height-(5+18*1), s);
}
/* spit out frame rate. */
frames++;
if (frames > NUM_FRAMES) {
sprintf(t, "%g fps", frames/elapsed());
frames = 0;
}
if (performance) {
shadowtext(5, 5, t);
}
glutSwapBuffers();
glEnable(GL_LIGHTING);
}
void keyboard(unsigned char key, int x, int y){
GLint params[2];
switch (key) {
case 'h':
printf("help\n\n");
printf("w - Toggle wireframe/filled\n");
printf("c - Toggle culling\n");
printf("n - Toggle facet/smooth normal\n");
printf("b - Toggle bounding box\n");
printf("r - Reverse polygon winding\n");
printf("m - Toggle color/material/none/texture mode\n");
printf("p - Toggle performance indicator\n");
printf("s/S - Scale model smaller/larger\n");
printf("t - Show model stats\n");
printf("o - Weld vertices in model\n");
printf("+/- - Increase/decrease smoothing angle\n");
printf("W - Write model to file (out.obj)\n");
printf("q/escape - Quit\n\n");
break;
case 't':
stats = !stats;
break;
case 'p':
performance = !performance;
break;
case 'm':
material_mode++;
if (material_mode > 3)
material_mode = 0;
printf("material_mode = %d\n", material_mode);
lists();
break;
case 'd':
glmDelete(model);
init();
lists();
break;
case 'w':
glGetIntegerv(GL_POLYGON_MODE, params);
if (params[0] == GL_FILL)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 'c':
if (glIsEnabled(GL_CULL_FACE))
glDisable(GL_CULL_FACE);
else
glEnable(GL_CULL_FACE);
break;
case 'b':
bounding_box = !bounding_box;
break;
case 'n':
facet_normal = !facet_normal;
lists();
break;
case 'r':
glmReverseWinding(model);
lists();
break;
case 's':
glmScale(model, 0.8);
lists();
break;
case 'S':
glmScale(model, 1.25);
lists();
break;
case 'o':
//printf("Welded %d\n", glmWeld(model, weld_distance));
glmVertexNormals(model, smoothing_angle);
lists();
break;
case 'O':
weld_distance += 0.01;
printf("Weld distance: %.2f\n", weld_distance);
glmWeld(model, weld_distance);
glmFacetNormals(model);
glmVertexNormals(model, smoothing_angle);
lists();
break;
case '-':
smoothing_angle -= 1.0;
printf("Smoothing angle: %.1f\n", smoothing_angle);
glmVertexNormals(model, smoothing_angle);
lists();
break;
case '+':
smoothing_angle += 1.0;
printf("Smoothing angle: %.1f\n", smoothing_angle);
glmVertexNormals(model, smoothing_angle);
lists();
break;
case 'W':
glmScale(model, 1.0/scale);
glmWriteOBJ(model, "out.obj", GLM_SMOOTH | GLM_MATERIAL);
break;
case 'R':
{
GLuint i;
GLfloat swap;
for (i = 1; i <= model->numvertices; i++) {
swap = model->vertices[3 * i + 1];
model->vertices[3 * i + 1] = model->vertices[3 * i + 2];
model->vertices[3 * i + 2] = -swap;
}
glmFacetNormals(model);
lists();
break;
}
case 'v':
{
spheres = !spheres;
/*printf("Il modello ha %u vertici \n", model->numvertices);
int i=0, j=0;
while (1){
printf("%d : %f %f %f\n", j, model->vertices[i], model->vertices[i+1], model->vertices[i+2]);
if(j >= model->numvertices)
break;
i=i+3;
j++;
}*/
break;
}
case 27:
exit(0);
break;
}
glutPostRedisplay();
}
void menu(int item){
int i = 0;
DIR* dirp;
char* name;
struct dirent* direntp;
if (item > 0) {
keyboard((unsigned char)item, 0, 0);
} else {
dirp = opendir(DATA_DIR);
while ((direntp = readdir(dirp)) != NULL) {
if (strstr(direntp->d_name, ".obj")) {
i++;
if (i == -item)
break;
}
}
if (!direntp)
return;
name = (char*)malloc(strlen(direntp->d_name) + strlen(DATA_DIR) + 1);
strcpy(name, DATA_DIR);
strcat(name, direntp->d_name);
model = glmReadOBJ(name);
scale = glmUnitize(model);
glmFacetNormals(model);
glmVertexNormals(model, smoothing_angle);
if (model->nummaterials > 0)
material_mode = 2;
else
material_mode = 0;
lists();
free(name);
glutPostRedisplay();
}
}
static GLint mouse_state;
static GLint mouse_button;
void mouse(int button, int state, int x, int y){
GLdouble model[4*4];
GLdouble proj[4*4];
GLint view[4];
/* fix for two-button mice -- left mouse + shift = middle mouse */
if (button == GLUT_LEFT_BUTTON && glutGetModifiers() & GLUT_ACTIVE_SHIFT)
button = GLUT_MIDDLE_BUTTON;
gltbMouse(button, state, x, y);
mouse_state = state;
mouse_button = button;
if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON) {
glGetDoublev(GL_MODELVIEW_MATRIX, model);
glGetDoublev(GL_PROJECTION_MATRIX, proj);
glGetIntegerv(GL_VIEWPORT, view);
gluProject((GLdouble)x, (GLdouble)y, 0.0,
model, proj, view,
&pan_x, &pan_y, &pan_z);
gluUnProject((GLdouble)x, (GLdouble)y, pan_z,
model, proj, view,
&pan_x, &pan_y, &pan_z);
pan_y = -pan_y;
}
glutPostRedisplay();
}
void motion(int x, int y){
GLdouble model[4*4];
GLdouble proj[4*4];
GLint view[4];
gltbMotion(x, y);
if (mouse_state == GLUT_DOWN && mouse_button == GLUT_MIDDLE_BUTTON) {
glGetDoublev(GL_MODELVIEW_MATRIX, model);
glGetDoublev(GL_PROJECTION_MATRIX, proj);
glGetIntegerv(GL_VIEWPORT, view);
gluProject((GLdouble)x, (GLdouble)y, 0.0,
model, proj, view,
&pan_x, &pan_y, &pan_z);
gluUnProject((GLdouble)x, (GLdouble)y, pan_z,
model, proj, view,
&pan_x, &pan_y, &pan_z);
pan_y = -pan_y;
}
glutPostRedisplay();
}
/* ppmRead: read a PPM raw (type P6) file. The PPM file has a header
that should look something like:
P6
# comment
width height max_value
rgbrgbrgb...
where "P6" is the magic cookie which identifies the file type and
should be the only characters on the first line followed by a
carriage return. Any line starting with a # mark will be treated
as a comment and discarded. After the magic cookie, three integer
values are expected: width, height of the image and the maximum
value for a pixel (max_value must be < 256 for PPM raw files). The
data section consists of width*height rgb triplets (one byte each)
in binary format (i.e., such as that written with fwrite() or
equivalent).
The rgb data is returned as an array of unsigned chars (packed
rgb). The malloc()'d memory should be free()'d by the caller. If
an error occurs, an error message is sent to stderr and NULL is
returned.
*/
unsigned char*
ppmRead(char* filename, int* width, int* height)
{
FILE* fp;
int i, w, h, d;
unsigned char* image;
char head[70]; /* max line <= 70 in PPM (per spec). */
fp = fopen(filename, "rb");
if (!fp) {
perror(filename);
return NULL;
}
/* grab first two chars of the file and make sure that it has the
correct magic cookie for a raw PPM file. */
fgets(head, 70, fp);
if (strncmp(head, "P6", 2)) {
fprintf(stderr, "%s: Not a raw PPM file\n", filename);
return NULL;
}
/* grab the three elements in the header (width, height, maxval). */
i = 0;
while(i < 3) {
fgets(head, 70, fp);
if (head[0] == '#') /* skip comments. */
continue;
if (i == 0)
i += sscanf(head, "%d %d %d", &w, &h, &d);
else if (i == 1)
i += sscanf(head, "%d %d", &h, &d);
else if (i == 2)
i += sscanf(head, "%d", &d);
}
/* grab all the image data in one fell swoop. */
image = (unsigned char*)malloc(sizeof(unsigned char)*w*h*3);
fread(image, sizeof(unsigned char), w*h*3, fp);
fclose(fp);
*width = w;
*height = h;
return image;
}
textures(void)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/* XXX - RE bug - must enable texture before bind. */
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 1);
texture = (GLubyte*)ppmRead(texnames[0], &w, &h);
// glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0,
// GL_RGB, GL_UNSIGNED_BYTE, texture);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w, h,
GL_RGB, GL_UNSIGNED_BYTE, texture);
free(texture);
/* XXX - RE bug - must enable texture before bind. */
glDisable(GL_TEXTURE_2D);
}
int main(int argc, char** argv){
int buffering = GLUT_DOUBLE;
struct dirent* direntp;
DIR* dirp;
int models;
glutInitWindowSize(512, 512);
glutInit(&argc, argv);
while (--argc) {
if (strcmp(argv[argc], "-sb") == 0)
buffering = GLUT_SINGLE;
else
model_file = argv[argc];
}
if (!model_file) {
// model_file = "data/dolphins.obj";
model_file = "data/boeing_2.obj";
}
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | buffering);
glutCreateWindow("Smooth");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMouseFunc(mouse);
glutMotionFunc(motion);
/* Image data packed tightly. */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
textures();
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
// glEnable(GL_TEXTURE_2D);
models = glutCreateMenu(menu);
dirp = opendir(DATA_DIR);
if (!dirp) {
fprintf(stderr, "%s: can't open data directory.\n", argv[0]);
} else {
while ((direntp = readdir(dirp)) != NULL) {
if (strstr(direntp->d_name, ".obj")) {
entries++;
glutAddMenuEntry(direntp->d_name, -entries);
}
}
closedir(dirp);
}
glutCreateMenu(menu);
glutAddMenuEntry("Smooth", 0);
glutAddMenuEntry("", 0);
glutAddSubMenu("Models", models);
glutAddMenuEntry("", 0);
glutAddMenuEntry("[w] Toggle wireframe/filled", 'w');
glutAddMenuEntry("[c] Toggle culling on/off", 'c');
glutAddMenuEntry("[n] Toggle face/smooth normals", 'n');
glutAddMenuEntry("[b] Toggle bounding box on/off", 'b');
glutAddMenuEntry("[p] Toggle frame rate on/off", 'p');
glutAddMenuEntry("[t] Toggle model statistics", 't');
glutAddMenuEntry("[m] Toggle color/material/none/texture mode", 'm');
glutAddMenuEntry("[r] Reverse polygon winding", 'r');
glutAddMenuEntry("[s] Scale model smaller", 's');
glutAddMenuEntry("[S] Scale model larger", 'S');
glutAddMenuEntry("[o] Weld redundant vertices", 'o');
glutAddMenuEntry("[+] Increase smoothing angle", '+');
glutAddMenuEntry("[-] Decrease smoothing angle", '-');
glutAddMenuEntry("[W] Write model to file (out.obj)", 'W');
glutAddMenuEntry("", 0);
glutAddMenuEntry("[Esc] Quit", 27);
glutAttachMenu(GLUT_RIGHT_BUTTON);
init();
glutMainLoop();
return 0;
}
glTranslatef() multiples the translation transform by the current matrix. Your code seems to be assuming that it overwrites the the current matrix.
Push, translate, draw, pop:
if(spheres){
i=0, j=0;
while (1){
printf("%d : %f %f %f\n", j, model->vertices[i], model->vertices[i+1], model->vertices[i+2]);
glPushMatrix();
glTranslatef(model->vertices[i], model->vertices[i+1], model->vertices[i+2]);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.4, 5, 5);
glPopMatrix();
if(j >= model->numvertices)
break;
i=i+3;
j++;
}
}
I am trying to implement soft particles in my projects.
Everything is fine , I implement the texture also. But when the mouse is moved to a certain angle,
the particles get distorted. The particle is generated in view space.
So, I would like to know how could I implement the billboard in my project so that every particles seem uniform.Here is my code:
bool CETSmokeRenderer::InitBuffers()
{
size_t vertexSize = 3 * 4 * m_NumVertex * sizeof(float);
size_t colorSize = 4 * 4 * m_NumVertex * sizeof(float);
size_t texCoordSize = 2 * 4 * m_NumVertex * sizeof(float);
if(!vertexBuffer)
{
glDeleteBuffersARB(1, &vertexBuffer);
glDeleteBuffersARB(1, &colorBuffer);
glDeleteBuffersARB(1, &texCoordBuffer);
}
glGenBuffersARB(1, &vertexBuffer);
glGenBuffersARB(1, &colorBuffer);
glGenBuffersARB(1, &texCoordBuffer);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexSize, NULL, GL_STREAM_DRAW_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, colorSize, NULL, GL_STREAM_DRAW_ARB);
// Creates the static texture data
size_t len = 2 * 4 * m_NumVertex;
if(0 > m_NumVertex)
{
return false;
}
else if(0 == m_NumVertex)
{
return true;
}
float *texCoords = new float[len];
{
size_t i = 0;
while(i < len)
{
// u v
texCoords[i++] = 0.0f; texCoords[i++] = 0.0f;
texCoords[i++] = 1.0f; texCoords[i++] = 0.0f;
texCoords[i++] = 1.0f; texCoords[i++] = 1.0f;
texCoords[i++] = 0.0f; texCoords[i++] = 1.0f;
}
}
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, texCoordSize, (void*)texCoords, GL_STATIC_DRAW_ARB);
delete texCoords;
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
return 0;
}
void CETSmokeRenderer::Draw(Camera &cam, bool useTex)
{
if(useTex)
glBindTexture(GL_TEXTURE_2D, texID);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
mBaseView->SetupViewingTransform();
size_t len = particleStore.size();
std::vector<SimpleSmokeParticle> toDraw;
for(size_t i = 0; i < len; i++)
{
SimpleSmokeParticle sp;
sp.transP = particleStore[i].p;
sp.index = i;
toDraw.push_back(sp);
}
//std::sort(toDraw.begin(), toDraw.end(), ParticleCmp);
#ifdef USE_VBO
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, 3 * 4 * m_NumVertex * sizeof(float), NULL, GL_STREAM_DRAW_ARB);
float *vertexPtr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
assert(vertexPtr);
for(size_t i = 0, count = 0; count < len; count++)
{
SmokeParticle &prt = particleStore[ toDraw[count].index ];
Point3f &p = toDraw[count].transP;
float w = prt.w / 0.5f;
float h = prt.h / 1.0f;
vertexPtr[i++] = p.x - w; vertexPtr[i++] = p.y - h; vertexPtr[i++] = p.z;
vertexPtr[i++] = p.x + w; vertexPtr[i++] = p.y - h; vertexPtr[i++] = p.z;
vertexPtr[i++] = p.x + w; vertexPtr[i++] = p.y + h; vertexPtr[i++] = p.z;
vertexPtr[i++] = p.x - w; vertexPtr[i++] = p.y + h; vertexPtr[i++] = p.z;
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, 4 * 4 * m_NumVertex * sizeof(float), NULL, GL_STREAM_DRAW_ARB);
float *colorPtr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
assert(colorBuffer);
for(size_t i = 0, count = 0; count < len; count++)
{
SmokeParticle &prt = particleStore[ toDraw[count].index ];
// r g b a
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
// Draws buffered data
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBuffer);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBuffer);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_QUADS, 0, (GLsizei)len *4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
#else
{..}
glPopMatrix();
}
void CETSmokeRenderer::Render()
{
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Renders depth information
if(useSoftParticles)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE);
glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE);
glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_FALSE);
glClearColor(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX);
glClear(GL_COLOR_BUFFER_BIT);;
glUseProgramObjectARB(0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_TRUE);
glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_TRUE);
glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_TRUE);
glBindTexture(GL_TEXTURE_2D, 0);
// renders the soft particles
glUseProgramObjectARB(particleShader);
// Sets texture data
GLint texloc = glGetUniformLocationARB(particleShader, "tex");
GLint depthTexloc = glGetUniformLocationARB(particleShader, "depthInfo");
GLint powerloc = glGetUniformLocationARB(particleShader, "power");
glUniform1fARB(powerloc, (float)softParticlePower);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID());
glUniform1iARB(texloc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthTex);
glUniform1iARB(depthTexloc, 1);
Draw(m_pCamera, false);
// Unbinds shader and textures
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgramObjectARB(0);
}
else
{
glUseProgramObjectARB(particleShader);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthTex);
Draw(m_pCamera, true);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgramObjectARB(0);
}
}
One way of achieving what you are looking for is to utilise point sprites, I have attached some code below that illustrates this in a simple way, hope this helps:
main.cpp
/*
Simple point-sprite particle demo - renders particles using spheres
Requirements:
GLM maths library
Freeglut
*/
#include <gl/glew.h>
#include <gl/freeglut.h>
#include <iostream>
#include "GLSLShader.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_projection.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <cassert>
#define GL_CHECK_ERRORS assert(glGetError()== GL_NO_ERROR)
using namespace std;
class Screen
{
public:
int width, height;
string title;
unsigned int displayFlags, contextFlags;
Screen(string ititle, int iwidth = 1024, int iheight = 768){
Screen(ititle, (GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA), (GLUT_CORE_PROFILE | GLUT_DEBUG), iwidth, iheight)
}
Screen(string ititle, unsigned int disFlags, unsigned int contFlags, int iwidth = 1024, int iheight = 768){
title = ititle; width = iwidth; height = iheight;
displayFlags = disFlags;
contextFlags = contFlags;
}
};
const int TOTAL= 9;
GLfloat positions[3*TOTAL]={-1,0,-1, 0,0,-1, 1,0,-1,-1,0, 0, 0,0, 0, 1,0, 0,-1,0, 1, 0,0, 1, 1,0,1};
GLuint vboID, vaoID;
GLsizei stride = sizeof(GLfloat)*3;
GLSLShader shader;
int filling=1;
// Absolute rotation values (0-359 degrees) and rotiation increments for each frame
float rotation_x=0, rotation_x_increment=0.1f;
float rotation_y=0, rotation_y_increment=0.05f;
float rotation_z=0, rotation_z_increment=0.03f;
glm::mat4 P; //projection matrix;
bool bRotate=true;
void InitShaders(void)
{
shader.LoadFromFile(GL_VERTEX_SHADER, "shader.vert");
shader.LoadFromFile(GL_FRAGMENT_SHADER, "shader.frag");
shader.CreateAndLinkProgram();
shader.Use();
shader.AddAttribute("vVertex");
shader.AddUniform("Color");
shader.AddUniform("lightDir");
shader.AddUniform("MVP");
glUniform3f(shader("lightDir"), 0,0,1);
glUniform3f(shader("Color"),1,0,0);
shader.UnUse();
GL_CHECK_ERRORS;
}
void InitVAO() {
GL_CHECK_ERRORS;
//Create vao and vbo stuff
glGenVertexArrays(1, &vaoID);
glGenBuffers (1, &vboID);
GL_CHECK_ERRORS;
glBindVertexArray(vaoID);
glBindBuffer (GL_ARRAY_BUFFER, vboID);
glBufferData (GL_ARRAY_BUFFER, sizeof(positions), &positions[0], GL_STATIC_DRAW);
GL_CHECK_ERRORS;
glEnableVertexAttribArray(shader["vVertex"]);
glVertexAttribPointer (shader["vVertex"], 3, GL_FLOAT, GL_FALSE,stride,0);
glBindVertexArray(0);
GL_CHECK_ERRORS;
}
void SetupGLBase() {
glGetError();
GL_CHECK_ERRORS;
glClearColor(0.0f,0.0f,0.2f,0.0f);
GL_CHECK_ERRORS;
InitShaders();
InitVAO();
glEnable(GL_DEPTH_TEST); // We enable the depth test (also called z buffer)
GL_CHECK_ERRORS;
glPointSize(50);
}
void OnRender() {
GL_CHECK_ERRORS;
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
//setup matrices
glm::mat4 T = glm::translate(glm::mat4(1.0f),glm::vec3(0.0f, 0.0f, -5));
glm::mat4 Rx = glm::rotate(T, rotation_x, glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 Ry = glm::rotate(Rx, rotation_y, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 MV = glm::rotate(Ry, rotation_z, glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 MVP = P*MV;
//draw the points
shader.Use();
glUniformMatrix4fv(shader("MVP"), 1, GL_FALSE, glm::value_ptr(MVP));
glBindVertexArray(vaoID);
glDrawArrays(GL_POINTS, 0, TOTAL);
glBindVertexArray(0);
shader.UnUse();
glutSwapBuffers();
}
void OnResize(int w, int h)
{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
//setup the projection matrix
P = glm::perspective(45.0f, (GLfloat)w/h, 1.f, 1000.f);
}
void OnShutdown() {
glDeleteBuffers(1, &vboID);
glDeleteVertexArrays(1, &vaoID);
}
void OnKey(unsigned char key, int x, int y)
{
switch (key)
{
case ' ': bRotate=!bRotate; break;
case 'r': case 'R':
if (filling==0)
{
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Filled Polygon Mode
filling=1;
}
else
{
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); // Outline Polygon Mode
filling=0;
}
break;
}
}
void OnSpecialKey(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_UP: rotation_x_increment = rotation_x_increment +0.005f; break;
case GLUT_KEY_DOWN: rotation_x_increment = rotation_x_increment -0.005f; break;
case GLUT_KEY_LEFT: rotation_y_increment = rotation_y_increment +0.005f; break;
case GLUT_KEY_RIGHT: rotation_y_increment = rotation_y_increment -0.005f; break;
}
}
void OnIdle() {
if(bRotate) {
rotation_x = rotation_x + rotation_x_increment;
rotation_y = rotation_y + rotation_y_increment;
rotation_z = rotation_z + rotation_z_increment;
}
if (rotation_x > 359) rotation_x = 0;
if (rotation_y > 359) rotation_y = 0;
if (rotation_z > 359) rotation_z = 0;
glutPostRedisplay();
}
void glTestAndInfo(GLEnum glewInitResponse)
{
if (GLEW_OK != glewInitResponse) {
cerr<<"Error: "<<glewGetErrorString(glewInitResponse)<<endl;
} else {
if (GLEW_VERSION_3_3)
{
cout<<"Driver supports OpenGL 3.3 or greater.\nDetails:"<<endl;
}
}
cout<<"Using GLEW "<<glewGetString(GLEW_VERSION)<<endl;
cout<<"Vendor: "<<glGetString (GL_VENDOR)<<endl;
cout<<"Renderer: "<<glGetString (GL_RENDERER)<<endl;
cout<<"Version: "<<glGetString (GL_VERSION)<<endl;
cout<<"GLSL: "<<glGetString (GL_SHADING_LANGUAGE_VERSION)<<endl;
}
void main(int argc, char** argv) {
Screen *screen = news Screen("Point sprites as spheres in OpenGL 3.3");
atexit(OnShutdown);
glutInit(&argc, argv);
glutInitDisplayMode(screen->displayFlags);
glutInitContextVersion (3, 3);
glutInitContextFlags (screen->contextFlags);
glutInitWindowSize(screen->width, screen->height);
glutCreateWindow(screen->title);
glewExperimental = GL_TRUE;
glTestAndInfo(glewInit());
SetupGLBase();
glutDisplayFunc(OnRender);
glutReshapeFunc(OnResize);
glutKeyboardFunc(OnKey);
glutSpecialFunc(OnSpecialKey);
glutIdleFunc(OnIdle);
glutMainLoop();
}
GLSLShader.h
#pragma once
#ifndef GLSL_SHADER_H
#define GLSL_SHADER_H
#include <GL/glew.h>
#include <map>
#include <string>
using namespace std;
class GLSLShader
{
public:
GLSLShader(void);
~GLSLShader(void);
void LoadFromString(GLenum whichShader, const string source);
void LoadFromFile(GLenum whichShader, const string filename);
void CreateAndLinkProgram();
void Use();
void UnUse();
void AddAttribute(const string attribute);
void AddUniform(const string uniform);
GLuint operator[](const string attribute);// indexer: returns the location of the named attribute
GLuint operator()(const string uniform);
private:
enum ShaderType {VERTEX_SHADER, FRAGMENT_SHADER, GEOMETRY_SHADER};
GLuint _program;
int _totalShaders;
GLuint _shaders[3];//0 vertexshader, 1 fragmentshader, 2 geometryshader
map<string,GLuint> _attributeList;
map<string,GLuint> _uniformLocationList;
};
#endif
GLSLShader.cpp
/*
Really basic glsl shader class
*/
#include "GLSLShader.h"
#include <iostream>
#include <fstream>
// constructor
GLSLShader::GLSLShader(void)
{
_totalShaders=0;
_shaders[VERTEX_SHADER]=0;
_shaders[FRAGMENT_SHADER]=0;
_shaders[GEOMETRY_SHADER]=0;
_attributeList.clear();
_uniformLocationList.clear();
}
// destructor
GLSLShader::~GLSLShader(void)
{
_attributeList.clear();
_uniformLocationList.clear();
glDeleteProgram(_program);
}
// loader functions
void GLSLShader::LoadFromString(GLenum type, const string source) {
GLuint shader = glCreateShader (type);
const char * ptmp = source.c_str();
glShaderSource (shader, 1, &ptmp, NULL);
//check whether the shader loads fine
GLint status;
glCompileShader (shader);
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLint infoLogLength;
glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *infoLog= new GLchar[infoLogLength];
glGetShaderInfoLog (shader, infoLogLength, NULL, infoLog);
cerr<<"Compile log: "<<infoLog<<endl;
delete [] infoLog;
}
_shaders[_totalShaders++]=shader;
}
void GLSLShader::LoadFromFile(GLenum whichShader, const string filename){
ifstream fp;
fp.open(filename.c_str(), ios_base::in);
if(fp) {
string line, buffer;
while(getline(fp, line)) {
buffer.append(line);
buffer.append("\r\n");
}
//copy to source
LoadFromString(whichShader, buffer);
} else {
cerr<<"Error loading shader: "<<filename<<endl;
}
}
// utilitarian functions
void GLSLShader::CreateAndLinkProgram() {
_program = glCreateProgram ();
if (_shaders[VERTEX_SHADER] != 0) {
glAttachShader (_program, _shaders[VERTEX_SHADER]);
}
if (_shaders[FRAGMENT_SHADER] != 0) {
glAttachShader (_program, _shaders[FRAGMENT_SHADER]);
}
if (_shaders[GEOMETRY_SHADER] != 0) {
glAttachShader (_program, _shaders[GEOMETRY_SHADER]);
}
//link and check whether the program links fine
GLint status;
glLinkProgram (_program);
glGetProgramiv (_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLint infoLogLength;
glGetProgramiv (_program, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *infoLog= new GLchar[infoLogLength];
glGetProgramInfoLog (_program, infoLogLength, NULL, infoLog);
cerr<<"Link log: "<<infoLog<<endl;
delete [] infoLog;
}
glDeleteShader(_shaders[VERTEX_SHADER]);
glDeleteShader(_shaders[FRAGMENT_SHADER]);
glDeleteShader(_shaders[GEOMETRY_SHADER]);
}
void GLSLShader::Use() {
glUseProgram(_program);
}
void GLSLShader::UnUse() {
glUseProgram(0);
}
void GLSLShader::AddAttribute(const string attribute) {
_attributeList[attribute]= glGetAttribLocation(_program, attribute.c_str());
}
// indexer: returns the location of the named attribute
GLuint GLSLShader::operator [](const string attribute) {
return _attributeList[attribute];
}
void GLSLShader::AddUniform(const string uniform) {
_uniformLocationList[uniform] = glGetUniformLocation(_program, uniform.c_str());
}
GLuint GLSLShader::operator()(const string uniform){
return _uniformLocationList[uniform];
}
This code is pretty old and I have no way to test rendering here (no distinct GFX card) so if there are any issues let me know and I can fix it once at my GFX dev machine.
Addendum:
Shaders may help too (dont know how I forgot them, old age maybe catching up on me!) so here they are:
Vertex shader (shader.vert)
#version 330 // set this to whatever minimum version you want to support
in vec3 vVertex;
uniform mat4 MVP;
void main()
{
gl_Position = MVP*vec4(vVertex,1);
}
Fragment shader (shader.frag)
#version 330
out vec4 vFragColour;
uniform vec3 Colour;
uniform vec3 lightDirection;
void main(void)
{
// calculate normal from texture coordinates
vec3 N;
N.xy = gl_PointCoord* 2.0 - vec2(1.0);
float mag = dot(N.xy, N.xy);
if (mag > 1.0) discard; // kill pixels outside the circle we want
N.z = sqrt(1.0-mag); // this might be expensive depending on your hardware
float diffuse = max(0.0, dot(lightDirection, N)); // calculate lighting
vFragColour = vec4(Colour,1) * diffuse;
}
Addendum 2:
To add the freeglut libraries to your build and resolve LNK 1104 errors simply go to *Project >> Properties >> VC++ Directories* and add the directories where your freeglut includes, source libraries and dlls are stored, for example for lib files go to
Add the folders as follows:
DLL Directories: add to Executable Directories
.h file Directories(include folder): add to Include Directories
.cpp file Directories: add to Source Directories
.lib file Directories: add to Library Directories
Hope this helps:)
I want to fill the screen with a grid of points. My desired performance would be about the same speed as drawing that many pixels as a contiguous quad (or equivalent triangle clipped with glViewport). Using GL_POINT primitives (positioned via gl_VertexID, not attribs) or glPolygonStipple are possibilities, but are still a little slower. Here's an example of what I want (though the black points drawn may be yet more sparse):
Are there any other methods to draw this grid? (in a similar time to a smaller quad of the same number of pixels)
Wouldn't it be great if the rasterizer was programmable!
The main point of this is to be able to write to both stencil and colour buffers in this grid pattern from a fragment shader.
EDIT
Some rendering times:
Full screen for me is 1680x1050, GTX670. Times are calculated drawing 10,000 times each frame, no depth test. I draw a quad with a big triangle and clip using glViewport.
Rendering a full screen quad and calling discard for coord%4>0: 0.112ms
Rendering a full screen quad, assigning const colour: 0.059ms
Rendering with glPolygonStipple creating %4 pattern: 0.009ms
Rendering quarter full screen quad: 0.003ms
Rendering a 1x1 quad: 0.002ms (binding VBO and shader, could prob be optimized)
The differences get larger with a more sparse grid, for example %16.
EDIT
OK, I've thrown together a small example. Requires glut and glew libraries:
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <memory.h>
#include <assert.h>
#include <stdio.h>
#define RESOLUTION_X 1680
#define RESOLUTION_Y 1050
#define USE_32_BIT 0
#define TEST_LOOP 1000 //number of quads to draw per frame
#define WARMUP_MS 1000 //time between switching methods
#define TEST_MS 4000 //time to benchmark for
#define TESTS 6
#define DRAW_GRAPH 1
#define SCALE_MS 0.2f //for drawing the graph
GLuint fbo, colourTex, vbo, shader, shaderPoints, shaderDiscard;
int viewport[2];
int test = 0;
int results_time[TESTS];
int results_frames[TESTS];
float colours[TESTS][3] = {
{1,0,0},
{1,1,0},
{1,0,1},
{0,1,0},
{0,1,1},
{0,0,1},
};
const char* names[TESTS] = {
"full",
"full discard",
"full stipple",
"draw points",
"quarter",
"one"
};
float triangleVerts[9] = {-1,-1,0,-1,4,0,4,-1,0};
const char* vertexShaderSrc = "#version 150\nin vec4 v;\nvoid main() {gl_Position = v;}\n";
const char* vertexShaderPointsSrc = "#version 150\nuniform ivec2 s;\nvoid main() {ivec2 p = ivec2(gl_VertexID%(s.x/4),gl_VertexID/(s.x/4)); gl_Position = vec4(2.0*(p*4+0.5)/s-1.0, 0, 1);}\n";
const char* fragmentShaderSrc = "#version 150\nout vec4 c;\nvoid main() {c = vec4(1,0,0,1);}\n";
const char* fragmentShaderDiscardSrc = "#version 150\nout vec4 c;\nvoid main() {if (int(gl_FragCoord.x)%4>0||int(gl_FragCoord.y)%4>0) discard; c = vec4(1,0,0,1);}\n";
void setupDraw(GLuint program, int x, int y)
{
glUseProgram(program);
glViewport(0, 0, x, y);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLuint loc = glGetAttribLocation(program, "v");
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
void polygonStippleGrid(int x, int y)
{
unsigned char tilePattern[32*32];
memset(tilePattern, 0, sizeof(tilePattern));
for (int j = 0; j < 32; j += y)
{
for (int i = 0; i < 32; i += x)
{
int index = (j * 32 + i);
tilePattern[index / 8] |= 1 << (index % 8);
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPolygonStipple(tilePattern);
}
void display()
{
static int lastTime = -1;
int elapsed = glutGet(GLUT_ELAPSED_TIME);
if (lastTime == -1) lastTime = elapsed;
int dt = elapsed - lastTime;
lastTime = elapsed;
static int warmup = WARMUP_MS + 2000;
static int running = TEST_MS;
warmup -= dt;
if (warmup <= 0 && test < TESTS)
{
running -= dt;
results_time[test] += dt;
results_frames[test] += 1;
if (running <= 0)
{
printf("%s %s %.6fms\n", names[test], USE_32_BIT?"rgba32":"rgba8", results_time[test]/(float)(results_frames[test] * TEST_LOOP));
test += 1;
warmup = WARMUP_MS;
running = TEST_MS;
}
}
#if DRAW_GRAPH
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, viewport[0], viewport[1]);
glClear(GL_COLOR_BUFFER_BIT);
float s = 2.0f / TESTS;
glBegin(GL_QUADS);
for (int i = 0; i < TESTS; ++i)
{
if (!results_frames[i]) continue;
glColor3fv(colours[i]);
float x = -1.0f + 2.0f * i / (float)TESTS;
float y = -1.0f + 2.0f * (results_time[i]/(float)(results_frames[i] * TEST_LOOP)) / SCALE_MS;
glVertex2f(x, -1.0f); glVertex2f(x, y); glVertex2f(x + s, y); glVertex2f(x + s, -1.0f);
}
glEnd();
#endif
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
switch (test)
{
case 0: //straight full screen quad
setupDraw(shader, RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
case 1: //full screen quad, discarding pixels in the frag shader
setupDraw(shaderDiscard, RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
case 2: //using polygon stipple to mask out fragments
polygonStippleGrid(4, 4);
glEnable(GL_POLYGON_STIPPLE);
setupDraw(shader, RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisable(GL_POLYGON_STIPPLE);
break;
case 3: //drawing points, but computing the position in the vertex shader
glUseProgram(shaderPoints);
glUniform2i(glGetUniformLocation(shaderPoints, "s"), RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_POINTS, 0, (RESOLUTION_X/4)*(RESOLUTION_Y/4));
break;
case 4: //a quad one quarter of the screen (as a speed comparison)
setupDraw(shader, RESOLUTION_X / 4, RESOLUTION_Y / 4);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
case 5: //a 1x1 quad (as a speed comparison)
setupDraw(shader,1, 1);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
default: break;
}
glUseProgram(0);
glDisableVertexAttribArray(0); //HACK: assumes location is always zero
//printf("%i %i %i\n", test, warmup, running);
glFinish();
glutSwapBuffers();
glutPostRedisplay();
assert(glGetError() == GL_NO_ERROR);
}
void reshape(int x, int y)
{
viewport[0] = x;
viewport[1] = y;
}
int main(int argc, char **argv)
{
memset(results_time, 0, sizeof(results_time));
memset(results_frames, 0, sizeof(results_frames));
//init glut
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutCreateWindow("quadtest");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glewInit();
//init gl stuff
glGenTextures(1, &colourTex);
glBindTexture(GL_TEXTURE_2D, colourTex);
#if USE_32_BIT
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, RESOLUTION_X, RESOLUTION_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RESOLUTION_X, RESOLUTION_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#endif
/*
GLuint stencilRB;
glGenRenderbuffers(1, &stencilRB);
glBindRenderbuffer(GL_RENDERBUFFER, stencilRB);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, RESOLUTION_X, RESOLUTION_Y);
*/
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colourTex, 0);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilRB);
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVerts), triangleVerts, GL_STATIC_DRAW);
GLuint v = glCreateShader(GL_VERTEX_SHADER);
GLuint vp = glCreateShader(GL_VERTEX_SHADER);
GLuint f = glCreateShader(GL_FRAGMENT_SHADER);
GLuint fd = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(v, 1, &vertexShaderSrc, NULL);
glShaderSource(vp, 1, &vertexShaderPointsSrc, NULL);
glShaderSource(f, 1, &fragmentShaderSrc, NULL);
glShaderSource(fd, 1, &fragmentShaderDiscardSrc, NULL);
GLint ok = GL_TRUE;
shader = glCreateProgram();
glAttachShader(shader, v);
glAttachShader(shader, f);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &ok);
assert(ok == GL_TRUE);
/*
char log[512];
int n;
glGetShaderInfoLog(v, 512, &n, log);
printf("%s\n", log);
glGetProgramInfoLog(shader, 512, &n, log);
printf("%s\n", log);
*/
shaderPoints = glCreateProgram();
glAttachShader(shaderPoints, vp);
glAttachShader(shaderPoints, f);
glLinkProgram(shaderPoints);
glGetProgramiv(shaderPoints, GL_LINK_STATUS, &ok);
assert(ok == GL_TRUE);
shaderDiscard = glCreateProgram();
glAttachShader(shaderDiscard, v);
glAttachShader(shaderDiscard, fd);
glLinkProgram(shaderDiscard);
glGetProgramiv(shaderDiscard, GL_LINK_STATUS, &ok);
assert(ok == GL_TRUE);
glDisable(GL_DEPTH_TEST);
assert(glGetError() == GL_NO_ERROR);
glutMainLoop();
return 0;
}
Interestingly, using GL_RGBA32F 32 bit colour impacts performance a fair bit, also bringing back the overhead of the discard method to approximately the same as a full screen quad. The glPolygonStipple method gives dramatic improvements in this case, more so than with 8 bit. There is a discrepancy with the previous glPolygonStipple result too, I can reproduce both and haven't narrowed down the difference yet.
output for GL_RGBA:
full rgba8 0.059ms
full discard rgba8 0.112ms
full stipple rgba8 0.050ms
draw points rgba8 0.079ms
quarter rgba8 0.004ms
one rgba8 <0.001ms
output for GL_RGBA32F:
full rgba32 0.240ms
full discard rgba32 0.241ms
full stipple rgba32 0.101ms
draw points rgba32 0.091ms
quarter rgba32 0.015ms
one rgba32 <0.001ms
Drawing points and positioning from gl_VertexID will beat glPolygonStipple for GL_RGBA32F. I'd assume this trend would carry on for more expensive shaders (or at least memory-intensive).
Are there any other methods to draw this grid?
Exactly this grid? Well in that case your grid has a periodicity of 4 and an offset of -1 in x and -2 in y direction. So the fragment shader to produce it (discarding the "black" pixels) would be
void main()
{
if( ((gl_FragPosition.x-1) % 4) == 0 && ((gl_FragPosition.y-2) % 4) == 0 )
discard;
gl_FragColor = vec4(1,1,1,1);
}
Setting the stencil op to always replace the stencil value, will set the stencil buffer to your ref value everywhere, where no pixels are discarded.
If you can't express your grid by some kind of formula, well, use a texture instead.
The scattered memory writes of a sparse grid may simply mean more overhead that can't be avoided.
Draw GL_POINTs
Use glPolygonStipple
Initialize the stencil buffer with the pattern for a masking a full screen quad
What ever you do do not use the discard method if the fragment shader is expensive[1]. This is really stupid because you clog the pipeline with many threads which don't do anything.
[1] Either takes a long time to execute or uses lots of registers or local memory
//
// 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)