代码
test.cpp
#include "SimpleGL/glad.c"
#include <GLFW/glfw3.h>
#include <iostream>
#include "SimpleGL/Program.hpp"
#include "SimpleGL/Camera.hpp"
#include "SimpleGL/Buffer.hpp"
#include "SimpleGL/Error.hpp"
#include "SimpleGL/Sprite.hpp"
using namespace glm;
using namespace SimpleGL;
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
bool isFirst;
vec2 lastPos;
float deltaTime;
float lastTime;
Camera camera;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* videomode = glfwGetVideoMode(monitor);
int width = videomode->width;
int height = videomode->height;
GLFWwindow* window = glfwCreateWindow(width, height, "LearnOpenGL", monitor, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
lastTime = glfwGetTime();
isFirst = true;
glEnable(GL_DEPTH_TEST);
camera.m_projection.loadPerspective(120.0f, 1.0f, 0.1f, 100.0f);
Sprite triangle = Sprite(vec3(0, 0, -1));
Program program;
VertexBufferObject vbo1;
VertexBufferObject vbo2;
VertexArrayObject vao;
ElementBufferObject ebo;
bool success = program.loadProgramFromFile("shader/usual.vert", "shader/usual.frag");
string error = program.getError();
cout << (success ? "program no error" : error) << endl;
GLfloat ver[] =
{
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-100.0f, 0.0f, 0.0f,
100.0f, 0.0f, 0.0f,
0.0f, -100.0f, 0.0f,
0.0f, 100.0f, 0.0f,
0.0f, 0.0f, -100.0f,
0.0f, 0.0f, 100.0f
};
GLfloat col[] =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f
};
GLuint ind[] =
{
0, 1, 2,
};
vao.bindVertexArray();
GLuint v1 = vbo1.getBufferID();
GLuint v2 = vbo2.getBufferID();
GLuint v3 = ebo.getBufferID();
GLenum type = GL_ARRAY_BUFFER;
glBindBuffer(type, v1);
glBufferData(type, sizeof(ver), ver, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(type, v2);
glBufferData(type, sizeof(col), col, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v3);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ind), ind, GL_STATIC_DRAW);
vao.bindVertexArray0();
vbo1.bindBuffer0();
ebo.bindBuffer0();
vbo1.deleteBuffer();
vbo2.deleteBuffer();
ebo.deleteBuffer();
while (!glfwWindowShouldClose(window))
{
processInput(window);
float nowTime = glfwGetTime();
deltaTime = nowTime - lastTime;
lastTime = nowTime;
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program.useProgram();
program.setUniform("u_projection", camera.m_projection.matrix);
program.setUniform("u_view", camera.m_view.matrix);
vao.bindVertexArray();
program.setUniform("u_model", triangle.matrix);
glDrawArrays(GL_TRIANGLES, 0, 3);
program.setUniform("u_model", glm::mat4(1.0f));
glDrawArrays(GL_LINES, 3, 6);
vao.bindVertexArray0();
triangle.rotate(vec3(0, 1, 0));
glfwSwapBuffers(window);
glfwPollEvents();
}
program.deleteProgram();
vao.deleteVertexArray();
glfwTerminate();
return 0;
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
else if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.processMove(Camera::Direction::FORWARD, deltaTime);
else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.processMove(Camera::Direction::BACKWARD, deltaTime);
else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.processMove(Camera::Direction::LEFT, deltaTime);
else if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.processMove(Camera::Direction::RIGHT, deltaTime);
else if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)
camera.processMove(Camera::Direction::UP, deltaTime);
else if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
camera.processMove(Camera::Direction::DOWN, deltaTime);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
camera.m_projection.loadPerspective(120.0f, float(width) / float(height), 0.1f, 100.0f);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
vec2 nowPos = vec2(xpos, ypos);
if (isFirst)
{
lastPos = nowPos;
isFirst = false;
}
vec2 delta = nowPos - lastPos;
lastPos = nowPos;
camera.processRotate(-delta.x, delta.y);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.processZoom(yoffset);
}
Sprite.hpp
#ifndef __SIMPLEGL_SPRITE_HPP__
#define __SIMPLEGL_SPRITE_HPP__
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace SimpleGL
{
class Sprite
{
public:
glm::vec3 m_position;
glm::vec3 m_rotate;
glm::vec3 m_scale;
glm::mat4 matrix;
public:
Sprite(glm::vec3 position = glm::vec3(0.0f), glm::vec3 rotate = glm::vec3(0.0f), glm::vec3 scale = glm::vec3(1.0f)) : m_position(position), m_rotate(rotate), m_scale(scale), matrix(glm::mat4(1.0f))
{
generateMatrix();
}
public:
Sprite& setPosition(glm::vec3 position)
{
m_position = position;
generateMatrix();
return *this;
}
Sprite& setRotate(glm::vec3 angle)
{
m_rotate = angle;
generateMatrix();
return *this;
}
Sprite& setScale(glm::vec3 size)
{
m_scale = size;
generateMatrix();
return *this;
}
Sprite& translate(glm::vec3 distance)
{
m_position += distance;
generateMatrix();
return *this;
}
Sprite& rotate(glm::vec3 angle)
{
m_rotate += angle;
generateMatrix();
return *this;
}
Sprite& scale(glm::vec3 size)
{
m_scale += size;
generateMatrix();
return *this;
}
glm::mat4 generateMatrix()
{
matrix = glm::mat4(1.0f);
matrix = glm::translate(matrix, m_position);
matrix = glm::rotate(matrix, glm::radians(m_rotate.x), glm::vec3(1.0f, 0.0f, 0.0f));
matrix = glm::rotate(matrix, glm::radians(m_rotate.y), glm::vec3(0.0f, 1.0f, 0.0f));
matrix = glm::rotate(matrix, glm::radians(m_rotate.z), glm::vec3(0.0f, 0.0f, 1.0f));
matrix = glm::scale(matrix, m_scale);
return matrix;
}
};
}
#endif
Program.hpp
#ifndef __SIMPLEGL_SHADER_HPP__
#define __SIMPLEGL_SHADER_HPP__
#include <iostream>
#include <sstream>
#include <fstream>
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Error.hpp"
#include "glad.c"
namespace SimpleGL
{
class Program
{
private:
GLuint m_programID;
std::stringstream m_error;
public:
static std::string check(GLuint ID, GLenum type)
{
GLint success;
std::string result = std::string();
if (type != GL_PROGRAM)
{
glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
if (!success)
{
GLchar* infoLog = NULL;
GLint length;
glGetShaderiv(ID, GL_INFO_LOG_LENGTH, &length);
infoLog = new char[length];
glGetShaderInfoLog(ID, length, NULL, infoLog);
result = std::string("Shader Error:\n") + std::string(infoLog);
delete[] infoLog;
}
}
else
{
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success)
{
GLchar* infoLog = NULL;
GLint length;
glGetProgramiv(ID, GL_INFO_LOG_LENGTH, &length);
infoLog = new char[length];
glGetProgramInfoLog(ID, length, NULL, infoLog);
result = std::string("Program Error:\n") + std::string(infoLog);
delete[] infoLog;
}
}
return result;
}
static GLuint createShader(std::string source, GLenum type, std::stringstream* error_output = nullptr)
{
const GLchar* s = source.data();
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &s, NULL);
glCompileShader(shader);
std::string error = check(shader, type);
if (error_output != nullptr)
{
if (error.length() > 0)
{
*error_output << error << std::endl;
}
}
if (error.length() > 0)
{
glDeleteShader(shader);
return 0;
}
return shader;
}
static GLuint createProgram(std::string vSource, std::string fSource, std::string gSource, std::stringstream* error_output = nullptr)
{
GLuint vShader = createShader(vSource, GL_VERTEX_SHADER, error_output);
GLuint fShader = createShader(fSource, GL_FRAGMENT_SHADER, error_output);
GLuint gShader = 0;
if (gSource.length() > 0)
{
gShader = createShader(gSource, GL_GEOMETRY_SHADER, error_output);
}
GLuint program = glCreateProgram();
glAttachShader(program, vShader);
glAttachShader(program, fShader);
if (gShader != 0)
{
glAttachShader(program, gShader);
}
glLinkProgram(program);
std::string error = check(program, GL_PROGRAM);
glDeleteShader(vShader);
glDeleteShader(fShader);
if (gShader != 0)
{
glDeleteShader(gShader);
}
if (error_output != nullptr)
{
if (error.length() > 0)
{
*error_output << error << std::endl;
}
}
if (error.length() > 0)
{
glDeleteProgram(program);
return 0;
}
return program;
}
static std::string loadStringFromStream(std::istream& is)
{
std::stringstream result;
std::string temp;
while (getline(is, temp))
{
result << temp << std::endl;
}
return result.str();
}
public:
Program()
{
m_programID = 0;
}
Program(std::string vSource, std::string fSource, std::string gSource = "")
{
m_programID = createProgram(vSource, fSource, gSource, &m_error);
}
bool loadProgram(std::string vSource, std::string fSource, std::string gSource = "")
{
GLuint id = createProgram(vSource, fSource, gSource, &m_error);
if (id == 0)
{
return false;
}
m_programID = id;
return true;
}
bool loadProgramFromStream(std::istream& vStream, std::istream& fStream)
{
std::string vSource = loadStringFromStream(vStream);
std::string fSource = loadStringFromStream(fStream);
return loadProgram(vSource, fSource);
}
bool loadProgramFromStream(std::istream& vStream, std::istream& fStream, std::istream& gStream)
{
std::string vSource = loadStringFromStream(vStream);
std::string fSource = loadStringFromStream(fStream);
std::string gSource = loadStringFromStream(gStream);
return loadProgram(vSource, fSource, gSource);
}
bool loadProgramFromFile(std::string vPath, std::string fPath)
{
std::ifstream vStream;
std::ifstream fStream;
bool result;
try
{
vStream.open(vPath);
fStream.open(fPath);
if (!vStream.is_open())
{
return false;
}
if (!fStream.is_open())
{
return false;
}
result = loadProgramFromStream(vStream, fStream);
vStream.close();
fStream.close();
}
catch (std::ifstream::failure& e)
{
result = false;
m_error << "File Error:\n" << e.what() << std::endl;
}
return result;
}
bool loadProgramFromFile(std::string vPath, std::string fPath, std::string gPath)
{
std::ifstream vStream;
std::ifstream fStream;
std::ifstream gStream;
bool result;
try
{
vStream.open(vPath);
fStream.open(fPath);
gStream.open(gPath);
if (!vStream.is_open())
{
return false;
}
if (!fStream.is_open())
{
return false;
}
if (!gStream.is_open())
{
return false;
}
result = loadProgramFromStream(vStream, fStream, gStream);
vStream.close();
fStream.close();
gStream.close();
}
catch (std::ifstream::failure& e)
{
result = false;
m_error << "File Error:\n" << e.what() << std::endl;
}
return result;
}
bool compile()
{
clearError();
glLinkProgram(m_programID);
std::string error = check(m_programID, GL_PROGRAM);
if (error.length() > 0)
{
m_error << error << std::endl;
return false;
}
return true;
}
Program& deleteProgram()
{
if (m_programID != 0)
{
glDeleteProgram(m_programID);
}
return *this;
}
Program& setProgramID(GLuint ID)
{
m_programID = ID;
return *this;
}
GLuint getProgramID()
{
return m_programID;
}
Program& useProgram()
{
if (m_programID != 0)
{
glUseProgram(m_programID);
}
return *this;
}
std::string getError()
{
return m_error.str();
}
Program& clearError()
{
m_error.str("");
return *this;
}
public:
GLuint getUniformLocation(std::string name)
{
if (m_programID != 0)
{
return glGetUniformLocation(m_programID, name.data());
}
return -1;
}
Program& setUniform(std::string name, glm::mat4 matrix, GLenum transpose = GL_FALSE)
{
if (m_programID != 0)
{
GLuint location = getUniformLocation(name);
glUniformMatrix4fv(location, 1, transpose, glm::value_ptr(matrix));
}
return *this;
}
};
class ComputeProgram
{
private:
GLuint m_programID;
public:
static std::string check(GLuint program)
{
return std::string();
}
static GLuint create(std::string cSource)
{
return 0;
}
public:
ComputeProgram() {}
};
}
#endif
Buffer.hpp
#ifndef __SIMPLEGL_BUFFER_HPP__
#define __SIMPLEGL_BUFFER_HPP__
#include "glad.c"
namespace SimpleGL
{
class BufferObject
{
private:
GLuint m_bufferObjectID;
GLenum m_type;
public:
static void bindBuffer0(GLenum type)
{
glBindBuffer(type, 0);
}
public:
BufferObject(GLenum type)
{
m_type = type;
genBuffer();
bindBuffer();
bindBuffer0();
}
~BufferObject()
{
deleteBuffer();
}
public:
BufferObject& setBufferID(GLuint id)
{
m_bufferObjectID = id;
return *this;
}
GLuint getBufferID()
{
return m_bufferObjectID;
}
const GLuint getBufferType()
{
return m_type;
}
public:
bool genBuffer()
{
deleteBuffer();
glGenBuffers(1, &m_bufferObjectID);
return (m_bufferObjectID != 0);
}
BufferObject& bindBuffer()
{
if (m_bufferObjectID != 0)
{
glBindBuffer(m_type, m_bufferObjectID);
}
return *this;
}
BufferObject& bindBuffer0()
{
bindBuffer0(m_type);
return *this;
}
BufferObject& deleteBuffer()
{
if (m_bufferObjectID != 0)
{
glDeleteBuffers(1, &m_bufferObjectID);
m_bufferObjectID = 0;
}
return *this;
}
};
class VertexBufferObject : public BufferObject
{
public:
VertexBufferObject() : BufferObject(GL_ARRAY_BUFFER) {}
};
class ElementBufferObject : public BufferObject
{
public:
ElementBufferObject() : BufferObject(GL_ELEMENT_ARRAY_BUFFER) {}
};
class VertexArrayObject
{
private:
GLuint m_VertexArrayObjectID;
public:
static void bindVertexArray0()
{
glBindVertexArray(0);
}
public:
VertexArrayObject()
{
genVertexArray();
bindVertexArray();
bindVertexArray0();
}
~VertexArrayObject()
{
deleteVertexArray();
}
public:
VertexArrayObject& setVertexArrayObjectID(GLuint id)
{
m_VertexArrayObjectID = id;
return *this;
}
GLuint getVertexArrayObjectID()
{
return m_VertexArrayObjectID;
}
public:
bool genVertexArray()
{
deleteVertexArray();
glGenVertexArrays(1, &m_VertexArrayObjectID);
return (m_VertexArrayObjectID != 0);
}
VertexArrayObject& bindVertexArray()
{
if (m_VertexArrayObjectID != 0)
{
glBindVertexArray(m_VertexArrayObjectID);
}
return *this;
}
VertexArrayObject& deleteVertexArray()
{
if (m_VertexArrayObjectID != 0)
{
glDeleteVertexArrays(1, &m_VertexArrayObjectID);
m_VertexArrayObjectID = 0;
}
return *this;
}
};
}
#endif
Error.hpp
#ifndef __SIMPLEGL_ERROR_HPP__
#define __SIMPLEGL_ERROR_HPP__
#include "glad.c"
#include <sstream>
namespace SimpleGL
{
class Error
{
public:
static std::string parse(GLenum error)
{
std::string result;
switch (error)
{
case GL_NO_ERROR:
result = "GL_NO_ERROR";
break;
case GL_INVALID_ENUM:
result = "GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
result = "GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
result = "GL_INVALID_OPERATION";
break;
case GL_STACK_OVERFLOW:
result = "GL_STACK_OVERFLOW";
break;
case GL_STACK_UNDERFLOW:
result = "GL_STACK_UNDERFLOW";
break;
case GL_OUT_OF_MEMORY:
result = "GL_OUT_OF_MEMORY";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
result = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
default:
result = "Unknow";
break;
}
return result;
}
static std::string describe(GLenum error)
{
std::string result;
switch (error)
{
case GL_NO_ERROR:
result = "No user error reported since the last call to glGetError.";
break;
case GL_INVALID_ENUM:
result = "Set when an enumeration parameter is not legal.";
break;
case GL_INVALID_VALUE:
result = "Set when a value parameter is not legal.";
break;
case GL_INVALID_OPERATION:
result = "Set when the state for a command is not legal for its given parameters.";
break;
case GL_STACK_OVERFLOW:
result = "Set when a stack pushing operation causes a stack overflow.";
break;
case GL_STACK_UNDERFLOW:
result = "Set when a stack popping operation occurs while the stack is at its lowest point.";
break;
case GL_OUT_OF_MEMORY:
result = "Set when a memory allocation operation cannot allocate (enough) memory.";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
result = "Set when reading or writing to a framebuffer that is not complete.";
break;
default:
result = "Unknown enumeration.";
break;
}
return result;
}
public:
GLenum error;
std::string value;
std::string description;
public:
Error()
{
error = glGetError();
value = parse(error);
description = describe(error);
}
Error& operator()()
{
error = glGetError();
value = parse(error);
description = describe(error);
return *this;
}
std::string toString()
{
std::stringstream sstr;
sstr << "Error Number:" << error << std::endl
<< "Error String:" << value << std::endl
<< "Error Description:" << description << std::endl;
return sstr.str();
}
};
}
#endif
Camera.hpp
#ifndef __SIMPLEGL_CAMERA_HPP__
#define __SIMPLEGL_CAMERA_HPP__
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace SimpleGL
{
class Camera
{
public:
enum Direction
{
FORWARD,
BACKWARD,
LEFT,
RIGHT,
UP,
DOWN
};
class View
{
public:
glm::vec3 position;
glm::vec3 front;
glm::vec3 center;
glm::vec3 up;
glm::vec3 right;
float yaw;
float pitch;
glm::mat4 matrix;
public:
View(glm::vec3 po = glm::vec3(0.0f), float y = 0, float pi = 0, glm::vec3 u = glm::vec3(0.0f, 1.0f, 0.0f))
{
position = po;
yaw = y;
pitch = pi;
up = u;
update();
}
public:
View& update()
{
if (pitch > 89.9f)
pitch = 89.9f;
if (pitch < -89.9f)
pitch = -89.9f;
float h = glm::cos(glm::radians(pitch));
float x = glm::cos(glm::radians(yaw)) * h;
float y = glm::sin(glm::radians(pitch));
float z = glm::sin(glm::radians(yaw)) * h;
front = glm::normalize(glm::vec3(x, y, z));
center = position + front;
right = glm::normalize(glm::cross(front, up));
return *this;
}
glm::mat4 generateMatrix()
{
matrix = glm::lookAt(position, center, up);
return matrix;
}
};
class Projection
{
public:
float fovy;
float aspect;
float near_plane;
float far_plane;
glm::mat4 matrix;
public:
Projection() : fovy(120.0f), aspect(1.0f), near_plane(0.1f), far_plane(100.0f), matrix(glm::mat4(1.0f)) {}
Projection(float n, float f) : near_plane(n), far_plane(f), matrix(glm::mat4(1.0f)) {}
glm::mat4 loadPerspective(float f, float a, float ne, float fa)
{
fovy = f;
aspect = a;
near_plane = ne;
far_plane = fa;
return loadPerspective();
}
glm::mat4 loadPerspective()
{
matrix = glm::perspective(fovy, aspect, near_plane, far_plane);
return matrix;
};
};
public:
View m_view;
Projection m_projection;
float m_sensitivity;
float m_movement_speed;
public:
Camera(View view = View(), Projection projection = Projection()) : m_view(view), m_projection(projection), m_sensitivity(0.05f), m_movement_speed(1) {}
Camera& processMove(Direction direction, float deltaTime)
{
float distance = m_movement_speed * deltaTime;
if (direction == Direction::FORWARD)
m_view.position += m_view.front * distance;
else if (direction == Direction::BACKWARD)
m_view.position += (-m_view.front) * distance;
else if (direction == Direction::LEFT)
m_view.position += (-m_view.right) * distance;
else if (direction == Direction::RIGHT)
m_view.position += m_view.right * distance;
else if (direction == Direction::UP)
m_view.position += m_view.up * distance;
else if (direction == Direction::DOWN)
m_view.position += (-m_view.up) * distance;
m_view.update();
m_view.generateMatrix();
return *this;
}
Camera& processRotate(float dx, float dy)
{
m_view.yaw += dx * m_sensitivity;
m_view.pitch += dy * m_sensitivity;
m_view.update();
m_view.generateMatrix();
return *this;
}
Camera& processZoom(float offset)
{
m_projection.fovy += offset;
if (m_projection.fovy < 1.0f)
m_projection.fovy = 1.0f;
else if (m_projection.fovy > 179.9f)
m_projection.fovy = 179.9f;
m_projection.loadPerspective();
return *this;
}
};
}
#endif
运行结果
|