Billboarding C++ - c++

I got a code from my teacher that currently shows a 3D globe and a 2D particle system. The camera moves around in circles. The particle system is supposed to face the camera.
According to my lecture notes, I have to multiply the billboard with the inverse of the camera's view matrix. I would love to try that but I have trouble using the variable for the view matrix.
#include "pch.h"
#include <Kore/Application.h>
#include <Kore/IO/FileReader.h>
#include <Kore/Math/Core.h>
#include <Kore/Math/Random.h>
#include <Kore/System.h>
#include <Kore/Input/Keyboard.h>
#include <Kore/Input/Mouse.h>
#include <Kore/Audio/Mixer.h>
#include <Kore/Graphics/Image.h>
#include <Kore/Graphics/Graphics.h>
#include <Kore/Log.h>
#include "ObjLoader.h"
#include "Collision.h"
#include "PhysicsWorld.h"
#include "PhysicsObject.h"
using namespace Kore;
// A simple particle implementation
class Particle {
public:
VertexBuffer* vb;
IndexBuffer* ib;
mat4 M;
// The current position
vec3 position;
// The current velocity
vec3 velocity;
// The remaining time to live
float timeToLive;
// The total time time to live
float totalTimeToLive;
// Is the particle dead (= ready to be re-spawned?)
bool dead;
void init(const VertexStructure& structure) {
vb = new VertexBuffer(4, structure,0);
float* vertices = vb->lock();
SetVertex(vertices, 0, -1, -1, 0, 0, 0);
SetVertex(vertices, 1, -1, 1, 0, 0, 1);
SetVertex(vertices, 2, 1, 1, 0, 1, 1);
SetVertex(vertices, 3, 1, -1, 0, 1, 0);
vb->unlock();
// Set index buffer
ib = new IndexBuffer(6);
int* indices = ib->lock();
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 0;
indices[4] = 2;
indices[5] = 3;
ib->unlock();
dead = true;
}
void Emit(vec3 pos, vec3 velocity, float timeToLive) {
position = pos;
this->velocity = velocity;
dead = false;
this->timeToLive = timeToLive;
totalTimeToLive = timeToLive;
}
Particle() {
}
void SetVertex(float* vertices, int index, float x, float y, float z, float u, float v) {
vertices[index* 8 + 0] = x;
vertices[index*8 + 1] = y;
vertices[index*8 + 2] = z;
vertices[index*8 + 3] = u;
vertices[index*8 + 4] = v;
vertices[index*8 + 5] = 0.0f;
vertices[index*8 + 6] = 0.0f;
vertices[index*8 + 7] = -1.0f;
}
void render(TextureUnit tex, Texture* image) {
Graphics::setTexture(tex, image);
Graphics::setVertexBuffer(*vb);
Graphics::setIndexBuffer(*ib);
Graphics::drawIndexedVertices();
}
void Integrate(float deltaTime) {
timeToLive -= deltaTime;
if (timeToLive < 0.0f) {
dead = true;
}
// Note: We are using no forces or gravity at the moment.
position += velocity * deltaTime;
// Build the matrix
M = mat4::Translation(position.x(), position.y(), position.z()) * mat4::Scale(0.2f, 0.2f, 0.2f);
}
};
class ParticleSystem {
public:
// The center of the particle system
vec3 position;
// The minimum coordinates of the emitter box
vec3 emitMin;
// The maximal coordinates of the emitter box
vec3 emitMax;
// The list of particles
Particle* particles;
// The number of particles
int numParticles;
// The spawn rate
float spawnRate;
// When should the next particle be spawned?
float nextSpawn;
ParticleSystem(int maxParticles, const VertexStructure& structure ) {
particles = new Particle[maxParticles];
numParticles = maxParticles;
for (int i = 0; i < maxParticles; i++) {
particles[i].init(structure);
}
spawnRate = 0.05f;
nextSpawn = spawnRate;
position = vec3(0.5f, 1.3f, 0.5f);
float b = 0.1f;
emitMin = position + vec3(-b, -b, -b);
emitMax = position + vec3(b, b, b);
}
void update(float deltaTime) {
// Do we need to spawn a particle?
nextSpawn -= deltaTime;
bool spawnParticle = false;
if (nextSpawn < 0) {
spawnParticle = true;
nextSpawn = spawnRate;
}
for (int i = 0; i < numParticles; i++) {
if (particles[i].dead) {
if (spawnParticle) {
EmitParticle(i);
spawnParticle = false;
}
}
particles[i].Integrate(deltaTime);
}
}
void render(TextureUnit tex, Texture* image, ConstantLocation mLocation, mat4 V) {
Graphics::setBlendingMode(BlendingOperation::SourceAlpha, BlendingOperation::InverseSourceAlpha);
Graphics::setRenderState(RenderState::DepthWrite, false);
/************************************************************************/
/* Exercise 7 1.1 */
/************************************************************************/
/* Change the matrix V in such a way that the billboards are oriented towards the camera */
/************************************************************************/
/* Exercise 7 1.2 */
/************************************************************************/
/* Animate using at least one new control parameter */
for (int i = 0; i < numParticles; i++) {
// Skip dead particles
if (particles[i].dead) continue;
Graphics::setMatrix(mLocation, particles[i].M * V);
particles[i].render(tex, image);
}
Graphics::setRenderState(RenderState::DepthWrite, true);
}
float getRandom(float minValue, float maxValue) {
int randMax = 1000000;
int randInt = Random::get(0, randMax);
float r = (float) randInt / (float) randMax;
return minValue + r * (maxValue - minValue);
}
void EmitParticle(int index) {
// Calculate a random position inside the box
float x = getRandom(emitMin.x(), emitMax.x());
float y = getRandom(emitMin.y(), emitMax.y());
float z = getRandom(emitMin.z(), emitMax.z());
vec3 pos;
pos.set(x, y, z);
vec3 velocity(0, 0.3f, 0);
particles[index].Emit(pos, velocity, 3.0f);
}
};
namespace {
const int width = 1024;
const int height = 768;
double startTime;
Shader* vertexShader;
Shader* fragmentShader;
Program* program;
float angle = 0.0f;
// null terminated array of MeshObject pointers
MeshObject* objects[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
// null terminated array of PhysicsObject pointers
PhysicsObject* physicsObjects[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
// The view projection matrix aka the camera
mat4 P;
mat4 View;
mat4 PV;
vec3 cameraPosition;
MeshObject* sphere;
PhysicsObject* po;
PhysicsWorld physics;
// uniform locations - add more as you see fit
TextureUnit tex;
ConstantLocation pvLocation;
ConstantLocation mLocation;
ConstantLocation tintLocation;
Texture* particleImage;
ParticleSystem* particleSystem;
double lastTime;
void update() {
double t = System::time() - startTime;
double deltaT = t - lastTime;
//Kore::log(Info, "%f\n", deltaT);
lastTime = t;
Kore::Audio::update();
Graphics::begin();
Graphics::clear(Graphics::ClearColorFlag | Graphics::ClearDepthFlag, 0xff9999FF, 1000.0f);
Graphics::setFloat4(tintLocation, vec4(1, 1, 1, 1));
program->set();
angle += 0.3f * deltaT;
float x = 0 + 3 * Kore::cos(angle);
float z = 0 + 3 * Kore::sin(angle);
cameraPosition.set(x, 2, z);
//PV = mat4::Perspective(60, (float)width / (float)height, 0.1f, 100) * mat4::lookAt(vec3(0, 2, -3), vec3(0, 2, 0), vec3(0, 1, 0));
P = mat4::Perspective(60, (float)width / (float)height, 0.1f, 100);
View = mat4::lookAt(vec3(x, 2, z), vec3(0, 2, 0), vec3(0, 1, 0));
PV = P * View;
Graphics::setMatrix(pvLocation, PV);
// iterate the MeshObjects
MeshObject** current = &objects[0];
while (*current != nullptr) {
// set the model matrix
Graphics::setMatrix(mLocation, (*current)->M);
(*current)->render(tex);
++current;
}
// Update the physics
physics.Update(deltaT);
PhysicsObject** currentP = &physics.physicsObjects[0];
while (*currentP != nullptr) {
(*currentP)->UpdateMatrix();
Graphics::setMatrix(mLocation, (*currentP)->Mesh->M);
(*currentP)->Mesh->render(tex);
++currentP;
}
particleSystem->update(deltaT);
particleSystem->render(tex, particleImage, mLocation, View);
Graphics::end();
Graphics::swapBuffers();
}
void SpawnSphere(vec3 Position, vec3 Velocity) {
PhysicsObject* po = new PhysicsObject();
po->SetPosition(Position);
po->Velocity = Velocity;
po->Collider.radius = 0.2f;
po->Mass = 5;
po->Mesh = sphere;
// The impulse should carry the object forward
// Use the inverse of the view matrix
po->ApplyImpulse(Velocity);
physics.AddObject(po);
}
void keyDown(KeyCode code, wchar_t character) {
if (code == Key_Space) {
// The impulse should carry the object forward
// Use the inverse of the view matrix
vec4 impulse(0, 0.4, 2, 0);
mat4 viewI = View;
viewI.Invert();
impulse = viewI * impulse;
vec3 impulse3(impulse.x(), impulse.y(), impulse.z());
SpawnSphere(cameraPosition + impulse3 *0.2f, impulse3);
}
}
void keyUp(KeyCode code, wchar_t character) {
if (code == Key_Left) {
// ...
}
}
void mouseMove(int x, int y, int movementX, int movementY) {
}
void mousePress(int button, int x, int y) {
}
void mouseRelease(int button, int x, int y) {
}
void init() {
FileReader vs("shader.vert");
FileReader fs("shader.frag");
vertexShader = new Shader(vs.readAll(), vs.size(), VertexShader);
fragmentShader = new Shader(fs.readAll(), fs.size(), FragmentShader);
// This defines the structure of your Vertex Buffer
VertexStructure structure;
structure.add("pos", Float3VertexData);
structure.add("tex", Float2VertexData);
structure.add("nor", Float3VertexData);
program = new Program;
program->setVertexShader(vertexShader);
program->setFragmentShader(fragmentShader);
program->link(structure);
tex = program->getTextureUnit("tex");
pvLocation = program->getConstantLocation("PV");
mLocation = program->getConstantLocation("M");
tintLocation = program->getConstantLocation("tint");
objects[0] = new MeshObject("Base.obj", "Level/basicTiles6x6.png", structure);
objects[0]->M = mat4::Translation(0.0f, 1.0f, 0.0f);
sphere = new MeshObject("ball_at_origin.obj", "Level/unshaded.png", structure);
SpawnSphere(vec3(0, 2, 0), vec3(0, 0, 0));
Graphics::setRenderState(DepthTest, true);
Graphics::setRenderState(DepthTestCompare, ZCompareLess);
Graphics::setTextureAddressing(tex, U, Repeat);
Graphics::setTextureAddressing(tex, V, Repeat);
particleImage = new Texture("SuperParticle.png", true);
particleSystem = new ParticleSystem(100, structure);
}
}
int kore(int argc, char** argv) {
Application* app = new Application(argc, argv, width, height, 0, false, "Exercise7");
init();
app->setCallback(update);
startTime = System::time();
lastTime = 0.0f;
Kore::Mixer::init();
Kore::Audio::init();
Keyboard::the()->KeyDown = keyDown;
Keyboard::the()->KeyUp = keyUp;
Mouse::the()->Move = mouseMove;
Mouse::the()->Press = mousePress;
Mouse::the()->Release = mouseRelease;
app->start();
delete app;
return 0;
}
There's a comment where the teacher wants us to add the code.
The variable for the view matrix "View" is in "namespace". I've only ever used namespace as a library but this one doesn't have a name. So how do I use it?
The comment says that we should use matrix V. So I just add V = Inverse View Matrix * Model Matrix to the code and it removes the rotation?
I'm sorry for the stupid questions, it's supposed to be a class for beginners but it's really anything but. The lecture notes aren't very helpful when it comes to the programming part and I only found tutorials for OpenGL or Unity or Direct X and where not using any of it.
Please help me, I need to hand this in until Saturday morning and I've already spent the last two days trying out code and I've got nothing so far!
You can find the whole thing here: https://github.com/TUDGameTechnology/Exercise7

You don't have to do anything special to access an unnamed namespace. This thread explains more.
You are most probably trying to reference View within methods that cannot see your namespace because of the order in which they are defined in your file.
This line in your update method:
particleSystem->render(tex, particleImage, mLocation, View);
is already passing View into the render method.
void render(TextureUnit tex, Texture* image, ConstantLocation mLocation, mat4 V)
That means that in this case mat4 v is your camera view.

Related

Using bullet physics car and heightfield, car get stuck how can I fix?

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);
}

