vectozavr-shooter/engine/Camera.cpp

235 lines
8.3 KiB
C++
Executable File

//
// Created by Иван Ильин on 14.01.2021.
//
#include "Camera.h"
#include "utils/Log.h"
std::vector<Triangle> &Camera::project(std::shared_ptr<Mesh> mesh) {
if(!_ready) {
Log::log("Camera::project(): cannot project _tris without camera initialization ( Camera::init() ) ");
return this->_triangles;
}
if(!mesh->isVisible())
return this->_triangles;
// Model transform matrix: translate _tris in the origin of body.
Matrix4x4 M = Matrix4x4::Translation(mesh->position());
Matrix4x4 VM = _V * M;
// We don't want to waste time re-allocating memory every time
std::vector<Triangle> clippedTriangles, tempBuffer;
for(auto& t : mesh->triangles()) {
double dot = t.norm().dot((mesh->position() + t[0] - _position).normalized());
if(dot > 0)
continue;
Triangle clipped[2];
// It needs to be cleared because it's reused through iterations. Usually it doesn't free memory.
clippedTriangles.clear();
// In the beginning we need to to translate drawTriangle from world coordinate to our camera system:
// After that we apply clipping for all planes from _clipPlanes
clippedTriangles.emplace_back(t * VM);
for(auto& plane : _clipPlanes)
{
while(!clippedTriangles.empty())
{
clipped[0] = clippedTriangles.back();
clipped[1] = clipped[0];
clippedTriangles.pop_back();
int additional = plane.clip(clipped[0], clipped[1]);
for(int i = 0; i < additional; i++)
tempBuffer.emplace_back(clipped[i]);
}
clippedTriangles.swap(tempBuffer);
}
for(auto& clippedTriangle : clippedTriangles) {
sf::Color color = clippedTriangle.color();
sf::Color ambientColor = sf::Color(color.r * (0.3 * std::abs(dot) + 0.7),
color.g * (0.3 * std::abs(dot) + 0.7),
color.b * (0.3 * std::abs(dot) + 0.7),
color.a);
// Finally its time to project our clipped colored drawTriangle from 3D -> 2D
// and transform it's coordinate to screen space (in pixels):
clippedTriangle = clippedTriangle * _SP;
clippedTriangle = Triangle(clippedTriangle[0] / clippedTriangle[0].w(),
clippedTriangle[1] / clippedTriangle[1].w(),
clippedTriangle[2] / clippedTriangle[2].w(),
ambientColor);
_triangles.emplace_back(clippedTriangle);
}
}
return this->_triangles;
}
void Camera::init(int width, int height, double fov, double ZNear, double ZFar) {
// We need to init camera only after creation or changing width, height, fov, ZNear or ZFar.
// Because here we calculate matrix that does not change during the motion of _objects or camera
_w = width; _h = height;
_aspect = (double)width / (double)height;
_P = Matrix4x4::Projection(fov, _aspect, ZNear, ZFar);
_S = Matrix4x4::ScreenSpace(width, height);
_SP = _S * _P; // screen-space-projections matrix
// This is planes for clipping _tris.
// Motivation: we are not interest in _tris that we cannot see.
_clipPlanes.emplace_back(Plane(Point4D{0, 0, 1}, Point4D{0, 0, ZNear})); // near plane
_clipPlanes.emplace_back(Plane(Point4D{0, 0, -1}, Point4D{0, 0, ZFar})); // far plane
double thetta1 = M_PI*fov*0.5/180.0;
double thetta2 = atan(_aspect * tan(thetta1));
_clipPlanes.emplace_back(Plane(Point4D{-cos(thetta2), 0, sin(thetta2)}, Point4D{0, 0, 0})); // left plane
_clipPlanes.emplace_back(Plane(Point4D{cos(thetta2), 0, sin(thetta2)}, Point4D{0, 0, 0})); // right plane
_clipPlanes.emplace_back(Plane(Point4D{0, cos(thetta1), sin(thetta1)}, Point4D{0, 0, 0})); // down plane
_clipPlanes.emplace_back(Plane(Point4D{0, -cos(thetta1), sin(thetta1)}, Point4D{0, 0, 0})); // up plane
_ready = true;
Log::log("Camera::init(): camera successfully initialized.");
}
std::vector<Triangle> &Camera::sorted() {
// Sort _tris from back to front
// This is some replacement for Z-buffer
std::sort(_triangles.begin(), _triangles.end(), [](Triangle &t1, Triangle &t2)
{
std::vector<double> v_z1({t1[0].z(), t1[1].z(), t1[2].z()});
std::vector<double> v_z2({t2[0].z(), t2[1].z(), t2[2].z()});
std::sort(v_z1.begin(), v_z1.end());
std::sort(v_z2.begin(), v_z2.end());
double z1 = v_z1[0] + v_z1[1] + v_z1[2];
double z2 = v_z2[0] + v_z2[1] + v_z2[2];
return z1 > z2;
});
return _triangles;
}
void Camera::clear() {
// Cleaning all _tris and recalculation of View matrix
// That is like preparation for new camera shot: we need to set
// the position of camera and insert new cartridge for photo.
_triangles.clear();
_V = Matrix4x4::View(_left, _up, _lookAt, _position);
}
void Camera::rotateX(double rx) {
_angle = Point4D{_angle.x() + rx, _angle.y(), _angle.z()};
_left = Matrix4x4::RotationX(rx) * _left;
_up = Matrix4x4::RotationX(rx) * _up;
_lookAt = Matrix4x4::RotationX(rx) * _lookAt;
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), Point4D{rx, 0, 0});
}
void Camera::rotateY(double ry) {
_angle = Point4D{_angle.x(), _angle.y() + ry, _angle.z()};
_left = Matrix4x4::RotationY(ry) * _left;
_up = Matrix4x4::RotationY(ry) * _up;
_lookAt = Matrix4x4::RotationY(ry) * _lookAt;
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), Point4D{0, ry, 0});
}
void Camera::rotateZ(double rz) {
_angle = Point4D{_angle.x(), _angle.y(), _angle.z() + rz};
_left = Matrix4x4::RotationZ(rz) * _left;
_up = Matrix4x4::RotationZ(rz) * _up;
_lookAt = Matrix4x4::RotationZ(rz) * _lookAt;
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), Point4D{0, 0, rz});
}
void Camera::rotate(const Point4D& r) {
rotateX(r.x());
rotateY(r.y());
rotateZ(r.z());
}
void Camera::rotate(const Point4D& v, double rv) {
_left = Matrix4x4::Rotation(v, rv) * _left;
_up = Matrix4x4::Rotation(v, rv) * _up;
_lookAt = Matrix4x4::Rotation(v, rv) * _lookAt;
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), v, rv);
}
void Camera::rotateLeft(double rl) {
_angleLeftUpLookAt = Point4D{_angleLeftUpLookAt.x() + rl, _angleLeftUpLookAt.y(), _angleLeftUpLookAt.z()};
rotate(_left, rl);
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), _left, rl);
}
void Camera::rotateUp(double ru) {
_angleLeftUpLookAt = Point4D{_angleLeftUpLookAt.x(), _angleLeftUpLookAt.y() + ru, _angleLeftUpLookAt.z()};
rotate(_up, ru);
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), _up, ru);
}
void Camera::rotateLookAt(double rlAt) {
_angleLeftUpLookAt = Point4D{_angleLeftUpLookAt.x(), _angleLeftUpLookAt.y(), _angleLeftUpLookAt.z() + rlAt};
rotate(_lookAt, rlAt);
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(position(), _lookAt, rlAt);
}
void Camera::rotateRelativePoint(const Point4D &s, const Point4D &r) {
_angle = _angle + r;
// Translate XYZ by vector r1
Point4D r1 = _position - s;
// In translated coordinate system we rotate camera and position
Point4D r2 = Matrix4x4::Rotation(r)*r1;
rotate(r);
// After rotation we translate XYZ by vector -r2 and recalculate position
_position = s + r2;
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(s, r);
}
void Camera::rotateRelativePoint(const Point4D &s, const Point4D &v, double r) {
// Translate XYZ by vector r1
Point4D r1 = _position - s;
// In translated coordinate system we rotate camera and position
Point4D r2 = Matrix4x4::Rotation(v, r)*r1;
rotate(v, r);
// After rotation we translate XYZ by vector -r2 and recalculate position
_position = s + r2;
for(auto attached : _attachedObjects)
attached->rotateRelativePoint(s, v, r);
}
void Camera::translateToPoint(const Point4D &point) {
translate(point - _position);
}