Related
I've been working on implementing a car in my game using bullet physics. The physics of the car uses btRaycastVehicle and the code is mostly based on the ForkLift Demo.
At this point, the vehicle seems to work properly on a flat ground but then I've started working on a non flat terrain and I saw the class btHeightfieldTerrainShape which take an array of heights to construct a shape.
So I've managed to use this class and the vehicle can be used on it but it sometimes get stuck on terrain's hollows even if the height to climb is really small.
I'm using MLCP constraint solver and tested PGS solver but does not help.
Here is the concerned code :
Vehicle.hpp
#define USE_MLCP_SOLVER
// I removed other #define because all were just floats
class Vehicle {
public:
// theses bools are set to true when the corresponding key is pressed
bool m_foward = false, m_backward = false, m_leftward = false, m_rightward = false;
bool m_reset = false;
Vehicle(Vao *chassisVao, Vao *wheelVao, const float *heights, uint32_t gridsize, float amplitude);
~Vehicle();
// this function runs the logic of the physics simulation, it gets executed each frame
void stepSimulation(uint32_t frameTime);
// this function instantiate objects to rendered, it gets executed each frame
// not including definition, not revalent
void prepareRendering(std::vector<const Entity *> &entities);
private:
// members are declared here ---> <---
// create physics world and vehicle
void initPhysics(const float *heights, uint32_t gridsize, float amplitude);
// cleanup things
// not including definition, not revalent
void exitPhysics(void);
// reset vehicle position, rotation, momentum, etc..
// not including definition, not revalent
void resetVehicle(void);
// helper function to create rigid body
// not including definition, not revalent
btRigidBody* localCreateRigidBody(btScalar mass, const btTransform& startTransform, btCollisionShape* shape);
};
Vehicle.cpp
#include "Vehicle.hpp"
Vehicle::Vehicle(Vao *chassisVao, Vao *wheelVao, const float *heights, uint32_t gridsize, float amplitude) {
initPhysics(heights, gridsize, amplitude);
if (chassisVao) {
m_chassisEntity = new Entity(chassisVao);
}
for (int i = 0; i < 4; ++i) {
m_wheelEntities.push_back(Entity(wheelVao));
}
}
Vehicle::~Vehicle() {
exitPhysics();
if (m_chassisEntity) {
delete m_chassisEntity;
}
m_wheelEntities.clear();
}
void Vehicle::initPhysics(const float *heights, uint32_t gridsize, float amplitude) {
// setup dynamics world
m_collisionConfiguration = new btDefaultCollisionConfiguration();
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
btVector3 worldMin(-1000, -1000, -1000);
btVector3 worldMax(1000, 1000, 1000);
m_overlappingPairCache = new btAxisSweep3(worldMin, worldMax);
#ifdef USE_MLCP_SOLVER
btDantzigSolver* mlcp = new btDantzigSolver();
// btSolveProjectedGaussSeidel* mlcp = new btSolveProjectedGaussSeidel();
btMLCPSolver* sol = new btMLCPSolver(mlcp);
m_solver = sol;
#else
m_solver = new btSequentialImpulseConstraintSolver();
#endif
m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
#ifdef USE_MLCP_SOLVER
m_world->getSolverInfo().m_minimumSolverBatchSize = 1;
#else
m_world->getSolverInfo().m_minimumSolverBatchSize = 128;
#endif
m_world->getSolverInfo().m_globalCfm = 0.00001;
// create ground object
// btVector3 groundExtents(100, 3, 100);
// btCollisionShape* groundShape = new btBoxShape(groundExtents);
btCollisionShape* groundShape = new btHeightfieldTerrainShape(gridsize + 1, gridsize + 1, heights, 0.0f, amplitude, 1, false);
m_collisionShapes.push_back(groundShape);
btTransform tr;
tr.setIdentity();
tr.setOrigin(btVector3(gridsize * 0.5f, WHEEL_RADIUS, gridsize * 0.5f));
localCreateRigidBody(0, tr, groundShape);
// create vehicle
// BEGIN - create chassis shape
btVector3 vehicleExtents(1.76f, 1.1f, 4.0f);
btCollisionShape* chassisShape = new btBoxShape(vehicleExtents);
m_collisionShapes.push_back(chassisShape);
btCompoundShape* compound = new btCompoundShape();
m_collisionShapes.push_back(compound);
btTransform localTrans;
localTrans.setIdentity();
//localTrans effectively shifts the center of mass with respect to the chassis
localTrans.setOrigin(btVector3(0, 1, 0));
compound->addChildShape(localTrans, chassisShape);
tr.setOrigin(btVector3(0, 0, 0));
m_carChassis = localCreateRigidBody(800, tr, compound);
// END - create chassis shape
// BEGIN - create vehicle
m_vehicleRayCaster = new btDefaultVehicleRaycaster(m_world);
m_vehicle = new btRaycastVehicle(m_tuning, m_carChassis, m_vehicleRayCaster);
m_carChassis->setActivationState(DISABLE_DEACTIVATION); // never deactivate the vehicle
m_world->addVehicle(m_vehicle);
// choose coordinate system
m_vehicle->setCoordinateSystem(0, 1, 2);
btVector3 wheelDirection(0, -1, 0);
btVector3 wheelAxis(-1, 0, 0);
btVector3 connectionPoint(0.5f * vehicleExtents.x(), WHEEL_RADIUS, 0.5f * vehicleExtents.z() - WHEEL_RADIUS);
m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, true);
connectionPoint = btVector3(-0.5f * vehicleExtents.x(), WHEEL_RADIUS, 0.5f * vehicleExtents.z() - WHEEL_RADIUS);
m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, true);
connectionPoint = btVector3(0.5f * vehicleExtents.x(), WHEEL_RADIUS, -0.5f * vehicleExtents.z() + WHEEL_RADIUS);
m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, false);
connectionPoint = btVector3(-0.5f * vehicleExtents.x(), WHEEL_RADIUS, -0.5f * vehicleExtents.z() + WHEEL_RADIUS);
m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, false);
for (int i = 0; i < m_vehicle->getNumWheels(); i++) {
btWheelInfo& wheel = m_vehicle->getWheelInfo(i);
wheel.m_suspensionStiffness = SUSPENSION_STIFFNESS;
wheel.m_wheelsDampingRelaxation = SUSPENSION_DAMPING;
wheel.m_wheelsDampingCompression = SUSPENSION_COMPRESSION;
wheel.m_frictionSlip = WHEEL_FRICTION;
wheel.m_rollInfluence = ROLL_IN_INFLUENCE;
}
resetVehicle();
}
void Vehicle::stepSimulation(uint32_t frameTime) {
float speed = m_vehicle->getCurrentSpeedKmHour();
m_vehicleEngineForce = 0.0f;
m_vehicleBreakingForce = 0.0f;
/* --->
Processing input sets m_vehicleEngineForce, m_vehicleBreakingForce, m_vehicleSteering
<--- */
m_vehicle->applyEngineForce(m_vehicleEngineForce, 2);
m_vehicle->setBrake(m_vehicleBreakingForce, 2);
m_vehicle->applyEngineForce(m_vehicleEngineForce, 3);
m_vehicle->setBrake(m_vehicleBreakingForce, 3);
m_vehicle->setSteeringValue(m_vehicleSteering, 0);
m_vehicle->setSteeringValue(m_vehicleSteering, 1);
m_world->stepSimulation(frameTime * 0.001f, 2);
btMLCPSolver *solver = (btMLCPSolver *) m_world->getConstraintSolver();
int numFallbacks = solver->getNumFallbacks();
if (numFallbacks) {
std::cerr << "MLCP solver failed " << numFallbacks << " times, falling back to btSequentialImpulseSolver" << std::endl;
}
solver->setNumFallbacks(0);
}
And here is a video to illustrate : link
Thank you
I finally solve this issue, I used bullet physics debug drawer to view bounding boxes. The problem was the chassis shape colliding on the terrain because btBoxShape takes the half extent, so I multiplied everything by 0.5 and it works well now.
Here is the debugger code written in C++ for modern OpenGL, based on this forum thread :
BulletDebugDrawer.hpp
#ifndef BULLET_DEBUG_DRAWER_H
#define BULLET_DEBUG_DRAWER_H
#include <bullet/LinearMath/btIDebugDraw.h>
#include <vector>
class BulletDebugDrawer : public btIDebugDraw {
private:
int m_debugMode;
std::vector<float> m_lines;
public:
BulletDebugDrawer();
virtual void drawLine(const btVector3& from,const btVector3& to,const btVector3& color);
virtual void reportErrorWarning(const char* warningString);
virtual void setDebugMode(int debugMode);
virtual int getDebugMode(void) const;
virtual void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color) {
}
virtual void draw3dText(const btVector3& location, const char* textString) {
}
void glfw3_device_create(void);
void glfw3_device_render(const float *matrix);
void glfw3_device_destroy(void);
};
#endif
BulletDebugDrawer.cpp
#include "BulletDebugDrawer.hpp"
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <glad/gl.h>
#define MAX_LINES_DRAWCALL 1000
GLuint dev_program;
GLint dev_uniform_proj;
GLint dev_uniform_col;
GLint dev_attrib_pos;
GLuint dev_vao;
GLuint dev_vbo;
BulletDebugDrawer::BulletDebugDrawer() : m_debugMode(0) {
}
void BulletDebugDrawer::drawLine(const btVector3& from,const btVector3& to, const btVector3& color) {
m_lines.push_back(from.getX());
m_lines.push_back(from.getY());
m_lines.push_back(from.getZ());
m_lines.push_back(to.getX());
m_lines.push_back(to.getY());
m_lines.push_back(to.getZ());
}
void BulletDebugDrawer::setDebugMode(int debugMode) {
m_debugMode = debugMode;
}
int BulletDebugDrawer::getDebugMode() const {
return m_debugMode;
}
void BulletDebugDrawer::reportErrorWarning(const char* warningString) {
std::cout << warningString << std::endl;
}
void BulletDebugDrawer::glfw3_device_create(void) {
GLint status;
static const GLchar *vertex_shader =
"#version 150\n"
"uniform mat4 ProjMtx;\n"
"in vec3 Position;\n"
"void main() {\n"
" gl_Position = ProjMtx * vec4(Position, 1);\n"
"}\n";
static const GLchar *fragment_shader =
"#version 150\n"
"uniform vec3 Color;\n"
"out vec4 Out_Color;\n"
"void main(){\n"
" Out_Color = vec4(Color, 1);\n"
"}\n";
dev_program = glCreateProgram();
GLuint vert_shdr = glCreateShader(GL_VERTEX_SHADER);
GLuint frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vert_shdr, 1, &vertex_shader, 0);
glShaderSource(frag_shdr, 1, &fragment_shader, 0);
glCompileShader(vert_shdr);
glCompileShader(frag_shdr);
glGetShaderiv(vert_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glGetShaderiv(frag_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glAttachShader(dev_program, vert_shdr);
glAttachShader(dev_program, frag_shdr);
glLinkProgram(dev_program);
glGetProgramiv(dev_program, GL_LINK_STATUS, &status);
assert(status == GL_TRUE);
glDetachShader(dev_program, vert_shdr);
glDetachShader(dev_program, frag_shdr);
glDeleteShader(vert_shdr);
glDeleteShader(frag_shdr);
dev_uniform_proj = glGetUniformLocation(dev_program, "ProjMtx");
dev_uniform_col = glGetUniformLocation(dev_program, "Color");
dev_attrib_pos = glGetAttribLocation(dev_program, "Position");
{
/* buffer setup */
glGenBuffers(1, &dev_vbo);
glGenVertexArrays(1, &dev_vao);
glBindVertexArray(dev_vao);
glBindBuffer(GL_ARRAY_BUFFER, dev_vbo);
glBufferData(GL_ARRAY_BUFFER, MAX_LINES_DRAWCALL * 24, nullptr, GL_STREAM_DRAW);
glEnableVertexAttribArray(dev_attrib_pos);
glVertexAttribPointer(dev_attrib_pos, 3, GL_FLOAT, GL_FALSE, 12, 0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void BulletDebugDrawer::glfw3_device_render(const float *matrix) {
glUseProgram(dev_program);
glUniformMatrix4fv(dev_uniform_proj, 1, GL_FALSE, matrix);
glUniform3f(dev_uniform_col, 1.0f, 0.0f, 0.0f);
glBindVertexArray(dev_vao);
glBindBuffer(GL_ARRAY_BUFFER, dev_vbo);
for (int i = 0; i < m_lines.size(); i += 2 * MAX_LINES_DRAWCALL) {
int batchVertexCount = std::min<int>(m_lines.size() - i, 2 * MAX_LINES_DRAWCALL);
glBufferSubData(GL_ARRAY_BUFFER, 0, batchVertexCount * 12, reinterpret_cast<void *>(m_lines.data() + i));
glDrawArrays(GL_LINES, 0, batchVertexCount);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
m_lines.clear();
}
void BulletDebugDrawer::glfw3_device_destroy(void) {
glDeleteProgram(dev_program);
glDeleteBuffers(1, &dev_vbo);
glDeleteVertexArrays(1, &dev_vao);
}
I have a problem on setting up two buffers for my shaders. I wanted to use Compute Shader, but the problem already starts in the Vertex Shader.
What I want: Two separate SSBO with particles inside that I can spin nd move individually.
How I want to start: Just put the initial values into VAOs, copy them into a SSBO in the vertex shader and process them in the Computer Shaders.
Problem: Both VAOs are created equally and the values are equal. However, when I want to copy the values into a SSBO the VAO breaks and only 512 values (which is half of the number of items) are further correct and the others partly have weird different values, what I can see from RenderDoc.
Have a look in my application.cpp
#include "application.hpp"
application::application()
{
this->initParticleSystem();
}
void application::initParticleSystem()
{
this->initializeWindow();
this->initializeOpenGL();
}
void application::runThread()
{
double lastFPStime = glfwGetTime();
int frameCounter = 0;
while (!glfwWindowShouldClose(m_RenderWindow))
{
this->run(frameCounter, lastFPStime);
}
}
void application::initializeWindow()
{
if (!glfwInit())
{
throw runtime_error("\n[ERROR] GLFW initialization failed!\n");
}
this->setupRenderWindow();
/// set key callback
this->setupKeyCallBack();
IOManager::getInstance()->setRenderWindow(m_RenderWindow);
}
void application::initializeOpenGL()
{
Log::logInit(m_Name, "OpenGL");
glewInit();
if (!glewIsSupported("GL_VERSION_4_6"))
{
throw std::runtime_error("\n[ERROR] Sorry, but you need an OpenGL 4.6 Core to run this program.\n");
}
/// get version info
Log::printSystemSettings();
this->initBuffers();
this->initBasicShader();
this->initComputeShader();
}
void application::initBuffers()
{
this->packVec4GridSizeSSBO(VORTEX_1_SEEDS_NR, SHADER_GRID_VOR_1_NR, m_GridPositionVaoHandle, m_PackedGridBufferHandle, { 0.,0. }, "Grid");
this->packVec4GridSizeSSBO(VORTEX_2_SEEDS_NR, SHADER_GRID_VOR_2_NR, m_VortexPositionVaoHandle, m_PackedVortexBufferHandle, { 0.,-0. }, "Vortex");
this->bindBuffers();
}
void application::initComputeShader()
{
this->initComputeShader(GRID_VELOCITY_NR, GRID_VELOCITY_SHADER);
}
void application::bindBuffers()
{
this->bindVertexBufferObject(VORTEX_1_SEEDS_NR, m_PackedGridBufferHandle, "Vortex 1");
this->bindVertexBufferObject(VORTEX_2_SEEDS_NR, m_PackedVortexBufferHandle, "Vortex 2");
}
void application::bindVertexBufferObject(unsigned int index, uint32_t &bufferHandle, string text)
{
glBindBuffer(GL_ARRAY_BUFFER, bufferHandle);
glVertexAttribPointer(index, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(index);
}
void application::packVec4GridSizeSSBO(unsigned int seedNumber, unsigned int shaderPosNumber, uint32_t &vaoHandle, uint32_t &packedBufferHandle, vec2f translate, string text)
{
constexpr ptrdiff_t ssboSize = sizeof(glm::vec4) * GRID_DIM_SQ;
constexpr ptrdiff_t packedBufferSize = ssboSize;
ptrdiff_t ssboOffset = 0;
this->initSeeds(packedBufferSize, ssboSize, packedBufferHandle, translate, text);
this->bindBufferToVertexArrayObject(vaoHandle, packedBufferHandle, seedNumber);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, shaderPosNumber, packedBufferHandle, ssboOffset, ssboSize); /// Grid Positions
}
void application::bindBufferToVertexArrayObject(uint32_t &vaoHandle, uint32_t &bufferHandle, unsigned int seedNumber)
{
glGenVertexArrays(1, &vaoHandle);
glBindVertexArray(vaoHandle);
}
void application::initBasicShader()
{
glEnable(GL_POINT_SPRITE);
uint32_t vertex_shader_handle = IOManager::getInstance()->loadShader(PARTICLE_VERTEX_SHADER, GL_VERTEX_SHADER);
uint32_t fragment_shader_handle = IOManager::getInstance()->loadShader(PARTICLE_FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
m_RenderProgramHandle = glCreateProgram();
glAttachShader(m_RenderProgramHandle, fragment_shader_handle);
glAttachShader(m_RenderProgramHandle, vertex_shader_handle);
glLinkProgram(m_RenderProgramHandle);
IOManager::getInstance()->checkProgramLinked(m_RenderProgramHandle, string(PARTICLE_VERTEX_SHADER) + ", " + string(PARTICLE_FRAGMENT_SHADER));
glDeleteShader(vertex_shader_handle);
glDeleteShader(fragment_shader_handle);
}
void application::initComputeShader(int index, string filePath)
{
uint32_t computeShaderHandle = IOManager::getInstance()->loadShader(filePath, GL_COMPUTE_SHADER);
m_ParticleShaders[index] = glCreateProgram();
glAttachShader(m_ParticleShaders[index], computeShaderHandle);
glLinkProgram(m_ParticleShaders[index]);
IOManager::getInstance()->checkProgramLinked(m_RenderProgramHandle, filePath);
Log::printProgramLog(m_ParticleShaders[index]);
/// delete shader as we're done with them.
glDeleteShader(computeShaderHandle);
}
void application::run(int &frameCounter, double &lastFPStime)
{
this->runParticleSystem();
IOManager::getInstance()->calculateTime(frameCounter, lastFPStime);
}
void application::runParticleSystem()
{
/// process user inputs
glfwPollEvents();
/// step through the simulation if not PAUSED
if (!paused)
{
glUseProgram(m_ParticleShaders[0]);
/// Number of invocations #grid_x * grid_y
glDispatchCompute(GRID_NUM_WORK_GROUPS, 1, 1);
//#TODO: all shaders but the Integrate-Shader can run parallel and don't need the barrier bit in between!
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
m_FrameNumber++;
}
this->runBasicShader();
glfwSwapBuffers(m_RenderWindow);
}
void application::runOpenGLBuffer()
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPointSize(PARTICLE_SIZE);
}
void application::runBasicShader()
{
this->runOpenGLBuffer();
glUseProgram(m_RenderProgramHandle);
glDrawArrays(GL_POINTS, 0, NUM_PARTICLES);
}
void application::initSeeds(ptrdiff_t packedBufferSize, ptrdiff_t positionSSBOSize, uint32_t &bufferHandle, vec2f translate, string text)
{
vector<glm::vec4> initialSeed(GRID_DIM_SQ);
for (int j = 0; j < GRID_DIM; j++) {
for (int i = 0; i < GRID_DIM; i++) {
int index = j * GRID_DIM + i;
/// Normalize positions for GPU to -1 <= xyPos <= 1
vec2f shaderPos = Utils::grid2ShaderDim(vec2i{ i, j });
initialSeed[index].x = shaderPos.x + translate.x;
initialSeed[index].y = shaderPos.y + translate.y;
initialSeed[index].z = index; /// Index of grid cell
initialSeed[index].w = 1;
}
}
this->saveDataInBuffer(packedBufferSize, positionSSBOSize, initialSeed, bufferHandle);
}
void application::saveDataInBuffer(ptrdiff_t packed_buffer_size, ptrdiff_t ssbo_size, vector<glm::vec4> dataVector, uint32_t &bufferHandle)
{
void* initialData = malloc(packed_buffer_size);
memset(initialData, 0, packed_buffer_size);
memcpy(initialData, dataVector.data(), ssbo_size);
glGenBuffers(1, &bufferHandle);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferHandle);
glBufferStorage(GL_SHADER_STORAGE_BUFFER, packed_buffer_size, initialData, GL_DYNAMIC_STORAGE_BIT);
free(initialData);
}
... and my very basic vertex shader.glsl:
#version 460
#extension GL_ARB_separate_shader_objects : enable
// ---- INPUT -----------------------------------
layout (location = 0) in vec4 in_VortexPosition1;
layout (location = 1) in vec4 in_VortexPosition2;
// ---- OUTPUT ----------------------------------
layout (location = 0) out flat uint out_PosIndex;
out gl_PerVertex
{
vec4 gl_Position;
};
// ---- UNIFORM ---------------------------------
layout(std430, binding = 6) buffer grid_vortex1_block
{
vec4 gridVortex1[];
};
layout(std430, binding = 7) buffer grid_vortex2_block
{
vec2 gridVortex2[];
};
void main ()
{
out_PosIndex = uint(in_VortexPosition1.z);
// only half the values
gl_Position = vec4(gridVortex2[out_PosIndex].xy, 0, 1);
// works fine
//gl_Position = vec4(gridVortex1[out_PosIndex].xy, 0, 1);
}
And the Compute Shader.glsl:
#version 460
#extension GL_ARB_separate_shader_objects : enable
#define WORK_GROUP_SIZE 128
layout (local_size_x = WORK_GROUP_SIZE) in;
// ---- UNIFORM ---------------------------------
layout(std430, binding = 6) buffer grid_vortex1_block
{
vec4 gridVortex1[];
};
layout(std430, binding = 7) buffer grid_vortex2_block
{
vec2 gridVortex2[];
};
void main()
{
uint INDEX = gl_GlobalInvocationID.x;
// THIS OPERATION SCREWS UP EVERYTHING IN THE BUFFER, as you can see in pic3
gridVortex1[INDEX].xy += 0.2f;
gridVortex1[INDEX].xy -= 0.2f;
}
The following two pictures show the result. First, correct grid (e.g. vortex 1, not spinning yet), second, the half disappears after one frame for vortex 2.
Thank you for your help.
I've just started following OpenGL SuperBible 7th ed, and translating the examples into LWJGL, but have become stuck on the tessellation shader. In the following program there is the line " //IF THESE TWO LINES..." if the following two lines are commented out then the vertex and fragment shaders work but when the control.tess.glsl and eval.tess.glsl are included then the triangle no longer renders.
I've uploaded my program onto github but will reproduce the code here as well:
package com.ch3vertpipeline;
public class App {
public static void main(String [] args){
LwjglSetup setup = new LwjglSetup();
setup.run();
}
}
package com.ch3vertpipeline;
import java.nio.IntBuffer;
import java.util.Scanner;
import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;
public class LwjglSetup {
private long window;
private int vertex_shader;
private int fragment_shader;
private int tess_control_shader;
private int tess_evaluation_shader;
private int program;
private int vertex_array_object;
public LwjglSetup() {
}
private void init() {
GLFWErrorCallback.createPrint(System.err).set();
if (!glfwInit()) {
throw new IllegalStateException("Unable to initialize GLFW");
}
// Configure GLFW
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
// Create the window
window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL);
if (window == NULL) {
throw new RuntimeException("Failed to create the GLFW window");
}
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
}
});
// Get the thread stack and push a new frame
try (MemoryStack stack = stackPush()) {
IntBuffer pWidth = stack.mallocInt(1); // int*
IntBuffer pHeight = stack.mallocInt(1); // int*
// Get the window size passed to glfwCreateWindow
glfwGetWindowSize(window, pWidth, pHeight);
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center the window
glfwSetWindowPos(
window,
(vidmode.width() - pWidth.get(0)) / 2,
(vidmode.height() - pHeight.get(0)) / 2
);
} // the stack frame is popped automatically
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
public void run() {
System.out.println("Hello LWJGL " + Version.getVersion() + "!");
init();
loop();
// Free the window callbacks and destroy the window
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
// Terminate GLFW and free the error callback
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private void loop() {
GL.createCapabilities();//Critical
System.out.println("OpenGL Verion: " + glGetString(GL_VERSION));
this.compileShader();
vertex_array_object = glGenVertexArrays();
glBindVertexArray(vertex_array_object);
while (!glfwWindowShouldClose(window)) {
double curTime = System.currentTimeMillis() / 1000.0;
double slowerTime = curTime;//assigned direcly but I was applying a factor here
final float colour[] = {
(float) Math.sin(slowerTime) * 0.5f + 0.5f,
(float) Math.cos(slowerTime) * 0.5f + 0.5f,
0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, colour);
glUseProgram(program);
final float attrib[] = {
(float) Math.sin(slowerTime) * 0.5f,
(float) Math.cos(slowerTime) * 0.6f,
0.0f, 0.0f};
//glPatchParameteri(GL_PATCH_VERTICES, 3);//this is the default so is unneeded
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glVertexAttrib4fv(0, attrib);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window); // swap the color buffers
glfwPollEvents();
}
glDeleteVertexArrays(vertex_array_object);
glDeleteProgram(program);
}
private String readFileAsString(String filename) {
String next = new Scanner(LwjglSetup.class.getResourceAsStream(filename), "UTF-8").useDelimiter("\\A").next();
System.out.println("readFileAsString: " + next);
return next;
}
private void compileShader() {
//int program;
//NEW CODE
//create and compile vertex shader
String vertShaderSource = readFileAsString("/vert.glsl");
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, vertShaderSource);
glCompileShader(vertex_shader);
//check compilation
if (glGetShaderi(vertex_shader, GL_COMPILE_STATUS) != 1) {
System.err.println(glGetShaderInfoLog(vertex_shader));
System.exit(1);
}
//create and compile fragment shader
String fragShaderSource = readFileAsString("/frag.glsl");
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, fragShaderSource);
glCompileShader(fragment_shader);
//check compilation
if (glGetShaderi(fragment_shader, GL_COMPILE_STATUS) != 1) {
System.err.println(glGetShaderInfoLog(fragment_shader));
System.exit(1);
}
//create and compile tessellation shader
String tessControlShaderSource = readFileAsString("/control.tess.glsl");
tess_control_shader = glCreateShader(GL40.GL_TESS_CONTROL_SHADER);
glShaderSource(tess_control_shader, tessControlShaderSource);
glCompileShader(tess_control_shader);
//check compilation
if (glGetShaderi(tess_control_shader, GL_COMPILE_STATUS) != 1) {
System.err.println(glGetShaderInfoLog(tess_control_shader));
System.exit(1);
}
//create and compile tessellation shader
String tessEvaluationShaderSource = readFileAsString("/eval.tess.glsl");
tess_evaluation_shader = glCreateShader(GL40.GL_TESS_EVALUATION_SHADER);
glShaderSource(tess_evaluation_shader, tessEvaluationShaderSource);
glCompileShader(tess_evaluation_shader);
//check compilation
if (glGetShaderi(tess_evaluation_shader, GL_COMPILE_STATUS) != 1) {
System.err.println(glGetShaderInfoLog(tess_evaluation_shader));
System.exit(1);
}
//create program and attach it
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
//IF THESE TWO LINES ARE COMMENTED PROGRAM WORKS...although there
//is no tessallation...
glAttachShader(program, tess_control_shader);
glAttachShader(program, tess_evaluation_shader);
glLinkProgram(program);
//check link
if (glGetProgrami(program, GL_LINK_STATUS) != 1) {
System.err.println(glGetProgramInfoLog(program));
System.exit(1);
}
glValidateProgram(program);
if (glGetProgrami(program, GL_VALIDATE_STATUS) != 1) {
System.err.println(glGetProgramInfoLog(program));
System.exit(1);
}
//delete shaders as the program has them now
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glDeleteShader(tess_control_shader);
glDeleteShader(tess_evaluation_shader);
//return program;
}
}
vert.glsl
#version 440 core
//'offset' is an input vertex attribute
layout (location=0) in vec4 offset;
layout (location=1) in vec4 color;
out vec4 vs_color;
void main(void)
{
const vec4 vertices[3] = vec4[3]( vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
//Add 'offset' to hour hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
//Output a fixed value for vs_color
vs_color = color;
}
frag.glsl
#version 440 core
in vec4 vs_color;
out vec4 color;
void main(void)
{
color = vs_color;
}
control.tess.glsl
#version 440 core
layout (vertices=3) out;
void main(void)
{
//Only if I am invocation 0
if (gl_InvocationID == 0){
gl_TessLevelInner[0] = 5.0;
gl_TessLevelOuter[0] = 5.0;
gl_TessLevelOuter[1] = 5.0;
gl_TessLevelOuter[2] = 5.0;
}
//Everybody copies their input to their output?
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
}
eval.tess.glsl
#version 440 core
layout (triangles, equal_spacing, cw) in;
void main(void){
gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +
(gl_TessCoord.y * gl_in[1].gl_Position) +
(gl_TessCoord.z * gl_in[2].gl_Position);
}
Finally, if it helps here is some version information, which is printed at the start of the application:
Hello LWJGL 3.1.5 build 1!
OpenGL Verion: 4.4.0 NVIDIA 340.107
glDrawArrays(GL_TRIANGLES, 0, 3);
When you draw something with tessellation, you are drawing patches, not triangles. Hence, you have to specify GL_PATCHES:
glDrawArrays(GL_PATCHES, 0, 3);
//Everybody copies their input to their output?
The reason is that the input vertices and output vertices of the tessellation control shader are not related to each other. The input vertices are taken from the input stream, i.e. your vertex buffers (after being processed by the vertex shader). Their number is specified by the GL_PATCH_VERTICES parameter. Each invocation takes this number of vertices from the buffer. The output vertices are kept internally in the pipeline. Their number is specified by the layout directive. This number can be different from the number of input vertices. They can also have different attributes. I find it more intuitive to think of these vertices as pieces of data instead of actual vertices with a geometric meaning. In some cases, this interpretation might make sense, but definitely not in all.
I use QOpenGLWidget to derive my own widget rendered OpenGL. I want to set vertices data later or changed them whenever possible instead of in initializeGL(). Trying to draw a triangle by giving vertices in initializedGL() is ok. But I want to call updateModel() function externally to changed model whenever I want.
In initializeGL(), commented lines are standard use of QOpenGLWidget. It works well. When I call updateModel() to changed vertices, it can't draw anything.
What's wrong? Thanks.
#include "openglwindow.h"
#include <QDebug>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QDebug>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QVector3D>
#include <QTimer>
OpenGLWidget::OpenGLWidget(QWidget *parent):
QOpenGLWidget(parent),
m_vshader(0),
m_fshader(0),
m_program(0),
m_mousePressed(false),
m_eye(QVector3D(0,0,10)),
m_center(QVector3D(0,0,0)),
m_up(QVector3D(0,1,0)),
m_verticalAngle(45.f){
m_modelUpdated = false;
setMinimumSize(300,300);
//matrix initialization
m_model.setToIdentity();
m_view.lookAt(m_eye,m_center,m_up),
m_projection.perspective(m_verticalAngle,aspectRatio(),0.01f,100.0f);
m_timer = new QTimer(this);
m_timer->setInterval(10);
connect(m_timer,&QTimer::timeout,this,QOverload<>::of(&QWidget::update));
}
OpenGLWidget::~OpenGLWidget()
{
// makeCurrent();
// delete m_program;
// delete m_vshader;
// delete m_fshader;
// m_vbo.destroy();
// m_vao.destroy();
// doneCurrent();
}
void OpenGLWidget::initializeGL()
{
qDebug()<<"initializeGL()";
initializeOpenGLFunctions();
glEnable(GL_DEPTH_TEST);
glClearColor(.2f,.3f,.3f,1.0f);
//Initialized program shader
m_vshader = new QOpenGLShader(QOpenGLShader::Vertex);
const char * vcode =
"#version 330 core \
layout (location = 0) in vec3 aPos;\
layout (location = 1) in vec3 aNor; \
uniform mat4 model; \
uniform mat4 view; \
uniform mat4 projection;\
out vec3 Normal;\
out vec3 FragPos;\
void main() \
{ \
gl_Position = projection*view*model*vec4(aPos,1.0);\
Normal = aNor;\
FragPos = vec3(model*vec4(aPos,1.0));\
}";
m_vshader->compileSourceCode(vcode);
m_fshader = new QOpenGLShader(QOpenGLShader::Fragment);
const char * fcode =
"#version 330 core \
out vec4 FragColor; \
in vec3 Normal;\
in vec3 FragPos;\
void main()\
{ \
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\
} ";
m_fshader->compileSourceCode(fcode);
m_program = new QOpenGLShaderProgram();
m_program->addShader(m_vshader);
m_program->addShader(m_fshader);
m_program->link();
m_program->bind();
m_attriPos = m_program->attributeLocation("aPos");
// m_modelAttriLocation = m_program->attributeLocation("model");
// m_viewAttriLocation = m_program->attributeLocation("view");
// m_projectAttriLocation = m_program->attributeLocation("projection");
// m_vertices<<QVector3D(-0.5f,-0.5f,0.0f)
// <<QVector3D(0.5f,-0.5f,0.0f)
// <<QVector3D(0.0f,0.5f,0.0f)
// <<QVector3D(-0.5f,0.5f,0.0f)
// <<QVector3D(0.5f,0.5f,0.0f)
// <<QVector3D(0.0f,-0.5f,0.0f);
//create VAO
m_vao.create();
m_vao.bind();
//create VBO
m_vbo.create();
qDebug()<<m_vbo.bind();
m_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo.allocate(m_vertices.constData(),m_vertices.count()*3*sizeof(float));
m_program->enableAttributeArray(0);
m_program->setAttributeBuffer(0,GL_FLOAT,0,3);
//m_vbo.release();
//m_vao.release();
m_program->release();
}
void OpenGLWidget::resizeGL(int w, int h){
//Updating m_projection matrix here
m_projection.setToIdentity();
m_projection.perspective(45.0f,w/float(h),0.01f,100.0f);
}
void OpenGLWidget::paintGL(){
//glClearColor(.2f,.3f,.3f,1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// m_program->bind();
// {
// m_program->setUniformValue("projection",m_projection);
// m_program->setUniformValue("view",m_view);
// m_program->setUniformValue("model",m_model);
// m_vao.bind();
// glDrawArrays(GL_TRIANGLES,0,3);
// m_vao.release();
// }
// m_program->release();
paintModel();
}
void OpenGLWidget::mousePressEvent(QMouseEvent *event)
{
m_mousePressed = true;
m_prevXPos = event->x();
m_prevYPos = event->y();
}
void OpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
if(m_mousePressed == true){
//update matrix here
int deltaX = event->x()-m_prevXPos;
int deltaY = event->y()-m_prevXPos;
updateCameraVectors(deltaX,deltaY);
}
}
void OpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
m_mousePressed = false;
}
void OpenGLWidget::wheelEvent(QWheelEvent *event)
{
updateCameraVectors(0,0,event->angleDelta().y());
}
float OpenGLWidget::aspectRatio()
{
return width()/static_cast<float>(height());
}
void OpenGLWidget::paintModel()
{
m_program->bind();
{
m_vao.bind();
m_program->setUniformValue("projection",m_projection);
m_program->setUniformValue("view",m_view);
m_program->setUniformValue("model",m_model);
glDrawArrays(GL_TRIANGLES,0,m_vertices.count());
m_vao.release();
}
m_program->release();
}
void OpenGLWidget::updateModel(const QVector<QVector3D> &model)
{
m_vertices = model;
if(isValid() == false)
return;
makeCurrent();
m_vbo.destroy();
m_vao.destroy();
if(m_vbo.isCreated() == false){
m_vao.create();
m_vao.bind();
m_vbo.create();
m_vbo.bind();
m_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo.allocate(m_vertices.constData(),m_vertices.count()*3*sizeof(float));
for(GLenum err;(err = glGetError())!=GL_NO_ERROR;){
qDebug()<<"error:"<<err;
}
m_vbo.release();
m_vao.release();
}
doneCurrent();
}
When the VBO/VAO get deleted and recreated, you also have to redo the VAO setup, namely
m_program->enableAttributeArray(0);
m_program->setAttributeBuffer(0,GL_FLOAT,0,3);
You might be confused because these functions are called on the shader program object, but they are actually part of the VAO state.
Note, that deleting and generating the VAO/VBO again in the updateModel is not really necessary. If you want to change the vertices, it is sufficient to call m_vbo.allocate with the new data.
I try to render 2048 quads in modern OpenGL with VBOs and shaders. But the performance is horrible. I've just about 50 FPS by using the following code. I think, I call something every frame, what slows down the application, but I don't know what it is.
Also I think, that it is very slow to send the position, view and projection matrix every frame to the shader, if only the objects position changed.
What can I do to get this code more faster?
Model Code:
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Arrays;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
public abstract class Model
{
private TexturedVertex[] vertices;
private byte[] indices;
private FloatBuffer verticesBuffer;
private ByteBuffer indicesBuffer;
private Vector3f position;
private Vector3f angle;
private Vector3f scale;
int shaderProgram;
int indicesCount;
private int projectionMatrixLocation;
private int viewMatrixLocation;
private int modelMatrixLocation;
int vaoID;
int vboID;
int vboiID;
public void load (TexturedVertex[] vertices, byte[] indices, Shader shader)
{
this.position = new Vector3f(0, 0, -1);
this.angle = new Vector3f(0, 0, 0);
this.scale = new Vector3f(1, 1, 1);
this.vertices = vertices;
this.indices = indices;
this.shaderProgram = shader.getProgramID();
this.generateBuffer();
this.generateArrayBufferObjects();
this.configureShader();
}
private void generateBuffer()
{
// === Vertices Buffer === //
this.verticesBuffer = BufferUtils.createFloatBuffer(vertices.length * TexturedVertex.elementCount);
for (int i = 0; i < vertices.length; i++)
{
verticesBuffer.put(vertices[i].getElements());
System.out.print ("XYZW: " + Arrays.toString(vertices[i].getXYZW()));
System.out.print (" RGBA: " + Arrays.toString(vertices[i].getRGBA()));
System.out.println (" ST: " + Arrays.toString(vertices[i].getST()));
}
verticesBuffer.flip();
// === Generate Indices Buffer === //
this.indicesCount = indices.length;
this.indicesBuffer = BufferUtils.createByteBuffer(indicesCount);
indicesBuffer.put(indices);
indicesBuffer.flip();
}
private void generateArrayBufferObjects()
{
// === Generate VAO & VBO === //
this.vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoID);
this.vboID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
this.vboiID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiID);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
System.out.println ();
System.out.println ("VAO-ID: #" + vaoID);
System.out.println ("VBO-ID: #" + vboID);
System.out.println ("VBOI-ID: #" + vboiID);
// === Put informations to shader === //
GL20.glVertexAttribPointer(0, TexturedVertex.positionElementCount, GL11.GL_FLOAT, false, TexturedVertex.stride, TexturedVertex.positionByteOffset);
GL20.glVertexAttribPointer(1, TexturedVertex.colorElementCount, GL11.GL_FLOAT, false, TexturedVertex.stride, TexturedVertex.colorByteOffset);
GL20.glVertexAttribPointer(2, TexturedVertex.textureElementCount, GL11.GL_FLOAT, false, TexturedVertex.stride, TexturedVertex.textureByteOffset);
// === Unbind Buffers === //
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
private void configureShader()
{
// === Bind shader === //
GL20.glUseProgram(shaderProgram);
// === Get matrix location === //
this.projectionMatrixLocation = GL20.glGetUniformLocation(shaderProgram, "projectionMatrix");
this.viewMatrixLocation = GL20.glGetUniformLocation(shaderProgram, "viewMatrix");
this.modelMatrixLocation = GL20.glGetUniformLocation(shaderProgram, "modelMatrix");
// === Update the matrix === //
this.updateMatrix();
// === Unbind shader === //
GL20.glUseProgram(0);
}
void updateMatrix()
{
// === Bind shader === //
GL20.glUseProgram(shaderProgram);
// === Load matrix === //
Matrix4f projectionMatrix = GameManager.camera.getProjectionMatrix();
Matrix4f viewMatrix = GameManager.camera.getViewMatrix();
Matrix4f modelMatrix = new Matrix4f();
// === Scale, translate and rotate matrix === //
Matrix4f.scale(scale, modelMatrix, modelMatrix);
Matrix4f.translate(position, modelMatrix, modelMatrix);
Matrix4f.rotate(Util.degreesToRadians(angle.z), new Vector3f(0, 0, 1), modelMatrix, modelMatrix);
Matrix4f.rotate(Util.degreesToRadians(angle.y), new Vector3f(0, 1, 0), modelMatrix, modelMatrix);
Matrix4f.rotate(Util.degreesToRadians(angle.x), new Vector3f(1, 0, 0), modelMatrix, modelMatrix);
// === Apply uniform matrix to shader === //
FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(16);
projectionMatrix.store(matrixBuffer);
matrixBuffer.flip();
GL20.glUniformMatrix4(projectionMatrixLocation, false, matrixBuffer);
viewMatrix.store(matrixBuffer);
matrixBuffer.flip();
GL20.glUniformMatrix4(viewMatrixLocation, false, matrixBuffer);
modelMatrix.store(matrixBuffer);
matrixBuffer.flip();
GL20.glUniformMatrix4(modelMatrixLocation, false, matrixBuffer);
// === Unbind shader === //
GL20.glUseProgram(0);
}
public void setPosition (Vector3f newPosition)
{
this.position = newPosition;
this.updateMatrix();
}
public void setAngle (Vector3f newAngle)
{
this.angle = newAngle;
this.updateMatrix();
}
public void setScale (Vector3f newAngle)
{
this.scale = newAngle;
this.updateMatrix();
}
}
Render Code:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Vector3f;
import de.matthiasmann.twl.utils.PNGDecoder;
import de.matthiasmann.twl.utils.PNGDecoder.Format;
public class GameManager
{
private Logger logger;
private Profiler profiler;
private Window window;
public static Camera camera;
public GameManager()
{
init();
ModelChest[] chests = new ModelChest[2048];
for (int i = 0; i < 2048; i++)
{
chests[i] = new ModelChest();
}
ModelChest chest = new ModelChest();
ModelChest chestB = new ModelChest();
int textureID = loadPNGTexture ("Getigerte-Katze-Baby-Decke.png", GL13.GL_TEXTURE0);
while (!window.isCloseRequested())
{
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
{
chest.setPosition(new Vector3f(1, 0, -0.5F));
GL20.glUseProgram(chest.shaderProgram);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL30.glBindVertexArray(chest.vaoID);
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, chest.vboiID);
{
GL11.glDrawElements(GL11.GL_TRIANGLES, chest.indicesCount, GL11.GL_UNSIGNED_BYTE, 0);
}
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL30.glBindVertexArray(0);
//GL20.glUseProgram(0);
for (float i = 0; i < 2048; i++)
{
chests[(int)i].setPosition(new Vector3f(-1F + ((float)i / 30F), 0, -0.5F));
GL20.glUseProgram(chests[(int)i].shaderProgram);
GL30.glBindVertexArray(chests[(int)i].vaoID);
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, chests[(int)i].vboiID);
{
GL11.glDrawElements(GL11.GL_TRIANGLE_STRIP , chests[(int)i].indicesCount, GL11.GL_UNSIGNED_BYTE, 0);
}
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL30.glBindVertexArray(0);
GL20.glUseProgram(0);
}
}
Display.update();
}
destroy();
}
public void init()
{
logger = new Logger();
logger.init();
profiler = new Profiler();
profiler.init();
window = new Window();
window.init();
camera = new Camera();
camera.init();
}
public void destroy()
{
logger.destroy();
profiler.destroy();
window.destroy();
camera.destroy();
System.exit(0);
}
private int loadPNGTexture(String filename, int textureUnit) {
ByteBuffer buf = null;
int tWidth = 0;
int tHeight = 0;
try {
// Open the PNG file as an InputStream
InputStream in = new FileInputStream(filename);
// Link the PNG decoder to this stream
PNGDecoder decoder = new PNGDecoder(in);
// Get the width and height of the texture
tWidth = decoder.getWidth();
tHeight = decoder.getHeight();
// Decode the PNG file in a ByteBuffer
buf = ByteBuffer.allocateDirect(
4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buf, decoder.getWidth() * 4, Format.RGBA);
buf.flip();
in.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
// Create a new texture object in memory and bind it
int texId = GL11.glGenTextures();
GL13.glActiveTexture(textureUnit);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texId);
// All RGB bytes are aligned to each other and each component is 1 byte
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
// Upload the texture data and generate mip maps (for scaling)
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, tWidth, tHeight, 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
// Setup the ST coordinate system
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
// Setup what to do when the texture has to be scaled
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR_MIPMAP_LINEAR);
return texId;
}
}
Camera:
import org.lwjgl.opengl.Display;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
public class Camera extends Worker
{
private Matrix4f projectionMatrix;
private Matrix4f viewMatrix;
private Vector3f position;
private Vector3f angle;
private float fieldOfView;
private float aspectRatio;
private float nearPlane;
private float farPlane;
private float xScale;
private float yScale;
private float frustumLength;
#Override
protected void onInitialize()
{
// Apply default settings
this.fieldOfView = 60f;
this.aspectRatio = (float) Display.getWidth() / (float) Display.getHeight();
this.nearPlane = 0.1f;
this.farPlane = 100f;
this.position = new Vector3f(0, 0, -1);
this.angle = new Vector3f(0, 0, 0);
// Calculate scale and furstum length
this.yScale = Util.coTangent(Util.degreesToRadians(fieldOfView / 2f));
this.xScale = yScale / aspectRatio;
this.frustumLength = farPlane - nearPlane;
// Projection Matrix
projectionMatrix = new Matrix4f();
projectionMatrix.m00 = xScale;
projectionMatrix.m11 = yScale;
projectionMatrix.m22 = -((farPlane + nearPlane) / frustumLength);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * nearPlane * farPlane) / frustumLength);
projectionMatrix.m33 = 0;
// View Matrix
viewMatrix = new Matrix4f();
Matrix4f.translate(position, viewMatrix, viewMatrix);
Matrix4f.rotate(Util.degreesToRadians(angle.z), new Vector3f(0, 0, 1),
viewMatrix, viewMatrix);
Matrix4f.rotate(Util.degreesToRadians(angle.y), new Vector3f(0, 1, 0),
viewMatrix, viewMatrix);
Matrix4f.rotate(Util.degreesToRadians(angle.x), new Vector3f(1, 0, 0),
viewMatrix, viewMatrix);
}
public Matrix4f getProjectionMatrix()
{
return this.projectionMatrix;
}
public Matrix4f getViewMatrix()
{
return this.viewMatrix;
}
public void setPosition (Vector3f newPosition)
{
Matrix4f.translate(newPosition, viewMatrix, viewMatrix);
}
public void setPosition (float x, float y, float z)
{
setPosition(new Vector3f (x, y, z));
}
public void setAngle (Vector3f newAngle)
{
Matrix4f.rotate(Util.degreesToRadians(newAngle.z), new Vector3f(0, 0, 1),
viewMatrix, viewMatrix);
Matrix4f.rotate(Util.degreesToRadians(newAngle.y), new Vector3f(0, 1, 0),
viewMatrix, viewMatrix);
Matrix4f.rotate(Util.degreesToRadians(newAngle.x), new Vector3f(1, 0, 0),
viewMatrix, viewMatrix);
}
public void setAngle (float x, float y, float z)
{
setAngle(new Vector3f (x, y, z));
}
#Override
protected void onDestroy()
{
;
}
#Override
protected void onTick()
{
;
}
}
Shader:
#version 150 core
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;
out vec4 pass_Color;
out vec2 pass_TextureCoord;
void main(void) {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;
pass_Color = in_Color;
pass_TextureCoord = in_TextureCoord;
}