Ray tracing: making a plane for sphere

I am working on an assignment where I need to ray trace a sphere with a plane (floor). I have the sphere but I am having trouble with the plane. I use the ray-plane intersection formula:
t = -(o-p).n / d.n. I have this in Plane.h, however when I run my code I get errors from Ray.h. Could someone explain what I'm doing wrong? Any help would be appreciated.
Plane.h
`#include "..\..\raytrace\Ray.h"
class Plane
{
using Colour = cv::Vec3b; // RGB Value
private:
Vec3 normal_;
Vec3 distance_;
Colour color_;
public:
Plane();
Plane(Vec3 norm, Vec3 dis, Colour color) : normal_(norm), distance_(dis), color_(color) {}
Vec3 norm() const {
return normal_;
}
Vec3 dis() const {
return distance_;
}
Colour color() const {
return color_;
}
float findIntersection(Ray ray) {
Vec3 rayDirection = ray.mPosition();
float denominator = rayDirection.dot(normal_);
if (denominator == 0) {
return false;
}
else {
//mPosition() is origin in Ray.h
float t = -(((ray.mPosition() - distance_)).dot(normal_)) / denominator;
}
}
};
`
Ray.h
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <cmath>
#include "Image.h"
// Data types
typedef float Scalar; //**custom datatype: Scalar is float
typedef Eigen::Matrix<Scalar, 3, 1> Vec3; //***Vec3 is a custom datatype (specific kind)
typedef Eigen::Matrix<Scalar, 2, 1> Vec2;
typedef unsigned char uchar;
class Ray
{
private:
Vec3 mPosition_; //point
public:
Ray() {}
//constuctor, when we construct ray we get mPosition_
Ray(Vec3 mPosition) : mPosition_(mPosition) {
//
}
float t;
Vec3 mPosition() const {
return mPosition_;
}
public:
inline Vec3 generateRay(Vec3 const& pt) {
Vec3 origin = mPosition_;
Vec3 direction = pt - mPosition_; // d = s -e, pt is pixel Position
direction.normalize();
return pt + t * direction;
}
};
main.cpp
#include <cmath>
#include "Image.h"
#include "Ray.h"
#include "../build/raytrace/Plane.h"
//Color functions
using Colour = cv::Vec3b; // RGB Value
//Color is a Vec3b datatype, use Color instead of Vec3b, it has 3 vectors, hold 3 values b/w 0-255
Colour red() { return Colour(255, 0, 0); }
Colour green() { return Colour(0, 255,0); }
Colour blue() { return Colour(0, 0, 255); }
Colour white() { return Colour(255, 255, 255); }
Colour black() { return Colour(0, 0, 0); }
//bounding the channel wise pixel color between 0 to 255
//bounding the color value, if a value is beyond 255 clamp it to 255, and any value below 0 clamp to 0.
uchar Clamp(int color)
{
if (color < 0) return 0;
if (color >= 255) return 255;
return color;
}
int main(int, char**){
//Create an image object with 500 x 500 resolution.
Image image = Image(500, 500);
//Coordinates of image rectangle
Vec3 llc = Vec3(-1, -1, -1); //**llc - lower left corner
Vec3 urc = Vec3(1, 1, -1); //**urc - upper right corner
int width = urc(0) - llc(0);
int height = urc(1) - llc(1);
Vec2 pixelUV = Vec2((float)width / image.cols, (float)height / image.rows);
/// TODO: define camera position (view point), sphere center, sphere radius (Weightage: 5%)
Vec3 CameraPoint = Vec3(0, 0, 0); //**it is the origin
Vec3 SphereCenter = Vec3(0, 0, -5); //**it is the Sphere Position
float SphereRadius = 2.0;
Vec3 LightSource = Vec3(2.0, 0.0, 3.0); //**
Vec3 ambient = Vec3(0, 0, 0.5); //**
Vec3 diffuse = Vec3(224, 180, 255); //** 0, 255, 100 - green
Vec3 Origin = CameraPoint;
//end
for (int row = 0; row < image.rows; ++row) {
for (int col = 0; col < image.cols; ++col) {
//TODO: Build primary rays
//Find the pixel position (PixelPos) for each row and col and then construct the vector PixelPos-Origin
Vec3 pixelPos = Vec3(llc(0) + pixelUV(0) * (col + 0.5), llc(1) + pixelUV(1) * (row + 0.5), -1);
//create a ray object
Ray r; //**
//Vec3 rayDir = pixelPos - Origin; //**direction of the ray
Vec3 rayDir = r.generateRay(pixelPos); //**pixelPos-Origin
rayDir.normalize(); //**normalize the ray direction vector
//Ray-sphere intersection...(refer to the lecture slides and Section 4.4.1 of the textbook)
float a = rayDir.dot(rayDir);
Vec3 s0_r0 = Origin - SphereCenter; //***s0_r0 - sphere center - ray origin
float b = 2.0 * rayDir.dot(s0_r0);
float c = s0_r0.dot(s0_r0) - pow(SphereRadius, 2);
//compute the discriminant
float discriminant = pow(b, 2) - 4 * a * c;
//if the discriminant is greater than zero
if(discriminant > 0){
//find roots t1 and t2
float t1 = (-b - sqrt((pow(b, 2)) - 4.0 * a * c)) / (2.0 * a); //**
float t2 = (-b + sqrt((pow(b, 2)) - 4.0 * a * c)) / (2.0 * a); //**
//determine which one is the real intersection point
float t;
//Sphere s;
if (t1 < t2 && (t1 > 0 && t2 > 0)) {
t = t1;
//} //should this be after the if-statement below, so that it uses t = t1 and not just float t.????
if (t > 0) {
//Shade the pixel, normal is Intersection - SphereCenter, LightVector is LightSource- Intersection, make sure to normalize the vectors
Vec3 Intersection = Origin + (t * rayDir);
Vec3 Normal = Intersection - SphereCenter; //** normalize
Normal.normalize(); //**
Vec3 LightVector = LightSource - Intersection; //**normalize
LightVector.normalize(); //**
float diffuseTerm = LightVector.dot(Normal);
if (diffuseTerm < 0) diffuseTerm = 0;
Colour colour(0, 0, 0); //The ambient base
colour[0] = Clamp(ambient[0] + diffuse[0] * diffuseTerm);
colour[1] = Clamp(ambient[1] + diffuse[1] * diffuseTerm);
colour[2] = Clamp(ambient[2] + diffuse[2] * diffuseTerm);
image(row, col) = colour;
}
}//
else {
image(row, col) = black();
}
} else {
//No intersection, discriminant < 0
image(row, col) = red(); //**makes blue background colour
}
////**Plane intersection
//create a plane object
Plane plane(Vec3(-5, 0, -4), Vec3(0, 0, -1), black());
//Plane plane;
////if ray hits plane -> color black
//if (plane.findIntersection(rayDir) == 1) {
// image(row, col) = black();
//}
//else {
// image(row, col) = white();
//}
}
}
/// Required outputs: (1) Ray traced image of a sphere (2) Ray traced image when the camera is placed inside the sphere (complete black)
image.save("./result.png");
image.display();
return EXIT_SUCCESS;
}
Errors
enter image description here
#include is a shockingly simple directive. It literally just copy-pastes the content of the file.
main.cpp includes both Ray.h and Plane.h, and Plane.h includes Ray.h, so Ray.h ends up being included twice. That's why the compiler is complaining about a "class redefinition".
You can add #pragma once at the top of all your header files to let the compiler know know to skip the file if it was included already.
N.B. #pragma once is not officially part of the language, but it is supported by all compilers and has a few small advantages over the alternative.

