2021-09-13 15:53:43 +03:00
|
|
|
//
|
|
|
|
// Created by Иван Ильин on 14.01.2021.
|
|
|
|
//
|
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
#include <cmath>
|
|
|
|
|
2021-09-13 15:53:43 +03:00
|
|
|
#include "Camera.h"
|
|
|
|
#include "utils/Log.h"
|
2021-10-28 16:58:02 +03:00
|
|
|
#include "Consts.h"
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-12 17:12:47 +03:00
|
|
|
std::vector<std::shared_ptr<Triangle>> Camera::project(std::shared_ptr<Mesh> mesh) {
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
if (!_ready) {
|
2021-09-19 11:25:10 +03:00
|
|
|
Log::log("Camera::project(): cannot project _tris without camera initialization ( Camera::init() ) ");
|
2021-10-12 17:12:47 +03:00
|
|
|
return _triangles;
|
2021-09-13 15:53:43 +03:00
|
|
|
}
|
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
if (!mesh->isVisible()) {
|
2021-09-19 11:25:10 +03:00
|
|
|
return this->_triangles;
|
2021-10-28 16:58:02 +03:00
|
|
|
}
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
// Model transform matrix: translate _tris in the origin of body.
|
2021-10-30 11:18:31 +03:00
|
|
|
Matrix4x4 M = mesh->model();
|
2022-07-07 13:14:00 +03:00
|
|
|
Matrix4x4 V = invModel();
|
2021-09-13 15:53:43 +03:00
|
|
|
|
|
|
|
// We don't want to waste time re-allocating memory every time
|
|
|
|
std::vector<Triangle> clippedTriangles, tempBuffer;
|
2021-10-12 17:12:47 +03:00
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
for (auto &t : mesh->triangles()) {
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-30 11:18:31 +03:00
|
|
|
Triangle MTriangle = t * M;
|
|
|
|
|
|
|
|
double dot = MTriangle.norm().dot((Vec3D(MTriangle[0]) - position()).normalized());
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
if (dot > 0) {
|
2021-09-13 15:53:43 +03:00
|
|
|
continue;
|
2021-10-28 16:58:02 +03:00
|
|
|
}
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-30 11:18:31 +03:00
|
|
|
Triangle VMTriangle = MTriangle * V;
|
|
|
|
|
2021-09-13 15:53:43 +03:00
|
|
|
// It needs to be cleared because it's reused through iterations. Usually it doesn't free memory.
|
|
|
|
clippedTriangles.clear();
|
2021-10-12 17:12:47 +03:00
|
|
|
tempBuffer.clear();
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-12 17:12:47 +03:00
|
|
|
// In the beginning we need to to translate triangle from world coordinate to our camera system:
|
2021-09-19 11:25:10 +03:00
|
|
|
// After that we apply clipping for all planes from _clipPlanes
|
2021-10-30 11:18:31 +03:00
|
|
|
clippedTriangles.emplace_back(VMTriangle);
|
2021-10-31 11:39:08 +03:00
|
|
|
for (auto &plane : _clipPlanes) {
|
|
|
|
while (!clippedTriangles.empty()) {
|
2021-10-12 17:12:47 +03:00
|
|
|
std::vector<Triangle> clipResult = plane.clip(clippedTriangles.back());
|
2021-09-13 15:53:43 +03:00
|
|
|
clippedTriangles.pop_back();
|
2021-10-31 11:39:08 +03:00
|
|
|
for (auto &i : clipResult) {
|
2021-10-12 17:12:47 +03:00
|
|
|
tempBuffer.emplace_back(i);
|
2021-10-28 16:58:02 +03:00
|
|
|
}
|
2021-09-13 15:53:43 +03:00
|
|
|
}
|
|
|
|
clippedTriangles.swap(tempBuffer);
|
|
|
|
}
|
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
for (auto &clipped : clippedTriangles) {
|
2021-10-12 17:12:47 +03:00
|
|
|
sf::Color color = clipped.color();
|
2021-10-31 12:01:31 +03:00
|
|
|
sf::Color ambientColor = sf::Color(static_cast<sf::Uint8>(color.r * (0.3 * std::abs(dot) + 0.7)),
|
|
|
|
static_cast<sf::Uint8>(color.g * (0.3 * std::abs(dot) + 0.7)),
|
|
|
|
static_cast<sf::Uint8>(color.b * (0.3 * std::abs(dot) + 0.7)),
|
|
|
|
static_cast<sf::Uint8>(color.a));
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
// Finally its time to project our clipped colored drawTriangle from 3D -> 2D
|
2021-09-13 15:53:43 +03:00
|
|
|
// and transform it's coordinate to screen space (in pixels):
|
2021-10-12 17:12:47 +03:00
|
|
|
Triangle clippedProjected = clipped * _SP;
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-12 17:12:47 +03:00
|
|
|
Triangle clippedProjectedNormalized = Triangle(clippedProjected[0] / clippedProjected[0].w(),
|
2021-10-22 19:42:32 +03:00
|
|
|
clippedProjected[1] / clippedProjected[1].w(),
|
|
|
|
clippedProjected[2] / clippedProjected[2].w(),
|
|
|
|
ambientColor);
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-12 17:12:47 +03:00
|
|
|
_triangles.emplace_back(std::make_shared<Triangle>(clippedProjectedNormalized));
|
2021-09-13 15:53:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
return this->_triangles;
|
2021-09-13 15:53:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2021-10-31 11:39:08 +03:00
|
|
|
_aspect = (double) width / (double) height;
|
2021-10-30 11:18:31 +03:00
|
|
|
Matrix4x4 P = Matrix4x4::Projection(fov, _aspect, ZNear, ZFar);
|
|
|
|
Matrix4x4 S = Matrix4x4::ScreenSpace(width, height);
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-30 11:18:31 +03:00
|
|
|
_SP = S * P; // screen-space-projections matrix
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
// This is planes for clipping _tris.
|
|
|
|
// Motivation: we are not interest in _tris that we cannot see.
|
2021-10-12 17:12:47 +03:00
|
|
|
_clipPlanes.emplace_back(Plane(Vec3D{0, 0, 1}, Vec3D{0, 0, ZNear})); // near plane
|
|
|
|
_clipPlanes.emplace_back(Plane(Vec3D{0, 0, -1}, Vec3D{0, 0, ZFar})); // far plane
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-31 11:39:08 +03:00
|
|
|
double thetta1 = Consts::PI * fov * 0.5 / 180.0;
|
2021-09-19 11:25:10 +03:00
|
|
|
double thetta2 = atan(_aspect * tan(thetta1));
|
2021-10-12 17:12:47 +03:00
|
|
|
_clipPlanes.emplace_back(Plane(Vec3D{-cos(thetta2), 0, sin(thetta2)}, Vec3D{0, 0, 0})); // left plane
|
|
|
|
_clipPlanes.emplace_back(Plane(Vec3D{cos(thetta2), 0, sin(thetta2)}, Vec3D{0, 0, 0})); // right plane
|
|
|
|
_clipPlanes.emplace_back(Plane(Vec3D{0, cos(thetta1), sin(thetta1)}, Vec3D{0, 0, 0})); // down plane
|
|
|
|
_clipPlanes.emplace_back(Plane(Vec3D{0, -cos(thetta1), sin(thetta1)}, Vec3D{0, 0, 0})); // up plane
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
_ready = true;
|
2021-09-13 15:53:43 +03:00
|
|
|
Log::log("Camera::init(): camera successfully initialized.");
|
|
|
|
}
|
|
|
|
|
2021-10-12 17:12:47 +03:00
|
|
|
std::vector<std::shared_ptr<Triangle>> Camera::sorted() {
|
2021-09-13 15:53:43 +03:00
|
|
|
|
2021-10-03 07:47:05 +03:00
|
|
|
// Sort _tris from _back to front
|
2021-09-13 15:53:43 +03:00
|
|
|
// This is some replacement for Z-buffer
|
2021-10-28 16:58:02 +03:00
|
|
|
std::sort(_triangles.begin(), _triangles.end(), [](std::shared_ptr<Triangle> &t1, std::shared_ptr<Triangle> &t2) {
|
2021-10-12 17:12:47 +03:00
|
|
|
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()});
|
2021-09-13 15:53:43 +03:00
|
|
|
|
|
|
|
std::sort(v_z1.begin(), v_z1.end());
|
|
|
|
std::sort(v_z2.begin(), v_z2.end());
|
|
|
|
|
2021-09-14 13:47:53 +03:00
|
|
|
double z1 = v_z1[0] + v_z1[1] + v_z1[2];
|
|
|
|
double z2 = v_z2[0] + v_z2[1] + v_z2[2];
|
2021-09-13 15:53:43 +03:00
|
|
|
|
|
|
|
return z1 > z2;
|
|
|
|
});
|
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
return _triangles;
|
2021-09-13 15:53:43 +03:00
|
|
|
}
|
|
|
|
|
2021-09-19 11:25:10 +03:00
|
|
|
void Camera::clear() {
|
|
|
|
// Cleaning all _tris and recalculation of View matrix
|
|
|
|
_triangles.clear();
|
2021-09-13 15:53:43 +03:00
|
|
|
}
|