How to implement interactive rotation operations in a decent way

Recently, I want to achieve interactive rotation operations as can be done in meshlab:
Basically, it achieves rotation of three degrees of freedom. I visualize these operations as following codes with the help of GLFW:
static void mouse_move_callback(GLFWwindow* window, double xpos, double ypos){
...
do{
//perform rotation operations only if keeping the right mouse key pressed
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_RELEASE) {
g_clr_right_mouse = true;
break;
}
/*clear mouse state once transferred from release state
to pressed state to prevent from a instant flicker*/
if(g_clr_right_mouse){
g_lastX = xpos;
g_lastY = ypos;
g_clr_right_mouse = false;
}
float xoffset = xpos - g_lastX; //let movement from down to top positive
float yoffset = g_lastY - ypos;
g_lastX = xpos;
g_lastY = ypos;
//do counterclockwise rotation around x-asis with movement in y direction
glm::mat4 r1 = glm::rotate(glm::mat4(), glm::radians(-yoffset * 0.5f), glm::vec3(1.0f,0.0f,0.0f));
//do counterclockwise rotation around y-asis with movement in x direction
glm::mat4 r2 = glm::rotate(glm::mat4(), glm::radians( xoffset * 0.5f), glm::vec3(0.0f,1.0f,0.0f));
glm::mat4 tmp = r2 * r1 * g_model;
for(int i=0; i<3; i++)
g_model[i] = tmp[i];
return ;
}while(false);
}
These codes are located here, and the whole project can be found here which can be downloaded and built. Finally, it performs as follows:
However, my implementation can only achieve rotation operations of 2 DOF, I add a keyboard callback to achieve rotation around the z axis:
void keyboard_callback(GLFWwindow* window, int key, int scancode, int action, int mod){
if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS){
glm::mat4 r3 = glm::rotate(glm::mat4(), glm::radians(3.0f), glm::vec3(0,0,1.0f));
glm::mat4 tmp = r3 * g_model;
for(int i=0; i<3; i++)
g_model[i] = tmp[i];
}else if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS){
glm::mat4 r3 = glm::rotate(glm::mat4(), glm::radians(-3.0f), glm::vec3(0,0,1.0f));
glm::mat4 tmp = r3 * g_model;
for(int i=0; i<3; i++)
g_model[i] = tmp[i];
}
}
So my question is how to decently achieve interactive rotation operations of 3 DOF only with mouse movement?
When dragging the mouse, the object must be rotated around an axis that is perpendicular to the direction of movement of the mouse. The pivot is the origin of the model.
Rotate the mouse movement vector by 90 ° in the XY plane of the view. Since this is a vector in view space, the vector must be transformed from view space into world space. The matrix that transforms a vector from view space to world space is the inverse matrix of the upper left 3x3 of the view matrix:
vec2 drag_start;
vec2 drag_end;
glm::mat3 to_world = glm::inverse(glm::mat3(view_matrix));
glm::vec2 drag_vec = glm::vec2(drag_end.x - drag_start.x, drag_start.y - drag_end.y);
glm::vec3 axis_vec = glm::normalize(to_world * glm::vec3(-drag_vec.y, drag_vec.x, 0));
Create a rotation matrix around the axis. The angle depends on the length of the vector (height is the height of the viewport in pixels):
GLfloat angle = glm::length(drag_vec) / height / 2 * M_PI;
drag_rotation = glm::rotate(glm::mat4(1.0f), angle, axis_vec);
Compute a rotation matrix while dragging the mouse. Concatenate the rotation matrix and the model matrix after the drag ends:
glm::mat4 view_matrix(1.0f);
glm::mat4 model_rotation(1.0f);
glm::mat4 drag_rotation(1.0f);
glm::vec2 drag_start(0.0f);
bool drag = false;
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button != GLFW_MOUSE_BUTTON_LEFT)
return;
if (action == GLFW_PRESS)
{
drag = true;
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
drag_start = glm::vec2(xpos, ypos);
}
else if (action == GLFW_RELEASE)
{
drag = false;
model_rotation = drag_rotation * model_rotation;
drag_rotation = glm::mat4(1.0f);
}
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
if (!drag)
return;
glm::mat3 to_world = glm::inverse(glm::mat3(view_matrix));
glm::vec2 drag_vec = glm::vec2(xpos - drag_start.x, drag_start.y - ypos);
glm::vec3 axis_vec = glm::normalize(to_world * glm::vec3(-drag_vec.y, drag_vec.x, 0));
GLfloat angle = glm::length(drag_vec) / height / 2 * M_PI;
drag_rotation = glm::rotate(glm::mat4(1.0f), angle, axis_vec);
}
The model matrix is the concatenation of drag_rotation and model_rotation:
glm::mat4 model = drag_rotation * model_rotation;
See also Orbit
Complete example:
#include <GL/glew.h>
#include <GL/gl.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <GLFW/glfw3.h>
#include <vector>
#include <string>
#include <stdexcept>
#include <iostream>
#define _USE_MATH_DEFINES
#include <cmath>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
std::string sh_vert = R"(
#version 460 core
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec3 a_uvw;
out vec3 v_uvw;
layout (location = 0) uniform mat4 u_projection;
layout (location = 1) uniform mat4 u_view;
layout (location = 2) uniform mat4 u_model;
void main()
{
v_uvw = a_uvw;
gl_Position = u_projection * u_view * u_model * a_position;
}
)";
std::string sh_frag = R"(
#version 460 core
out vec4 frag_color;
in vec3 v_uvw;
vec3 HUEtoRGB(in float H)
{
float R = abs(H * 6.0 - 3.0) - 1.0;
float G = 2.0 - abs(H * 6.0 - 2.0);
float B = 2.0 - abs(H * 6.0 - 4.0);
return clamp(vec3(R, G, B), 0.0, 1.0);
}
void main()
{
frag_color = vec4(HUEtoRGB(v_uvw.z), 1.0);
}
)";
class ShaderProgram
{
public:
GLuint programObject;
static ShaderProgram newProgram(const std::string& vsh, const std::string& fsh);
private:
GLuint compileShader(const std::string& sourceCode, GLenum shaderType);
void linkProgram(std::vector<GLuint> shObjs);
void compileStatus(GLuint shader);
void linkStatus();
};
class VertexArrayObject
{
public:
GLuint vaoObject = 0;
GLsizei noOfVertices = 0;
GLsizei noOfIndices = 0;
static VertexArrayObject newCube();
static VertexArrayObject newCircles();
static VertexArrayObject newVAO(const std::vector<GLfloat>& varray, const std::vector<GLuint>& iarray);
};
int width = 800, height = 600;
glm::mat4 view_matrix(1.0f);
glm::mat4 model_rotation(1.0f);
glm::mat4 drag_rotation(1.0f);
glm::vec2 drag_start(0.0f);
bool drag = false;
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button != GLFW_MOUSE_BUTTON_LEFT)
return;
if (action == GLFW_PRESS)
{
drag = true;
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
drag_start = glm::vec2(xpos, ypos);
}
else if (action == GLFW_RELEASE)
{
drag = false;
model_rotation = drag_rotation * model_rotation;
drag_rotation = glm::mat4(1.0f);
}
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
if (!drag)
return;
glm::mat3 to_world = glm::inverse(glm::mat3(view_matrix));
glm::vec2 drag_vec = glm::vec2(xpos - drag_start.x, drag_start.y - ypos);
glm::vec3 axis_vec = glm::normalize(to_world * glm::vec3(-drag_vec.y, drag_vec.x, 0));
GLfloat angle = glm::length(drag_vec) / height / 2 * M_PI;
drag_rotation = glm::rotate(glm::mat4(1.0f), angle, axis_vec);
}
int main(void)
{
if (glfwInit() == GLFW_FALSE)
throw std::runtime_error( "error initializing glfw" );
glfwWindowHint(GLFW_SAMPLES, 8);
GLFWwindow * window = glfwCreateWindow(width, height, "OGL window", nullptr, nullptr);
if (window == nullptr)
{
glfwTerminate();
throw std::runtime_error( "error initializing window" );
}
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwMakeContextCurrent(window);
if ( glewInit() != GLEW_OK )
throw std::runtime_error( "error initializing glew" );
auto progam = ShaderProgram::newProgram(sh_vert, sh_frag);
auto cube = VertexArrayObject::newCube();
auto circles = VertexArrayObject::newCircles();
glUseProgram(progam.programObject);
glEnable( GL_DEPTH_TEST );
glClearColor(0.1f, 0.3f, 0.2f, 0.0f);
view_matrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 7.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(view_matrix));
while (!glfwWindowShouldClose(window))
{
glfwGetFramebufferSize(window, &width, &height);
float ascpect = (float)width / (float)height;
glm::mat4 project = glm::perspective(glm::radians(60.0f), ascpect, 0.1f, 20.0f);
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(project));
glm::mat4 model = drag_rotation * model_rotation;
glViewport(0, 0, width, height);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glUniformMatrix4fv(2, 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(cube.vaoObject);
glDrawElements(GL_TRIANGLES, cube.noOfIndices, GL_UNSIGNED_INT, nullptr);
glUniformMatrix4fv(2, 1, GL_FALSE, glm::value_ptr(glm::scale(model, glm::vec3(2.5f))));
glBindVertexArray(circles.vaoObject);
glDrawElements(GL_LINES, circles.noOfIndices, GL_UNSIGNED_INT, nullptr);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
ShaderProgram ShaderProgram::newProgram(const std::string& vsh, const std::string& fsh)
{
ShaderProgram program;
auto shObjs = std::vector<GLuint>
{
program.compileShader(vsh, GL_VERTEX_SHADER),
program.compileShader(fsh, GL_FRAGMENT_SHADER),
};
for (auto shObj : shObjs)
program.compileStatus(shObj);
program.linkProgram(shObjs);
for (auto shObj : shObjs)
glDeleteShader(shObj);
return program;
}
GLuint ShaderProgram::compileShader(const std::string& sourceCode, GLenum shaderType)
{
auto shaderObj = glCreateShader(shaderType);
const char* srcCodePtr = sourceCode.c_str();
glShaderSource(shaderObj, 1, &srcCodePtr, nullptr);
glCompileShader(shaderObj);
return shaderObj;
}
void ShaderProgram::linkProgram(std::vector<GLuint> shObjs)
{
programObject = glCreateProgram();
for (auto shObj : shObjs)
glAttachShader(programObject, shObj);
glLinkProgram(programObject);
linkStatus();
}
void ShaderProgram::compileStatus(GLuint shader)
{
GLint status = GL_TRUE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint logLen;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
std::vector< char >log(logLen);
GLsizei written;
glGetShaderInfoLog(shader, logLen, &written, log.data());
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
}
void ShaderProgram::linkStatus()
{
GLint status = GL_TRUE;
glGetProgramiv(programObject, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint logLen;
glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &logLen);
std::vector< char >log(logLen);
GLsizei written;
glGetProgramInfoLog(programObject, logLen, &written, log.data());
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
}
VertexArrayObject VertexArrayObject::newCube()
{
static const std::vector<GLfloat> vertices{ -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1 };
static const std::vector<GLfloat> uv{ 0, 0, 1, 0, 1, 1, 0, 1 };
static const std::vector<size_t> faces{ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 };
std::vector<GLfloat> varray;
std::vector<GLuint> iarray;
for (auto si = 0; si < faces.size() / 4; si++)
{
for (auto qi = 0; qi < 4; qi++)
{
varray.insert(varray.end(), vertices.begin() + faces[si * 4 + qi] * 3, vertices.begin() + faces[si * 4 + qi] * 3 + 3);
std::vector<GLfloat> uvw{ 0, 0, (GLfloat)si * 4.0f / (GLfloat)faces.size() };
varray.insert(varray.end(), uvw.begin(), uvw.end());
}
std::vector<GLuint> indices{ 4u * si, 4u * si + 1, 4u * si + 2, 4u * si, 4u * si + 2, 4u * si + 3 };
iarray.insert(iarray.end(), indices.begin(), indices.end());
}
return newVAO(varray, iarray);
}
VertexArrayObject VertexArrayObject::newCircles()
{
const GLuint noC = 360;
std::vector<GLfloat> varray;
std::vector<GLuint> iarray;
for (int i = 0; i <= noC; i++)
{
GLfloat angle = static_cast<GLfloat>(i * 2 * M_PI / noC);
GLfloat c = cos(angle), s = sin(angle);
std::vector<GLfloat> va{ 0, c, s, 0, 0, 0, s, 0, c, 0, 0, 1.0f / 3.0f, c, s, 0, 0, 0, 2.0f / 3.0f };
varray.insert(varray.end(), va.begin(), va.end());
}
for (GLuint ci = 0; ci < 3; ci++)
{
for (GLuint i = 0; i <= noC; i++)
{
std::vector<GLuint> ia{ i * 3 + ci, ((i + 1) % noC) * 3 + ci };
iarray.insert(iarray.end(), ia.begin(), ia.end());
}
}
return newVAO(varray, iarray);
}
VertexArrayObject VertexArrayObject::newVAO(const std::vector<GLfloat>& varray, const std::vector<GLuint>& iarray)
{
VertexArrayObject vao;
vao.noOfIndices = static_cast<GLsizei>(iarray.size());
vao.noOfVertices = static_cast<GLsizei>(varray.size() / 6);
GLuint bufferObjects[2];
glGenBuffers(2, bufferObjects);;
glGenVertexArrays(1, &vao.vaoObject);
glBindVertexArray(vao.vaoObject);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[0]);
glBufferData(GL_ARRAY_BUFFER, varray.size() * sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*varray.data()), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*varray.data()), (void*)(3 * sizeof(*varray.data())));
if (vao.noOfIndices > 0)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iarray.size() * sizeof(*iarray.data()), iarray.data(), GL_STATIC_DRAW);
}
glBindVertexArray(0);
glDeleteBuffers(2, bufferObjects);
return vao;
}
You kinda need to draw the ball to make it intuitive.
On mouse down, you put an anchor on the ball directly under the mouse pointer. If the click is outside the ball, then you use the closest point on the ball.
As the mouse moves, you rotate the ball so that the anchor point follows the shortest path so that it remains directly under the mouse pointer. If the mouse pointer is off the ball, then the closest point on the ball is used.
Maybe this will help.

How can I rotate triangle in OpenGL?

I want to rotate my triangle in OpenGL, and running program on Raspberry Pi.
I can draw triangle and move it.
But I have no idea to rotate it..
Nothing rotates.
#include <cstdio>
#include <ctime>
#include <cmath>
#include <string>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES/gl.h>
#include <bcm_host.h>
EGLDisplay Disp;
EGLSurface Surface;
EGLContext Context;
int ScrWidth, ScrHeight;
float MVPMatrix[16];
float ProjectionMatrix[16];
float ViewMatrix[16];
using namespace std;
class Shader
{
private:
string VertexShaderFile;
string FragmentShaderFile;
GLuint Load(GLenum type, string FileName)
{
(Compile shader)
}
GLuint Program;
bool Linked;
public:
Shader(string FileNameV, string FileNameF)
{
Linked = false;
VertexShaderFile = FileNameV;
FragmentShaderFile = FileNameF;
}
bool Load()
{
(Link vertex/fragment shader)
}
void Use()
{
glUseProgram(Program);
}
int GetAttrLoc(const char *Name)
{
glGetAttribLocation(Program, Name);
}
int GetUniformLoc(const char *Name)
{
return glGetUniformLocation(Program, Name);
}
~Shader()
{
if(Linked)
{
Linked = false;
glDeleteProgram(Program);
}
}
};
class Triangle
{
private:
const int COORDS_PER_VERTEX = 3;
const int vertexCount = 9 / COORDS_PER_VERTEX; //9: Length of triangleCoords
const int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
static float TriangleCoords [];
float Color[4];
float XOff;
float YOff;
float ZOff;
Shader *S;
public:
Triangle()
{
XOff = YOff = ZOff = 0;
S = new Shader("Shaders/test.vsh", "Shaders/test.fsh");
if (!S->Load())
{
delete S;
S = NULL;
}
}
void SetColor(int R, int G, int B, int A)
{
Color[0] = R / 255.0;
Color[1] = G / 255.0;
Color[2] = B / 255.0;
Color[3] = A / 255.0;
}
void SetXYZ(int X, int Y, int Z)
{
(Sets position)
}
bool Draw()
{
float TriangleCoords[] = { // in counterclockwise order:
-0.0 + XOff, 0.622008459 + YOff, 0.0 + ZOff, // top
-0.5 + XOff, -0.311004243 + YOff, 0.0 + ZOff, // bottom left
0.5 + XOff, -0.311004243 + YOff, 0.0 + ZOff // bottom right
};
printf("%f\n", TriangleCoords[1]);
//glMatrixMode(GL_PROJECTION);
if (S == NULL)
return false;
S->Use();
// Load the vertex data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, TriangleCoords);
// get handle to shape's transformation matrix
// Pass the projection and view transformation to the shader
//UniformMatrix4fv(S->GetUniformLoc("uMVPMatrix"), 1, false, MVPMatrix);
glUniform4fv(S->GetUniformLoc("vColor"), 1, Color);
glEnableVertexAttribArray(0);
//glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
float X = LocalTime->tm_hour / 23.0;
float Y = LocalTime->tm_min / 59.0;
float Z = LocalTime->tm_sec / 59.0;
glTranslatef(0, 0, 1);
glRotatef(60, 1.f, 0.f, 0.f);
glRotatef(30, 0.f, 1.f, 0.f);
glRotatef(30, 0.f, 0.f, 1.f);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glPopMatrix();
return true;
}
};
bool InitDisplay()
{
bcm_host_init();
Disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(eglInitialize(Disp, NULL, NULL) != EGL_TRUE)
{
printf("Display initialize error.\n");
return false;
}
printf("Display initialized.\n");
static const EGLint AttrList[] =
{
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
EGLConfig Config;
int ConfigCount;
if(eglChooseConfig(Disp, AttrList, &Config, 1, &ConfigCount) != EGL_TRUE)
{
printf("Display choose config error.\n");
return false;
}
printf("Display config chosen. %d configs.\n", ConfigCount);
//if(eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE)
//{
// printf("Bind API error.\n");
// return false;
//}
//printf("API bound.\n");
static const EGLint ContextAttr[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
if((Context = eglCreateContext(Disp, Config, EGL_NO_CONTEXT, ContextAttr)) == EGL_NO_CONTEXT)
{
printf("Create context error.\n");
return false;
}
printf("Context created.\n");
if(graphics_get_display_size(0 /* LCD */, &ScrWidth, &ScrHeight) < 0)
{
printf("Get screen size error.\n");
return false;
}
printf("Got screen size. %dx%d\n", ScrWidth, ScrHeight);
DISPMANX_DISPLAY_HANDLE_T DispmanDisp;
DispmanDisp = vc_dispmanx_display_open(0 /* LCD */);
printf("Dispmanx - Display opened.\n");
DISPMANX_UPDATE_HANDLE_T DispmanUpdate;
DispmanUpdate = vc_dispmanx_update_start(0);
printf("Dispmanx - Update started.\n");
DISPMANX_ELEMENT_HANDLE_T DispmanElement;
VC_RECT_T DestRect;
VC_RECT_T SrcRect;
DestRect.x = 0;
DestRect.y = 0;
DestRect.width = ScrWidth;
DestRect.height = ScrHeight;
SrcRect.x = 0;
SrcRect.y = 0;
SrcRect.width = ScrWidth << 16;
SrcRect.height = ScrHeight << 16;
DispmanElement= vc_dispmanx_element_add(
DispmanUpdate,
DispmanDisp,
0/*layer*/,
&DestRect,
0/*src*/,
&SrcRect,
DISPMANX_PROTECTION_NONE,
0 /*alpha*/,
0/*clamp*/,
0/*transform*/
);
printf("Dispmanx - Element added.\n");
static EGL_DISPMANX_WINDOW_T NativeWindow;
NativeWindow.element = DispmanElement;
NativeWindow.width = ScrWidth;
NativeWindow.height = ScrHeight;
vc_dispmanx_update_submit_sync(DispmanUpdate);
printf("Dispmanx - Sync submited.\n");
if((Surface = eglCreateWindowSurface(Disp, Config, &NativeWindow, NULL)) == EGL_NO_SURFACE)
{
printf("Create surface error.\n");
return false;
}
printf("Surface created\n");
if(eglMakeCurrent(Disp, Surface, Surface, Context) != EGL_TRUE)
{
printf("Make onnection between context and surface error.\n");
return false;
}
printf("Connection made between context and surface.\n");
glEnable(GL_CULL_FACE);
glMatrixMode(GL_MODELVIEW);
printf("Graphics system ready.\n");
return true;
}
void makeFrustum(float fovY, float aspectRatio, float front, float back)
{
const float DEG2RAD = 3.14159265 / 180;
float tangent = tan(fovY / 2 * DEG2RAD); // tangent of half fovY
float height = front * tangent; // half height of near plane
float width = height * aspectRatio; // half width of near plane
// params: left, right, bottom, top, near, far
glFrustumf(-width, width, -height, height, front, back);
}
void DrawLoop()
{
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glViewport(0, 0, ScrWidth, ScrHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
makeFrustum(45.0, ScrWidth / (float)ScrHeight, 1, 500);
glEnableClientState(GL_VERTEX_ARRAY);
Triangle T1;
Triangle T2;
Triangle T3;
Triangle T4;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.f, 0.f, -50.f);
while (1)
{
time_t Time;
time(&Time);
tm *LocalTime = localtime(&Time);
printf("%d:%d:%d\n", LocalTime->tm_hour, LocalTime->tm_min, LocalTime->tm_sec);
float R = LocalTime->tm_hour / 23.0;
float G = LocalTime->tm_min / 59.0;
float B = LocalTime->tm_sec / 59.0;
T1.SetColor(255, 0, 0, 255);
T1.SetXYZ(B * ScrWidth, B * ScrHeight, 0);
//glClearColor(0, 0, 0, 1.0);
//glClear(GL_COLOR_BUFFER_BIT);
if (!T1.Draw() || !T2.Draw() || !T3.Draw() || !T4.Draw())
{
return;
}
glFlush();
eglSwapBuffers(Disp, Surface);
}
}
int main()
{
if(!InitDisplay())
{
printf("Display initialize error.\n");
return false;
}
DrawLoop();
return 0;
}
What should I do to rotate triangle?
I referenced working code, but it still doesn't rotates.
You're trying to use OpenGL ES 1.1 functions (glLoadIdentity, glMatrixMode, glTranslatef, glRotatef, etc) in an OpenGL ES 2.0 context - this won't work. You can use either OpenGL ES 1.1 OR OpenGL ES 2.0, but you can't use both at the same time from the same context.
I would suggest sticking with OpenGL ES 2.0 using shaders, and learning the OpenGL ES 2.0 way of doing things, as this is how all of the newer APIs work.
To do translation and rotation you need to encode it into an MVP matrix and pass this as a uniform to the vertex shader which you use when calculating gl_Position. Some examples here:
https://open.gl/transformations

Why my sphere texture broken? [Directx 9, c++]

I use D3DXCreateSphere() function create my sphere, and I want to get vertex position to calculate texture coordinate,but it broken!!! I need help~
front side
back side
This is my struct vertex:
struct _VERTEX { D3DXVECTOR3 norm; float tu; float tv; };
This is my code:
LPD3DXMESH mesh;
LPD3DXMESH texMesh;
D3DXCreateSphere(pDev, 3, 20, 20, &mesh, NULL);
mesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, FVF_VERTEX, pDev, &texMesh);
mesh->Release();
_VERTEX *pVerts;
if (SUCCEEDED(texMesh->LockVertexBuffer(0, (void **)&pVerts))) {
int numVerts = texMesh->GetNumVertices();
for (int i = 0; i<numVerts; i++) {
pVerts->tu = asinf(pVerts->norm.x) / D3DX_PI + 0.5f;
pVerts->tv = asinf(pVerts->norm.y) / D3DX_PI + 0.5f;
pVerts++;
}
texMesh->UnlockVertexBuffer();
}
pDev->SetTexture(0, texture);
texMesh->DrawSubset(0);