init commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.idea/
|
||||||
|
|
||||||
|
cmake-build-debug/
|
||||||
|
cmake-build-release/
|
||||||
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.31)
|
||||||
|
set(PROJECT_NAME Rasterizer)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
project(${PROJECT_NAME})
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC_SOURCE
|
||||||
|
src/**.cpp
|
||||||
|
src/vendor/stb/stb_image.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${SRC_SOURCE})
|
||||||
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
## build by cmake
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build --config Release -j18
|
||||||
|
```
|
||||||
274
src/Application/Application.cpp
Normal file
274
src/Application/Application.cpp
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "../Core/Renderer/Renderer.h"
|
||||||
|
#include "../Shader/DemoShader.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Application::Application(std::string name, const int width, const int height, CommandLineArgs args)
|
||||||
|
: m_Name(std::move(name)),m_Width(width), m_Height(height), m_Window(nullptr)
|
||||||
|
{
|
||||||
|
if (args.argc != 2)
|
||||||
|
{
|
||||||
|
std::string programName = args.argv[0];
|
||||||
|
std::cout << "should in command to use it\n" << std::endl;
|
||||||
|
std::cout << " Usage: " << programName.substr(programName.find_last_of("\\/") + 1) << " path/to/model.obj\n" << std::endl;
|
||||||
|
std::cout << " Or drag \"obj\" file in this program\n" << std::endl;
|
||||||
|
system("pause");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Init();
|
||||||
|
|
||||||
|
const std::string path = args.argv[1];
|
||||||
|
const std::string pathDir = path.substr(0, path.find_last_of("/\\"));
|
||||||
|
std::string mtllib;
|
||||||
|
|
||||||
|
LoadMesh(path.c_str(), mtllib);
|
||||||
|
|
||||||
|
if (!mtllib.empty())
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> textures;
|
||||||
|
if (GetTexture((pathDir + "\\" + mtllib).c_str(), textures))
|
||||||
|
{
|
||||||
|
m_Uniform.Diffuse = Texture::CreateTexture(pathDir + "\\" + textures["diff"]);
|
||||||
|
m_Uniform.Specular = Texture::CreateTexture(pathDir + "\\" + textures["spec"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::Init()
|
||||||
|
{
|
||||||
|
|
||||||
|
m_Window = Window::Create(m_Name, m_Width, m_Height);
|
||||||
|
m_LastFrameTime = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::Run()
|
||||||
|
{
|
||||||
|
while (!m_Window->Close())
|
||||||
|
{
|
||||||
|
auto nowFrameTime = std::chrono::steady_clock::now();
|
||||||
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(nowFrameTime - m_LastFrameTime);
|
||||||
|
const float deltaTime = duration.count() * 0.001f * 0.001f;
|
||||||
|
m_LastFrameTime = nowFrameTime;
|
||||||
|
|
||||||
|
OnUpdate(deltaTime);
|
||||||
|
Window::PollInputEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::Terminate()
|
||||||
|
{
|
||||||
|
|
||||||
|
delete m_Uniform.Diffuse;
|
||||||
|
delete m_Uniform.Specular;
|
||||||
|
|
||||||
|
m_Window->Terminate();
|
||||||
|
delete m_Window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::~Application()
|
||||||
|
{
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Application::OnUpdate(float ts)
|
||||||
|
{
|
||||||
|
|
||||||
|
static float fpstime = 0.0f;
|
||||||
|
static int fpsCount = 0;
|
||||||
|
fpstime +=ts;
|
||||||
|
fpsCount += 1;
|
||||||
|
if (fpstime >= 0.5f)
|
||||||
|
{
|
||||||
|
std::cout << "fps: "<< (int)((float)fpsCount/fpstime) << std::endl;
|
||||||
|
fpstime = 0.0f;
|
||||||
|
fpsCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnCameraUpdate(ts);
|
||||||
|
|
||||||
|
FrameBuffer framebuffer(m_Width, m_Height);
|
||||||
|
framebuffer.Clear({0.2f, 0.2f, 0.2f});
|
||||||
|
|
||||||
|
|
||||||
|
Math::Mat4 view = Math::Mat4LookAt(m_Camera.Position, m_Camera.Position + m_Camera.Front, {0.0f, 1.0f, 0.0f});
|
||||||
|
Math::Mat4 projection = Math::Mat4Perspective(90.f/180.f *Math::PI, m_Camera.Aspect, 0.1f ,100.0f);
|
||||||
|
Math::Mat4 model = Math::Mat4Identity();
|
||||||
|
m_Uniform.MVP = projection * view * model;
|
||||||
|
|
||||||
|
m_Uniform.Camera = m_Camera.Position;
|
||||||
|
m_Uniform.Model = model;
|
||||||
|
m_Uniform.ModelNormalToWorld = Math::Mat4Identity();
|
||||||
|
|
||||||
|
|
||||||
|
Program program(DemoVertexShader, DemoFragmentShader);
|
||||||
|
for (const auto& triangle : m_Meshes)
|
||||||
|
{
|
||||||
|
Renderer::Draw(framebuffer, program, triangle, m_Uniform);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Window->DrawFrameBuffer(framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Application::OnCameraUpdate(float ts)
|
||||||
|
{
|
||||||
|
constexpr float speed = 1.0f;
|
||||||
|
|
||||||
|
if (m_Window->GetKey(KEY_SPACE) == KEY_PRESS)
|
||||||
|
m_Camera.Position += speed * ts * m_Camera.Up;
|
||||||
|
if (m_Window->GetKey(KEY_LEFT_CONTROL) == KEY_PRESS)
|
||||||
|
m_Camera.Position -= speed * ts * m_Camera.Up;
|
||||||
|
if (m_Window->GetKey(KEY_W) == KEY_PRESS)
|
||||||
|
m_Camera.Position += speed * ts * m_Camera.Front;
|
||||||
|
if (m_Window->GetKey(KEY_S) == KEY_PRESS)
|
||||||
|
m_Camera.Position -= speed * ts * m_Camera.Front;
|
||||||
|
if (m_Window->GetKey(KEY_A) == KEY_PRESS)
|
||||||
|
m_Camera.Position -= speed * ts * m_Camera.Right;
|
||||||
|
if (m_Window->GetKey(KEY_D) == KEY_PRESS)
|
||||||
|
m_Camera.Position += speed * ts * m_Camera.Right;
|
||||||
|
// std::cout << m_Camera.Position << std::endl;
|
||||||
|
constexpr float rotationSpeed = 1.0f;
|
||||||
|
Math::Mat4 rotation = Math::Mat4Identity();
|
||||||
|
if (m_Window->GetKey(KEY_Q) == KEY_PRESS)
|
||||||
|
rotation = Math::Mat4RotateY(ts * rotationSpeed);
|
||||||
|
if (m_Window->GetKey(KEY_E) == KEY_PRESS)
|
||||||
|
rotation = Math::Mat4RotateY(-ts * rotationSpeed);
|
||||||
|
m_Camera.Front = rotation * m_Camera.Front;
|
||||||
|
m_Camera.Front = {Math::Normalize(m_Camera.Front), 0.0f};
|
||||||
|
m_Camera.Right = {Math::Normalize(Math::Cross(m_Camera.Front, Math::Vec3(0,1,0))), 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::LoadMesh(const std::string& filepath, std::string& mtllib)
|
||||||
|
{
|
||||||
|
if (filepath.substr(filepath.find_last_of('.')) != ".obj")
|
||||||
|
{
|
||||||
|
std::cout << "not a .obj file \n\t" << filepath << std::endl;
|
||||||
|
Terminate();
|
||||||
|
system("pause");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream file(filepath);
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load file! is exist? \n\t" << filepath << std::endl;
|
||||||
|
Terminate();
|
||||||
|
system("pause");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Math::Vec3> positions;
|
||||||
|
std::vector<Math::Vec2> texCoords;
|
||||||
|
std::vector<Math::Vec3> normals;
|
||||||
|
std::vector<unsigned int> posIndices;
|
||||||
|
std::vector<unsigned int> texIndices;
|
||||||
|
std::vector<unsigned int> normalIndices;
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (!file.eof())
|
||||||
|
{
|
||||||
|
std::getline(file, line);
|
||||||
|
int items = -1;
|
||||||
|
if (line.find("mtllib ") == 0)
|
||||||
|
{
|
||||||
|
mtllib = line.substr(7);
|
||||||
|
}
|
||||||
|
else if (line.find("v ") == 0)
|
||||||
|
{
|
||||||
|
Math::Vec3 position;
|
||||||
|
items = sscanf(line.c_str(), "v %f %f %f", &position.x, &position.y, &position.z);
|
||||||
|
ASSERT(items == 3);
|
||||||
|
|
||||||
|
positions.push_back(position);
|
||||||
|
}else if (line.find("vt ") == 0)
|
||||||
|
{
|
||||||
|
Math::Vec2 texCoord;
|
||||||
|
items = sscanf(line.c_str(), "vt %f %f", &texCoord.x, &texCoord.y);
|
||||||
|
ASSERT(items == 2);
|
||||||
|
texCoords.push_back(texCoord);
|
||||||
|
}else if (line.find("vn ") == 0)
|
||||||
|
{
|
||||||
|
Math::Vec3 normal;
|
||||||
|
items = sscanf(line.c_str(), "vn %f %f %f", &normal.x, &normal.y, &normal.z);
|
||||||
|
ASSERT(items == 3);
|
||||||
|
normals.push_back(normal);
|
||||||
|
}else if (line.find("f ") == 0)
|
||||||
|
{
|
||||||
|
int pIndices[3], uvIndices[3], nIndices[3];
|
||||||
|
items = sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d",
|
||||||
|
&pIndices[0], &uvIndices[0], &nIndices[0],
|
||||||
|
&pIndices[1], &uvIndices[1], &nIndices[1],
|
||||||
|
&pIndices[2], &uvIndices[2], &nIndices[2]);
|
||||||
|
|
||||||
|
ASSERT(items == 9);
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
posIndices.push_back(pIndices[i] - 1);
|
||||||
|
texIndices.push_back(uvIndices[i] - 1);
|
||||||
|
normalIndices.push_back(nIndices[i] - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
int triNum = posIndices.size() / 3;
|
||||||
|
for (int i = 0; i < triNum; i++)
|
||||||
|
{
|
||||||
|
Triangle<BlinnVertex> triangle;
|
||||||
|
for (int j = 0; j < 3; j++)
|
||||||
|
{
|
||||||
|
int index = 3 * i + j;
|
||||||
|
int posIndex = posIndices[index];
|
||||||
|
int texIndex = texIndices[index];
|
||||||
|
int normIndex = normalIndices[index];
|
||||||
|
|
||||||
|
triangle[j].ModelPosition = {positions[posIndex], 1.0f };
|
||||||
|
triangle[j].TexCoord = texCoords[texIndex];
|
||||||
|
triangle[j].ModelNormal = normals[normIndex];
|
||||||
|
}
|
||||||
|
m_Meshes.emplace_back(triangle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::GetTexture(const char* filepath, std::map<std::string, std::string>& textures)
|
||||||
|
{
|
||||||
|
std::ifstream file(filepath);
|
||||||
|
// ASSERT(file);
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to open file: " << filepath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasTexture = false;
|
||||||
|
std::string line;
|
||||||
|
while (!file.eof())
|
||||||
|
{
|
||||||
|
std::getline(file, line);
|
||||||
|
|
||||||
|
if (line.find("map_Kd ") == 0)
|
||||||
|
{
|
||||||
|
textures["diff"] = line.substr(7);
|
||||||
|
hasTexture = true;
|
||||||
|
}else if (line.find("map_Ks ") == 0)
|
||||||
|
{
|
||||||
|
textures["spec"] = line.substr(7);
|
||||||
|
hasTexture = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasTexture;
|
||||||
|
}
|
||||||
57
src/Application/Application.h
Normal file
57
src/Application/Application.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef APPLICATION_H
|
||||||
|
#define APPLICATION_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "../Core/Camera/Camera.h"
|
||||||
|
#include "../Core/Renderer/Renderer.h"
|
||||||
|
#include "../Core/Window/Window.h"
|
||||||
|
#include "../Shader/DemoShader.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct CommandLineArgs
|
||||||
|
{
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Application {
|
||||||
|
public:
|
||||||
|
Application(std::string name, int width, int height, CommandLineArgs args = CommandLineArgs());
|
||||||
|
~Application();
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
private:
|
||||||
|
void Init();
|
||||||
|
void Terminate();
|
||||||
|
|
||||||
|
void OnUpdate(float ts);
|
||||||
|
void OnCameraUpdate(float ts);
|
||||||
|
|
||||||
|
void LoadMesh(const std::string& filepath ,std::string& mtllib);
|
||||||
|
bool GetTexture(const char* filepath, std::map<std::string, std::string>& textures);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string m_Name;
|
||||||
|
int m_Width, m_Height;
|
||||||
|
Window* m_Window;
|
||||||
|
|
||||||
|
std::vector<Triangle<BlinnVertex>> m_Meshes;
|
||||||
|
BlinnUniform m_Uniform;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::chrono::steady_clock::time_point m_LastFrameTime;
|
||||||
|
|
||||||
|
Camera m_Camera;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //APPLICATION_H
|
||||||
5
src/Core/Assert.cpp
Normal file
5
src/Core/Assert.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Assert.h"
|
||||||
12
src/Core/Assert.h
Normal file
12
src/Core/Assert.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASSERT_H
|
||||||
|
#define ASSERT_H
|
||||||
|
|
||||||
|
#define LOG(...)
|
||||||
|
#define ASSERT(x, ...) {if(!(x)) {LOG(__VA_ARGS__); __debugbreak(); }}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //ASSERT_H
|
||||||
5
src/Core/Camera/Camera.cpp
Normal file
5
src/Core/Camera/Camera.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-30.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Camera.h"
|
||||||
21
src/Core/Camera/Camera.h
Normal file
21
src/Core/Camera/Camera.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-30.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CAMERA_H
|
||||||
|
#define CAMERA_H
|
||||||
|
|
||||||
|
#include "../Math.h"
|
||||||
|
|
||||||
|
class Camera
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Math::Vec4 Position{0.0f, 0.0f, 3.0f, 0.0f};
|
||||||
|
Math::Vec4 Right{1.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
Math::Vec4 Up{0.0f, 1.0f, 0.0f, 0.0f};
|
||||||
|
Math::Vec4 Front{0.0f, 0.0f, -1.0f, 0.0f};
|
||||||
|
float Aspect = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //CAMERA_H
|
||||||
5
src/Core/Input/InputCodes.cpp
Normal file
5
src/Core/Input/InputCodes.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "InputCodes.h"
|
||||||
144
src/Core/Input/InputCodes.h
Normal file
144
src/Core/Input/InputCodes.h
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef INPUTCODES_H
|
||||||
|
#define INPUTCODES_H
|
||||||
|
|
||||||
|
|
||||||
|
// From glfw3.h
|
||||||
|
#define KEY_SPACE 32
|
||||||
|
#define KEY_APOSTROPHE 39 /* ' */
|
||||||
|
#define KEY_COMMA 44 /* , */
|
||||||
|
#define KEY_MINUS 45 /* - */
|
||||||
|
#define KEY_PERIOD 46 /* . */
|
||||||
|
#define KEY_SLASH 47 /* / */
|
||||||
|
#define KEY_0 48
|
||||||
|
#define KEY_1 49
|
||||||
|
#define KEY_2 50
|
||||||
|
#define KEY_3 51
|
||||||
|
#define KEY_4 52
|
||||||
|
#define KEY_5 53
|
||||||
|
#define KEY_6 54
|
||||||
|
#define KEY_7 55
|
||||||
|
#define KEY_8 56
|
||||||
|
#define KEY_9 57
|
||||||
|
#define KEY_SEMICOLON 59 /* ; */
|
||||||
|
#define KEY_EQUAL 61 /* = */
|
||||||
|
#define KEY_A 65
|
||||||
|
#define KEY_B 66
|
||||||
|
#define KEY_C 67
|
||||||
|
#define KEY_D 68
|
||||||
|
#define KEY_E 69
|
||||||
|
#define KEY_F 70
|
||||||
|
#define KEY_G 71
|
||||||
|
#define KEY_H 72
|
||||||
|
#define KEY_I 73
|
||||||
|
#define KEY_J 74
|
||||||
|
#define KEY_K 75
|
||||||
|
#define KEY_L 76
|
||||||
|
#define KEY_M 77
|
||||||
|
#define KEY_N 78
|
||||||
|
#define KEY_O 79
|
||||||
|
#define KEY_P 80
|
||||||
|
#define KEY_Q 81
|
||||||
|
#define KEY_R 82
|
||||||
|
#define KEY_S 83
|
||||||
|
#define KEY_T 84
|
||||||
|
#define KEY_U 85
|
||||||
|
#define KEY_V 86
|
||||||
|
#define KEY_W 87
|
||||||
|
#define KEY_X 88
|
||||||
|
#define KEY_Y 89
|
||||||
|
#define KEY_Z 90
|
||||||
|
#define KEY_LEFT_BRACKET 91 /* [ */
|
||||||
|
#define KEY_BACKSLASH 92 /* \ */
|
||||||
|
#define KEY_RIGHT_BRACKET 93 /* ] */
|
||||||
|
#define KEY_GRAVE_ACCENT 96 /* ` */
|
||||||
|
#define KEY_WORLD_1 161 /* non-US #1 */
|
||||||
|
#define KEY_WORLD_2 162 /* non-US #2 */
|
||||||
|
|
||||||
|
/* Functkeys */
|
||||||
|
#define KEY_ESCAPE 256
|
||||||
|
#define KEY_ENTER 257
|
||||||
|
#define KEY_TAB 258
|
||||||
|
#define KEY_BACKSPACE 259
|
||||||
|
#define KEY_INSERT 260
|
||||||
|
#define KEY_DELETE 261
|
||||||
|
#define KEY_RIGHT 262
|
||||||
|
#define KEY_LEFT 263
|
||||||
|
#define KEY_DOWN 264
|
||||||
|
#define KEY_UP 265
|
||||||
|
#define KEY_PAGE_UP 266
|
||||||
|
#define KEY_PAGE_DOWN 267
|
||||||
|
#define KEY_HOME 268
|
||||||
|
#define KEY_END 269
|
||||||
|
#define KEY_CAPS_LOCK 280
|
||||||
|
#define KEY_SCROLL_LOCK 281
|
||||||
|
#define KEY_NUM_LOCK 282
|
||||||
|
#define KEY_PRINT_SCREEN 283
|
||||||
|
#define KEY_PAUSE 284
|
||||||
|
#define KEY_F1 290
|
||||||
|
#define KEY_F2 291
|
||||||
|
#define KEY_F3 292
|
||||||
|
#define KEY_F4 293
|
||||||
|
#define KEY_F5 294
|
||||||
|
#define KEY_F6 295
|
||||||
|
#define KEY_F7 296
|
||||||
|
#define KEY_F8 297
|
||||||
|
#define KEY_F9 298
|
||||||
|
#define KEY_F10 299
|
||||||
|
#define KEY_F11 300
|
||||||
|
#define KEY_F12 301
|
||||||
|
#define KEY_F13 302
|
||||||
|
#define KEY_F14 303
|
||||||
|
#define KEY_F15 304
|
||||||
|
#define KEY_F16 305
|
||||||
|
#define KEY_F17 306
|
||||||
|
#define KEY_F18 307
|
||||||
|
#define KEY_F19 308
|
||||||
|
#define KEY_F20 309
|
||||||
|
#define KEY_F21 310
|
||||||
|
#define KEY_F22 311
|
||||||
|
#define KEY_F23 312
|
||||||
|
#define KEY_F24 313
|
||||||
|
#define KEY_F25 314
|
||||||
|
#define KEY_KP_0 320
|
||||||
|
#define KEY_KP_1 321
|
||||||
|
#define KEY_KP_2 322
|
||||||
|
#define KEY_KP_3 323
|
||||||
|
#define KEY_KP_4 324
|
||||||
|
#define KEY_KP_5 325
|
||||||
|
#define KEY_KP_6 326
|
||||||
|
#define KEY_KP_7 327
|
||||||
|
#define KEY_KP_8 328
|
||||||
|
#define KEY_KP_9 329
|
||||||
|
#define KEY_KP_DECIMAL 330
|
||||||
|
#define KEY_KP_DIVIDE 331
|
||||||
|
#define KEY_KP_MULTIPLY 332
|
||||||
|
#define KEY_KP_SUBTRACT 333
|
||||||
|
#define KEY_KP_ADD 334
|
||||||
|
#define KEY_KP_ENTER 335
|
||||||
|
#define KEY_KP_EQUAL 336
|
||||||
|
#define KEY_LEFT_SHIFT 340
|
||||||
|
#define KEY_LEFT_CONTROL 341
|
||||||
|
#define KEY_LEFT_ALT 342
|
||||||
|
#define KEY_LEFT_SUPER 343
|
||||||
|
#define KEY_RIGHT_SHIFT 344
|
||||||
|
#define KEY_RIGHT_CONTROL 345
|
||||||
|
#define KEY_RIGHT_ALT 346
|
||||||
|
#define KEY_RIGHT_SUPER 347
|
||||||
|
#define KEY_MENU 348
|
||||||
|
#define KEY_MAX_COUNT 512
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define BUTTON_LEFT 0
|
||||||
|
#define BUTTON_RIGHT 1
|
||||||
|
#define BUTTON_MAX_COUNT 8
|
||||||
|
|
||||||
|
#define KEY_RELEASE 0
|
||||||
|
#define KEY_PRESS 1
|
||||||
|
|
||||||
|
|
||||||
|
#endif //INPUTCODES_H
|
||||||
322
src/Core/Math.cpp
Normal file
322
src/Core/Math.cpp
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Math.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Math
|
||||||
|
{
|
||||||
|
|
||||||
|
Vec2 operator+ (const Vec2& left, const Vec2& right)
|
||||||
|
{
|
||||||
|
return Vec2{ left.x + right.x, left.y + right.y };
|
||||||
|
}
|
||||||
|
Vec2 operator- (const Vec2& left, const Vec2& right)
|
||||||
|
{
|
||||||
|
return Vec2{ left.x - right.x, left.y - right.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 operator+ (const Vec3& left, const Vec3& right)
|
||||||
|
{
|
||||||
|
return Vec3{ left.x + right.x, left.y + right.y, left.z + right.z };
|
||||||
|
}
|
||||||
|
Vec3 operator- (const Vec3& left, const Vec3& right)
|
||||||
|
{
|
||||||
|
return left + (-1.0f * right);
|
||||||
|
}
|
||||||
|
Vec3 operator* (const float left, const Vec3& right)
|
||||||
|
{
|
||||||
|
return Vec3{ left * right.x, left * right.y, left * right.z };
|
||||||
|
}
|
||||||
|
Vec3 operator* (const Vec3& left, const float right)
|
||||||
|
{
|
||||||
|
return right * left;
|
||||||
|
}
|
||||||
|
Vec3 operator* (const Vec3& left, const Vec3& right)
|
||||||
|
{
|
||||||
|
return { left.x * right.x, left.y * right.y , left.z * right.z };
|
||||||
|
}
|
||||||
|
Vec3 operator/ (const Vec3& left, const float right)
|
||||||
|
{
|
||||||
|
ASSERT(right != 0);
|
||||||
|
return left * (1.0f / right);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Dot(const Vec3& left, const Vec3& right)
|
||||||
|
{
|
||||||
|
return left.x * right.x + left.y * right.y + left.z * right.z;
|
||||||
|
}
|
||||||
|
Vec3 Cross(const Vec3& left, const Vec3& right)
|
||||||
|
{
|
||||||
|
float x = left.y * right.z - left.z * right.y;
|
||||||
|
float y = left.z * right.x - left.x * right.z;
|
||||||
|
float z = left.x * right.y - left.y * right.x;
|
||||||
|
return { x, y, z };
|
||||||
|
}
|
||||||
|
Vec3 Normalize(const Vec3& v)
|
||||||
|
{
|
||||||
|
float len = (float)std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||||
|
ASSERT(len != 0);
|
||||||
|
return v / len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 operator+ (const Vec4& left, const Vec4& right)
|
||||||
|
{
|
||||||
|
return Vec4{ left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w };
|
||||||
|
}
|
||||||
|
Vec4 operator- (const Vec4& left, const Vec4& right)
|
||||||
|
{
|
||||||
|
return Vec4{ left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 operator+=(Vec4& left, const Vec4& right)
|
||||||
|
{
|
||||||
|
left = left + right;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 operator-=(Vec4& left, const Vec4& right)
|
||||||
|
{
|
||||||
|
left = left - right;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 operator* (const float left, const Vec4& right)
|
||||||
|
{
|
||||||
|
return Vec4{ left * right.x, left * right.y, left * right.z, left * right.w };
|
||||||
|
}
|
||||||
|
Vec4 operator* (const Vec4& left, const float right)
|
||||||
|
{
|
||||||
|
return right * left;
|
||||||
|
}
|
||||||
|
Vec4 operator/ (const Vec4& left, const float right)
|
||||||
|
{
|
||||||
|
ASSERT(right != 0);
|
||||||
|
return left * (1.0f / right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 operator* (const Mat4& mat4, const Vec4& vec4)
|
||||||
|
{
|
||||||
|
Vec4 res;
|
||||||
|
res.x = mat4.m[0][0] * vec4.x + mat4.m[0][1] * vec4.y + mat4.m[0][2] * vec4.z + mat4.m[0][3] * vec4.w;
|
||||||
|
res.y = mat4.m[1][0] * vec4.x + mat4.m[1][1] * vec4.y + mat4.m[1][2] * vec4.z + mat4.m[1][3] * vec4.w;
|
||||||
|
res.z = mat4.m[2][0] * vec4.x + mat4.m[2][1] * vec4.y + mat4.m[2][2] * vec4.z + mat4.m[2][3] * vec4.w;
|
||||||
|
res.w = mat4.m[3][0] * vec4.x + mat4.m[3][1] * vec4.y + mat4.m[3][2] * vec4.z + mat4.m[3][3] * vec4.w;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Mat4::Mat4()
|
||||||
|
{
|
||||||
|
for (int i =0 ; i < 4; i++)
|
||||||
|
for (int j =0 ; j < 4; j++)
|
||||||
|
m[i][j] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat4::Mat4(const Vec4& v0, const Vec4& v1, const Vec4& v2, const Vec4& v3)
|
||||||
|
{
|
||||||
|
m[0][0] = v0.x; m[1][0] = v0.y; m[2][0] = v0.z; m[3][0] = v0.w;
|
||||||
|
m[0][1] = v1.x; m[1][1] = v1.y; m[2][1] = v1.z; m[3][1] = v1.w;
|
||||||
|
m[0][2] = v2.x; m[1][2] = v2.y; m[2][2] = v2.z; m[3][2] = v2.w;
|
||||||
|
m[0][3] = v3.x; m[1][3] = v3.y; m[2][3] = v3.z; m[3][3] = v3.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat4::operator std::string() const
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res += "[";
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
res += std::to_string(m[i][j]);
|
||||||
|
res += (i == 3 && j == 3) ? "]" : ", ";
|
||||||
|
}
|
||||||
|
res += "\n";
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat4 operator* (const Mat4& left, const Mat4& right)
|
||||||
|
{
|
||||||
|
Mat4 res;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 4; k++)
|
||||||
|
{
|
||||||
|
res.m[i][j] += left.m[i][k] * right.m[k][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
Mat4& operator*= (Mat4& left, const Mat4& right)
|
||||||
|
{
|
||||||
|
left = left * right;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat4 Mat4Identity()
|
||||||
|
{
|
||||||
|
return Mat4({ 1,0,0,0 }, { 0,1,0,0 }, { 0,0,1,0 }, { 0,0,0,1 });
|
||||||
|
}
|
||||||
|
Mat4 Mat4Translate(float tx, float ty, float tz)
|
||||||
|
{
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
m.m[0][3] = tx;
|
||||||
|
m.m[1][3] = ty;
|
||||||
|
m.m[2][3] = tz;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
Mat4 Mat4Scale(float sx, float sy, float sz)
|
||||||
|
{
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
ASSERT(sx != 0 && sy != 0 && sz != 0);
|
||||||
|
m.m[0][0] = sx;
|
||||||
|
m.m[1][1] = sy;
|
||||||
|
m.m[2][2] = sz;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Mat4 Mat4RotateX(float angle)
|
||||||
|
{
|
||||||
|
float c = (float)std::cos(angle);
|
||||||
|
float s = (float)std::sin(angle);
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
m.m[1][1] = c;
|
||||||
|
m.m[1][2] = -s;
|
||||||
|
m.m[2][1] = s;
|
||||||
|
m.m[2][2] = c;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
Mat4 Mat4RotateY(float angle)
|
||||||
|
{
|
||||||
|
float c = (float)std::cos(angle);
|
||||||
|
float s = (float)std::sin(angle);
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
m.m[2][2] = c;
|
||||||
|
m.m[2][0] = -s;
|
||||||
|
m.m[0][2] = s;
|
||||||
|
m.m[0][0] = c;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
Mat4 Mat4RotateZ(float angle)
|
||||||
|
{
|
||||||
|
float c = (float)std::cos(angle);
|
||||||
|
float s = (float)std::sin(angle);
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
m.m[0][0] = c;
|
||||||
|
m.m[0][1] = -s;
|
||||||
|
m.m[1][0] = s;
|
||||||
|
m.m[1][1] = c;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
Mat4 Mat4LookAt(const Vec3& xAxis, const Vec3& yAxis, const Vec3& zAxis, const Vec3& eye)
|
||||||
|
{
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
|
||||||
|
m.m[0][0] = xAxis.x;
|
||||||
|
m.m[0][1] = xAxis.y;
|
||||||
|
m.m[0][2] = xAxis.z;
|
||||||
|
|
||||||
|
m.m[1][0] = yAxis.x;
|
||||||
|
m.m[1][1] = yAxis.y;
|
||||||
|
m.m[1][2] = yAxis.z;
|
||||||
|
|
||||||
|
m.m[2][0] = zAxis.x;
|
||||||
|
m.m[2][1] = zAxis.y;
|
||||||
|
m.m[2][2] = zAxis.z;
|
||||||
|
|
||||||
|
m.m[0][3] = -Dot(xAxis, eye);
|
||||||
|
m.m[1][3] = -Dot(yAxis, eye);
|
||||||
|
m.m[2][3] = -Dot(zAxis, eye);
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
Mat4 Mat4LookAt(const Vec3& eye, const Vec3& target, const Vec3& up)
|
||||||
|
{
|
||||||
|
Vec3 zAxis = Normalize(eye - target);
|
||||||
|
Vec3 xAxis = Normalize(Cross(up, zAxis));
|
||||||
|
Vec3 yAxis = Normalize(Cross(zAxis, xAxis));
|
||||||
|
return Mat4LookAt(xAxis, yAxis, zAxis, eye);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* fovy: the field of view angle in the y direction, in radians
|
||||||
|
* aspect: the aspect ratio, defined as width divided by height
|
||||||
|
* near, far: the distances to the near and far depth clipping planes
|
||||||
|
*
|
||||||
|
* 1/(aspect*tan(fovy/2)) 0 0 0
|
||||||
|
* 0 1/tan(fovy/2) 0 0
|
||||||
|
* 0 0 -(f+n)/(f-n) -2fn/(f-n)
|
||||||
|
* 0 0 -1 0
|
||||||
|
*
|
||||||
|
* this is the same as
|
||||||
|
* float half_h = near * (float)tan(fovy / 2);
|
||||||
|
* float half_w = half_h * aspect;
|
||||||
|
* mat4_frustum(-half_w, half_w, -half_h, half_h, near, far);
|
||||||
|
*
|
||||||
|
* see http://www.songho.ca/opengl/gl_projectionmatrix.html
|
||||||
|
*/
|
||||||
|
Mat4 Mat4Perspective(float fovy, float aspect, float near, float far)
|
||||||
|
{
|
||||||
|
float z_range = far - near;
|
||||||
|
Mat4 m = Mat4Identity();
|
||||||
|
ASSERT(fovy > 0 && aspect > 0);
|
||||||
|
ASSERT(near > 0 && far > 0 && z_range > 0);
|
||||||
|
m.m[1][1] = 1 / (float)std::tan(fovy / 2);
|
||||||
|
m.m[0][0] = m.m[1][1] / aspect;
|
||||||
|
m.m[2][2] = -(near + far) / z_range;
|
||||||
|
m.m[2][3] = -2 * near * far / z_range;
|
||||||
|
m.m[3][2] = -1;
|
||||||
|
m.m[3][3] = 0;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Lerp(const float start, const float end, const float t)
|
||||||
|
{
|
||||||
|
return end * t + start * (1.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 Lerp(const Vec3& start, const Vec3& end, const float t)
|
||||||
|
{
|
||||||
|
return end * t + start * (1.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec4 Lerp(const Vec4& start, const Vec4& end, const float t)
|
||||||
|
{
|
||||||
|
return end * t + start * (1.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char Float2UChar(const float f)
|
||||||
|
{
|
||||||
|
return (unsigned char)(f * 255.0f);
|
||||||
|
}
|
||||||
|
float UChar2Float(const unsigned char c)
|
||||||
|
{
|
||||||
|
return (float)c / 255.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Clamp(const float val, const float min, const float max)
|
||||||
|
{
|
||||||
|
if (val < min)
|
||||||
|
{
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
else if (val > max)
|
||||||
|
{
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/Core/Math.h
Normal file
160
src/Core/Math.h
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MATH_H
|
||||||
|
#define MATH_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "Assert.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Math
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr float PI = 3.14159265359f;
|
||||||
|
constexpr float EPSILON = 1e-5f;
|
||||||
|
|
||||||
|
struct Vec2
|
||||||
|
{
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
|
||||||
|
constexpr Vec2() : x(0.0f), y(0.0f) {}
|
||||||
|
constexpr Vec2(const float x, const float y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
explicit operator std::string() const
|
||||||
|
{
|
||||||
|
return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vec3
|
||||||
|
{
|
||||||
|
union { float x, r; };
|
||||||
|
union { float y, g; };
|
||||||
|
union { float z, b; };
|
||||||
|
|
||||||
|
constexpr Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
|
||||||
|
constexpr Vec3(const Vec2& vec, const float z) : x(vec.x), y(vec.y), z(z) {}
|
||||||
|
constexpr Vec3(const Vec3& vec) : x(vec.x), y(vec.y), z(vec.z) {}
|
||||||
|
constexpr Vec3(const float x, const float y, const float z) : x(x), y(y), z(z) {}
|
||||||
|
|
||||||
|
explicit operator Vec2() const { return {x, y}; }
|
||||||
|
|
||||||
|
explicit operator std::string() const
|
||||||
|
{
|
||||||
|
return "(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(x) + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vec4
|
||||||
|
{
|
||||||
|
union { float x, r; };
|
||||||
|
union { float y, g; };
|
||||||
|
union { float z, b; };
|
||||||
|
union { float w, a; };
|
||||||
|
|
||||||
|
constexpr Vec4() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {}
|
||||||
|
constexpr Vec4(const float val) : x(val), y(val), z(val), w(val) {}
|
||||||
|
constexpr Vec4(const Vec2& vec2, const float z, const float w) : x(vec2.x), y(vec2.y), z(z), w(w) {}
|
||||||
|
constexpr Vec4(const Vec3& vec3, const float w) : x(vec3.x), y(vec3.y), z(vec3.z), w(w) {}
|
||||||
|
constexpr Vec4(const float x, const float y, const float z, const float w) : x(x), y(y), z(z), w(w) {}
|
||||||
|
|
||||||
|
operator Vec2() const { return {x, y}; }
|
||||||
|
operator Vec3() const { return {x, y, z}; }
|
||||||
|
|
||||||
|
explicit operator std::string() const
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res += '(';
|
||||||
|
res += std::to_string(x);
|
||||||
|
res += ',';
|
||||||
|
res += std::to_string(y);
|
||||||
|
res += ',';
|
||||||
|
res += std::to_string(z);
|
||||||
|
res += ',';
|
||||||
|
res += std::to_string(w);
|
||||||
|
res += ')';
|
||||||
|
res += '\n';
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Mat4
|
||||||
|
{
|
||||||
|
float m[4][4];
|
||||||
|
|
||||||
|
Mat4();
|
||||||
|
Mat4(const Vec4& v0, const Vec4& v1, const Vec4& v2, const Vec4& v3);
|
||||||
|
|
||||||
|
explicit operator std::string() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Vec2 operator+(const Vec2& left, const Vec2& right);
|
||||||
|
Vec2 operator-(const Vec2& left, const Vec2& right);
|
||||||
|
Vec2 operator*(const Vec2& left, float right);
|
||||||
|
Vec2 operator+=(Vec2& left, const Vec2& right);
|
||||||
|
Vec2 operator-=(Vec2& left, const Vec2& right);
|
||||||
|
|
||||||
|
|
||||||
|
Vec3 operator+ (const Vec3& left, const Vec3& right);
|
||||||
|
Vec3 operator- (const Vec3& left, const Vec3& right);
|
||||||
|
Vec3 operator* (const Vec3& left, const Vec3& right);
|
||||||
|
Vec3 operator* (const Vec3& vec, float scalar);
|
||||||
|
Vec3 operator* (float scalar, const Vec3& vec);
|
||||||
|
Vec3 operator/ (const Vec3& left, float scalar);
|
||||||
|
Vec3 operator+= (Vec3& left, const Vec3& right);
|
||||||
|
Vec3 operator-= (Vec3& left, const Vec3& right);
|
||||||
|
|
||||||
|
Vec4 operator+ (const Vec4& left, const Vec4& right);
|
||||||
|
Vec4 operator- (const Vec4& left, const Vec4& right);
|
||||||
|
Vec4 operator* (const Mat4& mat4, const Vec4& vec4);
|
||||||
|
Vec4 operator* (float value, const Vec4& vec4);
|
||||||
|
Vec4 operator* (const Vec4& vec4, float value);
|
||||||
|
Vec4 operator/ (const Vec4& vec4, float value);
|
||||||
|
Vec4 operator+= (Vec4& left, const Vec4& right);
|
||||||
|
Vec4 operator-= (Vec4& left, const Vec4& right);
|
||||||
|
|
||||||
|
Mat4 operator* (const Mat4& left, const Mat4& right);
|
||||||
|
Mat4& operator*= (Mat4& left, const Mat4& right);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Vec3 Normalize(const Vec3& v);
|
||||||
|
Mat4 Mat4Identity();
|
||||||
|
Mat4 Mat4Translate(float tx, float ty, float tz);
|
||||||
|
Mat4 Mat4Scale(float sx, float sy, float sz);
|
||||||
|
Mat4 Mat4RotateX(float angle);
|
||||||
|
Mat4 Mat4RotateY(float angle);
|
||||||
|
Mat4 Mat4RotateZ(float angle);
|
||||||
|
|
||||||
|
Vec3 Cross(const Vec3& left, const Vec3& right);
|
||||||
|
Vec3 Lerp(const Vec3& start, const Vec3& end, float t);
|
||||||
|
Mat4 Mat4LookAt(const Vec3& eye, const Vec3& target, const Vec3& up);
|
||||||
|
|
||||||
|
float Dot(const Vec3& left, const Vec3& right);
|
||||||
|
float Lerp(float start, float end, float t);
|
||||||
|
float Clamp(float val, float min, float max);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 1/(aspect*tan(fovy/2)) 0 0 0
|
||||||
|
* 0 1/tan(fovy/2) 0 0
|
||||||
|
* 0 0 -(f+n)(f-n) -2fn/(f-n)
|
||||||
|
* 0 0 -1 0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Mat4 Mat4Perspective(float fovy, float aspect, float zNear, float zFar);
|
||||||
|
|
||||||
|
inline unsigned char FloatToUchar(const float f) { return (unsigned char)(f * 255.0f); }
|
||||||
|
inline float UCharToFloat(const unsigned char c) { return (float)c / 255.0f; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //MATH_H
|
||||||
89
src/Core/Renderer/FrameBuffer.cpp
Normal file
89
src/Core/Renderer/FrameBuffer.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "FrameBuffer.h"
|
||||||
|
|
||||||
|
#include "../Assert.h"
|
||||||
|
#include "../../Platform/WindowsWindow.h"
|
||||||
|
|
||||||
|
FrameBuffer::FrameBuffer(const int width, const int height)
|
||||||
|
: m_Width(width), m_Height(height)
|
||||||
|
{
|
||||||
|
ASSERT(width > 0 && height > 0);
|
||||||
|
|
||||||
|
m_PixelSize = width * height;
|
||||||
|
m_ColorBuffer = new Math::Vec3[m_PixelSize]();
|
||||||
|
m_DepthBuffer = new float[m_PixelSize]();
|
||||||
|
Clear({0.0f, 0.0f, 0.0f});
|
||||||
|
ClearDepth(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameBuffer::~FrameBuffer()
|
||||||
|
{
|
||||||
|
delete[] m_ColorBuffer;
|
||||||
|
delete[] m_DepthBuffer;
|
||||||
|
|
||||||
|
m_ColorBuffer = nullptr;
|
||||||
|
m_DepthBuffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Math::Vec3 FrameBuffer::GetColor(const int x, const int y) const
|
||||||
|
{
|
||||||
|
int index = GetPixelIndex(x, y);
|
||||||
|
if (index < m_PixelSize && index >= 0)
|
||||||
|
return m_ColorBuffer[index];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT(false);
|
||||||
|
return Math::Vec3{0.0f, 0.0f, 0.0f};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameBuffer::SetColor(const int x, const int y, const Math::Vec3& color) const
|
||||||
|
{
|
||||||
|
if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
|
||||||
|
{
|
||||||
|
// ASSERT(false);
|
||||||
|
return;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
const int index = GetPixelIndex(x, y);
|
||||||
|
m_ColorBuffer[index] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameBuffer::SetDepth(const int x, const int y, const float depth)
|
||||||
|
{
|
||||||
|
const int index = GetPixelIndex(x, y);
|
||||||
|
if (index < m_PixelSize && index >= 0)
|
||||||
|
m_DepthBuffer[index] = depth;
|
||||||
|
else
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
float FrameBuffer::GetDepth(const int x, const int y) const
|
||||||
|
{
|
||||||
|
int index = GetPixelIndex(x, y);
|
||||||
|
if (index < m_PixelSize && index >= 0)
|
||||||
|
return m_DepthBuffer[index];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT(false);
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FrameBuffer::Clear(const Math::Vec3& color)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_PixelSize; i++)
|
||||||
|
m_ColorBuffer[i] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameBuffer::ClearDepth(const float depth)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_PixelSize; i++)
|
||||||
|
m_DepthBuffer[i] = depth;
|
||||||
|
}
|
||||||
41
src/Core/Renderer/FrameBuffer.h
Normal file
41
src/Core/Renderer/FrameBuffer.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FRAMEBUFFER_H
|
||||||
|
#define FRAMEBUFFER_H
|
||||||
|
|
||||||
|
#include "../Math.h"
|
||||||
|
|
||||||
|
|
||||||
|
class FrameBuffer {
|
||||||
|
public:
|
||||||
|
FrameBuffer(int width, int height);
|
||||||
|
~FrameBuffer();
|
||||||
|
|
||||||
|
int GetWidth() const { return m_Width; }
|
||||||
|
int GetHeight() const { return m_Height; }
|
||||||
|
|
||||||
|
Math::Vec3 GetColor(int x, int y) const;
|
||||||
|
void SetColor(int x, int y, const Math::Vec3& color) const;
|
||||||
|
void SetDepth(int x, int y, float depth);
|
||||||
|
float GetDepth(int x, int y) const;
|
||||||
|
|
||||||
|
void Clear(const Math::Vec3& color = Math::Vec3{0.0f, 0.0f, 0.0f});
|
||||||
|
void ClearDepth(float depth = 1.0f);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline int GetPixelIndex(const int x, const int y) const { return y * m_Width + x; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_Width = 800;
|
||||||
|
int m_Height = 600;
|
||||||
|
|
||||||
|
int m_PixelSize = 0;
|
||||||
|
float* m_DepthBuffer;
|
||||||
|
Math::Vec3* m_ColorBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FRAMEBUFFER_H
|
||||||
124
src/Core/Renderer/Renderer.cpp
Normal file
124
src/Core/Renderer/Renderer.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
void Renderer::CalculateWeights(float(& screenWeights)[3], float(& weights)[3], const Math::Vec4(& fragCoords)[3], const Math::Vec2& screenPoint)
|
||||||
|
{
|
||||||
|
const Math::Vec2 ab = fragCoords[1] - fragCoords[0];
|
||||||
|
const Math::Vec2 ac = fragCoords[2] - fragCoords[0];
|
||||||
|
const Math::Vec2 ap = screenPoint - fragCoords[0];
|
||||||
|
|
||||||
|
const float factor = 1.0f/(ab.x * ac.y - ab.y * ac.x);
|
||||||
|
const float s = (ac.y * ap.x - ac.x * ap.y) * factor;
|
||||||
|
const float t = (ab.x * ap.y - ab.y * ap.x) * factor;
|
||||||
|
|
||||||
|
screenWeights[0] = 1-s-t;
|
||||||
|
screenWeights[1] = s;
|
||||||
|
screenWeights[2] = t;
|
||||||
|
|
||||||
|
const float w0 = fragCoords[0].w * screenWeights[0];
|
||||||
|
const float w1 = fragCoords[1].w * screenWeights[1];
|
||||||
|
const float w2 = fragCoords[2].w * screenWeights[2];
|
||||||
|
|
||||||
|
const float normalizer = 1.0f/(w0 + w1 + w2);
|
||||||
|
weights[0] = w0 * normalizer;
|
||||||
|
weights[1] = w1 * normalizer;
|
||||||
|
weights[2] = w2 * normalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::IsInsideTriangle(float(& weights)[3])
|
||||||
|
{
|
||||||
|
return weights[0] >= -Math::EPSILON && weights[1] >= -Math::EPSILON && weights[2] >= -Math::EPSILON;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::IsVertexVisible(const Math::Vec4& clipPos)
|
||||||
|
{
|
||||||
|
return std::fabs(clipPos.x) <= clipPos.w && std::fabs(clipPos.y) <= clipPos.w && std::fabs(clipPos.z) <= clipPos.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::IsInsidePlane(const Math::Vec4& clipPos, const Plane& plane)
|
||||||
|
{
|
||||||
|
switch (plane)
|
||||||
|
{
|
||||||
|
case Plane::POSITIVE_W: return clipPos.w >= 0.0f;
|
||||||
|
case Plane::POSITIVE_X: return clipPos.x <= +clipPos.w;
|
||||||
|
case Plane::POSITIVE_Y: return clipPos.y <= +clipPos.w;
|
||||||
|
case Plane::POSITIVE_Z: return clipPos.z <= +clipPos.w;
|
||||||
|
case Plane::NEGATIVE_X: return clipPos.x >= -clipPos.w;
|
||||||
|
case Plane::NEGATIVE_Y: return clipPos.y >= -clipPos.w;
|
||||||
|
case Plane::NEGATIVE_Z: return clipPos.z >= -clipPos.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Renderer::GetIntersectRatio(const Math::Vec4& prev, const Math::Vec4& curr, const Plane plane)
|
||||||
|
{
|
||||||
|
switch (plane)
|
||||||
|
{
|
||||||
|
case Plane::POSITIVE_W: return (prev.w - 0.0f)/(prev.w - curr.w);
|
||||||
|
case Plane::POSITIVE_X: return (prev.w - prev.x)/((prev.w - prev.x) - (curr.w - curr.x));
|
||||||
|
case Plane::NEGATIVE_X: return (prev.w + prev.x)/((prev.w + prev.x) - (curr.w + curr.x));
|
||||||
|
case Plane::POSITIVE_Y: return (prev.w - prev.y)/((prev.w - prev.y) - (curr.w - curr.y));
|
||||||
|
case Plane::NEGATIVE_Y: return (prev.w + prev.y)/((prev.w + prev.y) - (curr.w + curr.y));
|
||||||
|
case Plane::POSITIVE_Z: return (prev.w - prev.z)/((prev.w - prev.z) - (curr.w - curr.z));
|
||||||
|
case Plane::NEGATIVE_Z: return (prev.w + prev.z)/((prev.w + prev.z) - (curr.w + curr.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(false);
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::BoundingBox Renderer::GetBoundingBox(const Math::Vec4(& fragCoords)[3], const int width, const int height)
|
||||||
|
{
|
||||||
|
auto xList = { fragCoords[0].x, fragCoords[1].x, fragCoords[2].x};
|
||||||
|
auto yList = { fragCoords[0].y, fragCoords[1].y, fragCoords[2].y};
|
||||||
|
|
||||||
|
float minX = std::min<float>(xList);
|
||||||
|
float maxX = std::max<float>(xList);
|
||||||
|
float minY = std::min<float>(yList);
|
||||||
|
float maxY = std::max<float>(yList);
|
||||||
|
|
||||||
|
minX = Math::Clamp(minX, 0.0f, (float)(width - 1));
|
||||||
|
maxX = Math::Clamp(maxX, 0.0f, (float)(width - 1));
|
||||||
|
minY = Math::Clamp(minY, 0.0f, (float)(height - 1));
|
||||||
|
maxY = Math::Clamp(maxY, 0.0f, (float)(height - 1));
|
||||||
|
|
||||||
|
BoundingBox box{};
|
||||||
|
box.MinX = std::floor(minX);
|
||||||
|
box.MinY = std::floor(minY);
|
||||||
|
box.MaxX = std::ceil(maxX);
|
||||||
|
box.MaxY = std::ceil(maxY);
|
||||||
|
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::IsBackingFacing(const Math::Vec4& a,const Math::Vec4& b,const Math::Vec4& c)
|
||||||
|
{
|
||||||
|
// 逆时针为正
|
||||||
|
const float signedAera = a.x * b.y - a.y * b.x +
|
||||||
|
b.x * c.y - b.y * c.x +
|
||||||
|
c.x * a.y - c.y * a.x;
|
||||||
|
|
||||||
|
return signedAera <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::PassDepthTest(const float writeDepth, const float fDepth, const DepthFuncType depthFunc)
|
||||||
|
{
|
||||||
|
switch (depthFunc)
|
||||||
|
{
|
||||||
|
case DepthFuncType::LESS: return fDepth - writeDepth > Math::EPSILON;
|
||||||
|
case DepthFuncType::LEQUAL: return fDepth - writeDepth >= -Math::EPSILON;
|
||||||
|
case DepthFuncType::ALWAYS: return true;
|
||||||
|
}
|
||||||
|
ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
347
src/Core/Renderer/Renderer.h
Normal file
347
src/Core/Renderer/Renderer.h
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef RENDERER_H
|
||||||
|
#define RENDERER_H
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "FrameBuffer.h"
|
||||||
|
#include "ShaderBase.h"
|
||||||
|
|
||||||
|
|
||||||
|
template<typename vertex_t>
|
||||||
|
struct Triangle
|
||||||
|
{
|
||||||
|
static_assert(std::is_base_of_v<VertexBase, vertex_t>,"vertex_t 必须继承自 VertexBase");
|
||||||
|
|
||||||
|
vertex_t vertex[3];
|
||||||
|
vertex_t& operator[](size_t i) {return vertex[i];}
|
||||||
|
const vertex_t& operator[](size_t i) const {return vertex[i];}
|
||||||
|
|
||||||
|
Triangle() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DepthFuncType
|
||||||
|
{
|
||||||
|
LESS,
|
||||||
|
LEQUAL,
|
||||||
|
ALWAYS,
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename vertex_t, typename uniforms_t, typename varyings_t>
|
||||||
|
struct Program
|
||||||
|
{
|
||||||
|
using vertexShader_t = void(*)(varyings_t& , const vertex_t&, const uniforms_t&);
|
||||||
|
vertexShader_t VertexShader;
|
||||||
|
|
||||||
|
using fragmentShader_t = Math::Vec4(*)(bool& discard, const varyings_t&, const uniforms_t&);
|
||||||
|
fragmentShader_t FragmentShader;
|
||||||
|
|
||||||
|
bool EnableDoubleSided = false;
|
||||||
|
bool EnableDepthTest = true;
|
||||||
|
bool EnableWriteDepth = true;
|
||||||
|
bool EnableBlend = true;
|
||||||
|
|
||||||
|
DepthFuncType DepthFunc = DepthFuncType::LESS;
|
||||||
|
|
||||||
|
explicit Program(const vertexShader_t vertexShader, const fragmentShader_t fragmentShader) : VertexShader(vertexShader), FragmentShader(fragmentShader) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Renderer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static constexpr int VARYINGS_MAX_COUNT = 9;
|
||||||
|
|
||||||
|
|
||||||
|
enum class Plane
|
||||||
|
{
|
||||||
|
POSITIVE_W,
|
||||||
|
POSITIVE_X,
|
||||||
|
NEGATIVE_X,
|
||||||
|
POSITIVE_Y,
|
||||||
|
NEGATIVE_Y,
|
||||||
|
POSITIVE_Z,
|
||||||
|
NEGATIVE_Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundingBox {int MinX, MinY, MaxX, MaxY;};
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void CalculateWeights(float(&screenWeights)[3], float(&weights)[3], const Math::Vec4(&fragCoords)[3], const Math::Vec2& screenPoint);
|
||||||
|
static bool IsInsideTriangle(float (&weights)[3]);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static bool IsVertexVisible(const Math::Vec4& clipPos);
|
||||||
|
static bool IsInsidePlane(const Math::Vec4& clipPos, const Plane& plane);
|
||||||
|
static bool IsBackingFacing(const Math::Vec4& a, const Math::Vec4& b,const Math::Vec4& c);
|
||||||
|
static bool PassDepthTest(float writeDepth, float fDepth, DepthFuncType depthFunc);
|
||||||
|
|
||||||
|
static float GetIntersectRatio(const Math::Vec4& prev, const Math::Vec4& curr, Plane plane);
|
||||||
|
static BoundingBox GetBoundingBox(const Math::Vec4(&fragCoords)[3], int width, int height );
|
||||||
|
|
||||||
|
template<typename vertex_t, typename uniforms_t, typename varyings_t>
|
||||||
|
static void ResterizeTriangle(FrameBuffer& frameBuffer, const Program<vertex_t, uniforms_t, varyings_t>& program, const varyings_t(&varyings)[3], const uniforms_t& uniforms)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!program.EnableDoubleSided)
|
||||||
|
{
|
||||||
|
bool isBackingFacing = false;
|
||||||
|
isBackingFacing = IsBackingFacing(varyings[0].NDCPos, varyings[1].NDCPos, varyings[2].NDCPos);
|
||||||
|
if (isBackingFacing) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Math::Vec4 fragCoords[3];
|
||||||
|
fragCoords[0] = varyings[0].FragPos;
|
||||||
|
fragCoords[1] = varyings[1].FragPos;
|
||||||
|
fragCoords[2] = varyings[2].FragPos;
|
||||||
|
|
||||||
|
const Math::Vec2 size = { (float)frameBuffer.GetWidth(), (float)frameBuffer.GetHeight()};
|
||||||
|
const BoundingBox box = GetBoundingBox(fragCoords, frameBuffer.GetWidth(), frameBuffer.GetHeight());
|
||||||
|
|
||||||
|
for (int y = box.MinY; y <= box.MaxY; ++y)
|
||||||
|
{
|
||||||
|
for (int x = box.MinX; x <= box.MaxX; ++x)
|
||||||
|
{
|
||||||
|
// Varyings setup
|
||||||
|
float screenWeights[3];
|
||||||
|
float weights[3];
|
||||||
|
Math::Vec2 screenPoint{ (float)x + 0.5f, (float)y + 0.5f};
|
||||||
|
|
||||||
|
CalculateWeights(screenWeights, weights, fragCoords, screenPoint);
|
||||||
|
if (!IsInsideTriangle(weights))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
varyings_t pixVaryings;
|
||||||
|
LerpVaryings(pixVaryings, varyings, weights, size.x, size.y);
|
||||||
|
|
||||||
|
// Early depth test
|
||||||
|
if (program.EnableDepthTest)
|
||||||
|
{
|
||||||
|
const float depth = pixVaryings.FragPos.z;
|
||||||
|
const float fDepth = frameBuffer.GetDepth(x, y);
|
||||||
|
const DepthFuncType depthFunc = program.DepthFunc;
|
||||||
|
if (!PassDepthTest(depth, fDepth, depthFunc))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessPixel(frameBuffer, x, y, program, pixVaryings, uniforms);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename varyings_t>
|
||||||
|
static void CalculateNDCPos(varyings_t(&varyings)[VARYINGS_MAX_COUNT], const int vertexNum)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < vertexNum; ++i)
|
||||||
|
{
|
||||||
|
float w = varyings[i].ClipPos.w;
|
||||||
|
varyings[i].NDCPos = varyings[i].ClipPos / w;
|
||||||
|
varyings[i].NDCPos.w = 1.0f / w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename varyings_t>
|
||||||
|
static void CalculateFragPos(varyings_t(&varyings)[VARYINGS_MAX_COUNT],
|
||||||
|
const int vertexNum,
|
||||||
|
const float width,
|
||||||
|
const float height)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < vertexNum; ++i)
|
||||||
|
{
|
||||||
|
float x = ((varyings[i].NDCPos.x + 1.0f) * 0.5f * width);
|
||||||
|
float y = ((varyings[i].NDCPos.y + 1.0f) * 0.5f * height);
|
||||||
|
float z = ((varyings[i].NDCPos.z + 1.0f) * 0.5f);
|
||||||
|
float w = varyings[i].NDCPos.w;
|
||||||
|
// float w = varyings[i].FragPos.w;
|
||||||
|
|
||||||
|
varyings[i].FragPos.x = x;
|
||||||
|
varyings[i].FragPos.y = y;
|
||||||
|
varyings[i].FragPos.z = z;
|
||||||
|
varyings[i].FragPos.w = w;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename varyings_t>
|
||||||
|
static void LerpVaryings(varyings_t& out, const varyings_t& start, const varyings_t& end, const float ratio)
|
||||||
|
{
|
||||||
|
constexpr uint32_t floatNum = sizeof(varyings_t) / sizeof(float);
|
||||||
|
float* startFloat = (float*)&start;
|
||||||
|
float* endFloat = (float*)&end;
|
||||||
|
float* outFloat = (float*)&out;
|
||||||
|
|
||||||
|
for (int i = 0; i < floatNum; ++i)
|
||||||
|
{
|
||||||
|
outFloat[i] = Math::Lerp(startFloat[i], endFloat[i], ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename varyings_t>
|
||||||
|
static void LerpVaryings(varyings_t& out, const varyings_t(&varyings)[3], float(&weigths)[3], const int width, const int height)
|
||||||
|
{
|
||||||
|
out.ClipPos = varyings[0].ClipPos * weigths[0] +
|
||||||
|
varyings[1].ClipPos * weigths[1] +
|
||||||
|
varyings[2].ClipPos * weigths[2];
|
||||||
|
|
||||||
|
out.NDCPos = out.ClipPos / out.ClipPos.w;
|
||||||
|
out.NDCPos.w = 1.0f / out.ClipPos.w;
|
||||||
|
|
||||||
|
out.FragPos.x = (out.NDCPos.x + 1.0f) * 0.5f * width;
|
||||||
|
out.FragPos.y = (out.NDCPos.y + 1.0f) * 0.5f * height;
|
||||||
|
out.FragPos.z = (out.NDCPos.z + 1.0f) * 0.5f;
|
||||||
|
out.FragPos.w = out.NDCPos.w;
|
||||||
|
|
||||||
|
constexpr uint32_t floatOffset = sizeof(Math::Vec4)*3 / sizeof(float);
|
||||||
|
constexpr uint32_t floatNum = sizeof(varyings_t) / sizeof(float);
|
||||||
|
float* v0 = (float*)&varyings[0];
|
||||||
|
float* v1 = (float*)&varyings[1];
|
||||||
|
float* v2 = (float*)&varyings[2];
|
||||||
|
float* outFloat = (float*)&out;
|
||||||
|
|
||||||
|
for (int i = floatOffset; i < floatNum; ++i)
|
||||||
|
{
|
||||||
|
outFloat[i] = v0[i] * weigths[0] + v1[i] * weigths[1] + v2[i] * weigths[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename varyings_t>
|
||||||
|
static int ClipAginstPlane(varyings_t(& outVaryings)[VARYINGS_MAX_COUNT], const varyings_t(& inVaryings)[VARYINGS_MAX_COUNT], const Plane plane,
|
||||||
|
const int inVertexNum)
|
||||||
|
{
|
||||||
|
ASSERT(inVertexNum >= 3);
|
||||||
|
|
||||||
|
int outVaryingNum = 0;
|
||||||
|
for (int i = 0; i < inVertexNum; ++i)
|
||||||
|
{
|
||||||
|
int prevIndex = (i - 1 + inVertexNum) % inVertexNum;
|
||||||
|
int currIndex = i;
|
||||||
|
|
||||||
|
const varyings_t& prevVarings = inVaryings[prevIndex];
|
||||||
|
const varyings_t& currVarings = inVaryings[currIndex];
|
||||||
|
|
||||||
|
const bool prevInside = IsInsidePlane(prevVarings.ClipPos, plane);
|
||||||
|
const bool currInside = IsInsidePlane(currVarings.ClipPos, plane);
|
||||||
|
|
||||||
|
if (prevInside != currInside)
|
||||||
|
{
|
||||||
|
float ratio = GetIntersectRatio(prevVarings.ClipPos, currVarings.ClipPos, plane);
|
||||||
|
LerpVaryings(outVaryings[outVaryingNum], prevVarings, currVarings, ratio);
|
||||||
|
outVaryingNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currInside)
|
||||||
|
{
|
||||||
|
outVaryings[outVaryingNum] = inVaryings[currIndex];
|
||||||
|
outVaryingNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT(outVaryingNum <= VARYINGS_MAX_COUNT);
|
||||||
|
return outVaryingNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename varyings_t>
|
||||||
|
static int Clip(varyings_t(& varyings)[VARYINGS_MAX_COUNT])
|
||||||
|
{
|
||||||
|
const bool v0_Visable = IsVertexVisible(varyings[0].ClipPos);
|
||||||
|
const bool v1_Visable = IsVertexVisible(varyings[1].ClipPos);
|
||||||
|
const bool v2_Visable = IsVertexVisible(varyings[2].ClipPos);
|
||||||
|
if (v0_Visable && v1_Visable && v2_Visable) return 3;
|
||||||
|
|
||||||
|
int vertexNum = 3;
|
||||||
|
varyings_t varyings_[VARYINGS_MAX_COUNT];
|
||||||
|
|
||||||
|
vertexNum = ClipAginstPlane(varyings_, varyings, Plane::NEGATIVE_Z, vertexNum);
|
||||||
|
if (vertexNum == 0) return 0;
|
||||||
|
vertexNum = ClipAginstPlane(varyings, varyings_, Plane::POSITIVE_Z, vertexNum);
|
||||||
|
if (vertexNum == 0) return 0;
|
||||||
|
vertexNum = ClipAginstPlane(varyings_, varyings, Plane::NEGATIVE_X, vertexNum);
|
||||||
|
if (vertexNum == 0) return 0;
|
||||||
|
vertexNum = ClipAginstPlane(varyings, varyings_, Plane::POSITIVE_X, vertexNum);
|
||||||
|
if (vertexNum == 0) return 0;
|
||||||
|
vertexNum = ClipAginstPlane(varyings_, varyings, Plane::NEGATIVE_Y, vertexNum);
|
||||||
|
if (vertexNum == 0) return 0;
|
||||||
|
vertexNum = ClipAginstPlane(varyings, varyings_, Plane::POSITIVE_Y, vertexNum);
|
||||||
|
if (vertexNum == 0) return 0;
|
||||||
|
|
||||||
|
return vertexNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename vertex_t, typename uniform_t, typename varyings_t>
|
||||||
|
static void Draw(FrameBuffer& frameBuffer, const Program<vertex_t, uniform_t, varyings_t>& program,
|
||||||
|
const Triangle<vertex_t>& triangle, const uniform_t& uniforms)
|
||||||
|
{
|
||||||
|
|
||||||
|
static_assert(std::is_base_of_v<VertexBase, vertex_t>, "vertex_t 必须继承 BlinnVertex");
|
||||||
|
static_assert(std::is_base_of_v<VaryingBase, varyings_t>, "varyings_t 必须继承 BlinnVaryings");
|
||||||
|
|
||||||
|
// Vertex Shading & Projection
|
||||||
|
varyings_t varyings[VARYINGS_MAX_COUNT];
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
program.VertexShader(varyings[i], triangle[i], uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clipping
|
||||||
|
int vertexNum = Clip(varyings);
|
||||||
|
|
||||||
|
// Screen Mapping
|
||||||
|
CalculateNDCPos(varyings, vertexNum);
|
||||||
|
int fWidth = frameBuffer.GetWidth();
|
||||||
|
int fHeight = frameBuffer.GetHeight();
|
||||||
|
|
||||||
|
CalculateFragPos(varyings, vertexNum, (float)fWidth, (float)fHeight);
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexNum - 2; ++i)
|
||||||
|
{
|
||||||
|
varyings_t triVaryings[3];
|
||||||
|
triVaryings[0] = varyings[0];
|
||||||
|
triVaryings[1] = varyings[i + 1];
|
||||||
|
triVaryings[2] = varyings[i + 2];
|
||||||
|
|
||||||
|
ResterizeTriangle(frameBuffer, program, triVaryings, uniforms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename vertex_t, typename uniform_t, typename varyings_t>
|
||||||
|
static void ProcessPixel(FrameBuffer& framebuffer, const int x, const int y, const Program<vertex_t, uniform_t, varyings_t>& program, const varyings_t& varyings, const uniform_t& uniforms)
|
||||||
|
{
|
||||||
|
bool discard = false;
|
||||||
|
|
||||||
|
Math::Vec4 color{0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
color = program.FragmentShader(discard, varyings, uniforms);
|
||||||
|
if (discard) return;
|
||||||
|
|
||||||
|
color.x = Math::Clamp(color.x , 0.0f, 1.0f);
|
||||||
|
color.y = Math::Clamp(color.y , 0.0f, 1.0f);
|
||||||
|
color.z = Math::Clamp(color.z , 0.0f, 1.0f);
|
||||||
|
color.w = Math::Clamp(color.w , 0.0f, 1.0f);
|
||||||
|
|
||||||
|
if (program.EnableBlend)
|
||||||
|
{
|
||||||
|
Math::Vec3 dstColor = framebuffer.GetColor(x, y);
|
||||||
|
Math::Vec3 srcColor = color;
|
||||||
|
float alpha = color.w;
|
||||||
|
color = { Math::Lerp(dstColor, srcColor, alpha), 1.0f };
|
||||||
|
framebuffer.SetColor(x, y, color);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
framebuffer.SetColor(x, y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program.EnableWriteDepth)
|
||||||
|
{
|
||||||
|
const float depth = varyings.FragPos.z;
|
||||||
|
framebuffer.SetDepth(x, y, depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //RENDERER_H
|
||||||
5
src/Core/Renderer/ShaderBase.cpp
Normal file
5
src/Core/Renderer/ShaderBase.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ShaderBase.h"
|
||||||
32
src/Core/Renderer/ShaderBase.h
Normal file
32
src/Core/Renderer/ShaderBase.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SHADERBASE_H
|
||||||
|
#define SHADERBASE_H
|
||||||
|
#include "../Math.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct VertexBase
|
||||||
|
{
|
||||||
|
Math::Vec4 ModelPosition;
|
||||||
|
|
||||||
|
explicit operator const std::string() const { return "ModedPos: " + (std::string)ModelPosition; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VaryingBase
|
||||||
|
{
|
||||||
|
Math::Vec4 ClipPos { 0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
Math::Vec4 NDCPos { 0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
Math::Vec4 FragPos { 0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UniformBase
|
||||||
|
{
|
||||||
|
Math::Mat4 MVP;
|
||||||
|
|
||||||
|
|
||||||
|
explicit operator const std::string() const { return "MVP: " + (std::string)MVP; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //SHADERBASE_H
|
||||||
103
src/Core/Texture/Texture.cpp
Normal file
103
src/Core/Texture/Texture.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-29.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "../../vendor/stb/stb_image.h"
|
||||||
|
|
||||||
|
Texture::Texture(const std::string& filepath)
|
||||||
|
: m_Filepath(filepath)
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::~Texture()
|
||||||
|
{
|
||||||
|
if (m_Data)
|
||||||
|
{
|
||||||
|
delete[] m_Data;
|
||||||
|
m_Data = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vec4 Texture::Sample(Math::Vec2 texCoords) const
|
||||||
|
{
|
||||||
|
const float vx = Math::Clamp(texCoords.x, 0.0f, 1.0f);
|
||||||
|
const float vy = Math::Clamp(texCoords.y, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
const int x = (m_Width - 1) * vx + 0.5f;
|
||||||
|
const int y = (m_Height - 1) * vy + 0.5f;
|
||||||
|
|
||||||
|
const int index = x + y * m_Width;
|
||||||
|
|
||||||
|
return m_Data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture* Texture::CreateTexture(const std::string& filepath)
|
||||||
|
{
|
||||||
|
const std::ifstream file(filepath);
|
||||||
|
if (file.good())
|
||||||
|
{
|
||||||
|
return new Texture(filepath);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::Init()
|
||||||
|
{
|
||||||
|
int width, height, channels;
|
||||||
|
|
||||||
|
stbi_set_flip_vertically_on_load(1);
|
||||||
|
|
||||||
|
const stbi_uc* data = stbi_load(m_Filepath.c_str(), &width, &height, &channels, 0);
|
||||||
|
ASSERT(data);
|
||||||
|
|
||||||
|
m_Width = width;
|
||||||
|
m_Height = height;
|
||||||
|
m_Channels = channels;
|
||||||
|
const int size = width * height;
|
||||||
|
m_Data = new Math::Vec4[size];
|
||||||
|
|
||||||
|
if (channels == 4)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
m_Data[i].x = Math::UCharToFloat(data[i * 4 + 0]);
|
||||||
|
m_Data[i].y = Math::UCharToFloat(data[i * 4 + 1]);
|
||||||
|
m_Data[i].z = Math::UCharToFloat(data[i * 4 + 2]);
|
||||||
|
m_Data[i].w = Math::UCharToFloat(data[i * 4 + 3]);
|
||||||
|
}
|
||||||
|
}else if (channels == 3)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
m_Data[i].x = Math::UCharToFloat(data[i * 3 + 0]);
|
||||||
|
m_Data[i].y = Math::UCharToFloat(data[i * 3 + 1]);
|
||||||
|
m_Data[i].z = Math::UCharToFloat(data[i * 3 + 2]);
|
||||||
|
m_Data[i].w = 0.0f;
|
||||||
|
}
|
||||||
|
}else if (channels == 2)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
m_Data[i].x = Math::UCharToFloat(data[i * 2 + 0]);
|
||||||
|
m_Data[i].y = Math::UCharToFloat(data[i * 2 + 1]);
|
||||||
|
m_Data[i].z = 0.0f;
|
||||||
|
m_Data[i].w = 0.0f;
|
||||||
|
}
|
||||||
|
}else if (channels == 1)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
m_Data[i].x = Math::UCharToFloat(data[i * 1 + 0]);
|
||||||
|
m_Data[i].y = 0.0f;
|
||||||
|
m_Data[i].z = 0.0f;
|
||||||
|
m_Data[i].w = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_image_free((void*)data);
|
||||||
|
}
|
||||||
34
src/Core/Texture/Texture.h
Normal file
34
src/Core/Texture/Texture.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-29.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TEXTURE_H
|
||||||
|
#define TEXTURE_H
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../Math.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Texture {
|
||||||
|
public:
|
||||||
|
Texture(const std::string& filepath);
|
||||||
|
~Texture();
|
||||||
|
|
||||||
|
Math::Vec4 Sample(Math::Vec2 texCoords) const;
|
||||||
|
static Texture* CreateTexture(const std::string& filepath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_Width, m_Height, m_Channels;
|
||||||
|
std::string m_Filepath;
|
||||||
|
|
||||||
|
Math::Vec4* m_Data;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //TEXTURE_H
|
||||||
26
src/Core/Window/Window.cpp
Normal file
26
src/Core/Window/Window.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Window.h"
|
||||||
|
|
||||||
|
#include "../Assert.h"
|
||||||
|
#include "../../Platform/WindowsWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
Window::Window(const std::string& name, const int width, const int height)
|
||||||
|
: m_Name(name), m_Width(width), m_Height(height)
|
||||||
|
{
|
||||||
|
ASSERT((m_Width>0) && (m_Height>0));
|
||||||
|
memset(m_Keys, KEY_RELEASE, sizeof(m_Keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* Window::Create(const std::string& name, const int width, const int height)
|
||||||
|
{
|
||||||
|
return new WindowsWindow(name, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::PollInputEvents()
|
||||||
|
{
|
||||||
|
WindowsWindow::PollInputEvents();
|
||||||
|
}
|
||||||
46
src/Core/Window/Window.h
Normal file
46
src/Core/Window/Window.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WINDOW_H
|
||||||
|
#define WINDOW_H
|
||||||
|
|
||||||
|
#include "../Renderer/FrameBuffer.h"
|
||||||
|
#include "../Input/InputCodes.h"
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Window {
|
||||||
|
public:
|
||||||
|
static Window* Create(const std::string& name, int width, int height);
|
||||||
|
|
||||||
|
Window(const std::string& name, int width, int height);
|
||||||
|
virtual ~Window() = default;
|
||||||
|
|
||||||
|
virtual void Show() const = 0;
|
||||||
|
virtual void DrawFrameBuffer(const FrameBuffer& frameBuffer) = 0;
|
||||||
|
|
||||||
|
bool Close() const { return m_Closed; }
|
||||||
|
|
||||||
|
inline char GetKey(const uint32_t key) const { return m_Keys[key]; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void Init() = 0;
|
||||||
|
virtual void Terminate() = 0;
|
||||||
|
static void PollInputEvents();
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
std::string m_Name;
|
||||||
|
int m_Width, m_Height;
|
||||||
|
bool m_Closed = true;
|
||||||
|
|
||||||
|
char m_Keys[KEY_MAX_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //WINDOW_H
|
||||||
220
src/Platform/WindowsWindow.cpp
Normal file
220
src/Platform/WindowsWindow.cpp
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "../Platform/WindowsWindow.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "../Core/Assert.h"
|
||||||
|
#define WINDOW_ENTRY_NAME "Entry"
|
||||||
|
#define WINDOW_CLASS_NAME "Class"
|
||||||
|
|
||||||
|
|
||||||
|
bool WindowsWindow::s_Inited = false;
|
||||||
|
|
||||||
|
void WindowsWindow::Init()
|
||||||
|
{
|
||||||
|
ASSERT(!s_Inited);
|
||||||
|
Register();
|
||||||
|
s_Inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::Terminate()
|
||||||
|
{
|
||||||
|
ASSERT(s_Inited);
|
||||||
|
UnRegister();
|
||||||
|
s_Inited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::Register()
|
||||||
|
{
|
||||||
|
ATOM atom;
|
||||||
|
WNDCLASS wc = { 0 };
|
||||||
|
|
||||||
|
wc.cbClsExtra = 0;
|
||||||
|
wc.cbWndExtra = 0;
|
||||||
|
wc.hbrBackground = (HBRUSH)(WHITE_BRUSH); // background Color
|
||||||
|
wc.hCursor = NULL; // default cursor
|
||||||
|
wc.hIcon = NULL; // default Icon
|
||||||
|
wc.hInstance = GetModuleHandle(NULL);
|
||||||
|
wc.lpfnWndProc = WindowsWindow::WindowProc; // window process function
|
||||||
|
wc.lpszClassName = WINDOW_CLASS_NAME;
|
||||||
|
wc.style = CS_HREDRAW | CS_VREDRAW; // redraw when window resize
|
||||||
|
wc.lpszMenuName = NULL; // no Menu
|
||||||
|
atom = RegisterClass(&wc); // register Window
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::UnRegister()
|
||||||
|
{
|
||||||
|
UnregisterClass(WINDOW_CLASS_NAME, GetModuleHandle(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::KeyPressImpl(WindowsWindow* window, const WPARAM wparam, const char state)
|
||||||
|
{
|
||||||
|
if (wparam >= '0' && wparam <= '9')
|
||||||
|
{
|
||||||
|
window->m_Keys[wparam] = state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wparam >= 'A' && wparam <= 'Z')
|
||||||
|
{
|
||||||
|
window->m_Keys[wparam] = state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (wparam)
|
||||||
|
{
|
||||||
|
case VK_SPACE: window->m_Keys[KEY_SPACE] = state; break;
|
||||||
|
case VK_SHIFT:
|
||||||
|
{
|
||||||
|
window->m_Keys[KEY_LEFT_SHIFT] = state;
|
||||||
|
window->m_Keys[KEY_RIGHT_SHIFT] = state;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VK_CONTROL:
|
||||||
|
{
|
||||||
|
window->m_Keys[KEY_LEFT_CONTROL] = state;
|
||||||
|
window->m_Keys[KEY_RIGHT_CONTROL] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VK_ESCAPE: window->m_Keys[KEY_ESCAPE] = state; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WindowsWindow::WindowsWindow(const std::string& name, const int width, const int height)
|
||||||
|
: Window(name, width, height), m_Buffer(nullptr)
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
|
||||||
|
ASSERT(s_Inited, "not Init");
|
||||||
|
|
||||||
|
DWORD style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||||
|
RECT rect;
|
||||||
|
rect.left = 0;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.right = width;
|
||||||
|
rect.bottom = height;
|
||||||
|
|
||||||
|
AdjustWindowRect(&rect, style, false);
|
||||||
|
|
||||||
|
m_hWnd = CreateWindow(
|
||||||
|
WINDOW_CLASS_NAME,
|
||||||
|
m_Name.c_str(),
|
||||||
|
style,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
0,
|
||||||
|
rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
GetModuleHandle(NULL),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
ASSERT(m_hWnd != NULL);
|
||||||
|
m_Closed = false;
|
||||||
|
SetProp(m_hWnd, WINDOW_ENTRY_NAME, this);
|
||||||
|
|
||||||
|
HDC hdc = GetDC(m_hWnd);
|
||||||
|
m_MemoryDC = CreateCompatibleDC(hdc);
|
||||||
|
|
||||||
|
BITMAPINFOHEADER bitHeader = {};
|
||||||
|
HBITMAP newBitMap;
|
||||||
|
HBITMAP oldBitMap;
|
||||||
|
|
||||||
|
bitHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
bitHeader.biWidth = ((long)width);
|
||||||
|
bitHeader.biHeight = -((long)height);
|
||||||
|
bitHeader.biPlanes = 1;
|
||||||
|
bitHeader.biBitCount = 24;
|
||||||
|
bitHeader.biCompression = BI_RGB;
|
||||||
|
|
||||||
|
// alloc memory
|
||||||
|
newBitMap = CreateDIBSection(m_MemoryDC, (BITMAPINFO*)&bitHeader, DIB_RGB_COLORS, (void**)&m_Buffer, nullptr, 0);
|
||||||
|
ASSERT(newBitMap != NULL);
|
||||||
|
|
||||||
|
constexpr int channelCount = 3;
|
||||||
|
|
||||||
|
int size = width * height * channelCount * sizeof(unsigned char);
|
||||||
|
memset(m_Buffer, 0, size);
|
||||||
|
oldBitMap = (HBITMAP)SelectObject(m_MemoryDC, newBitMap);
|
||||||
|
|
||||||
|
DeleteObject(oldBitMap);
|
||||||
|
ReleaseDC(m_hWnd, m_MemoryDC);
|
||||||
|
|
||||||
|
WindowsWindow::Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsWindow::~WindowsWindow()
|
||||||
|
{
|
||||||
|
ShowWindow(m_hWnd, SW_HIDE);
|
||||||
|
RemoveProp(m_hWnd, WINDOW_ENTRY_NAME);
|
||||||
|
DeleteDC(m_MemoryDC);
|
||||||
|
DeleteObject(m_MemoryDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::Show() const
|
||||||
|
{
|
||||||
|
HDC windowDC = GetDC(m_hWnd);
|
||||||
|
BitBlt(windowDC, 0, 0, m_Width, m_Height, m_MemoryDC, 0, 0, SRCCOPY);
|
||||||
|
ShowWindow(m_hWnd, SW_SHOW);
|
||||||
|
ReleaseDC(m_hWnd, windowDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::DrawFrameBuffer(const FrameBuffer& frameBuffer)
|
||||||
|
{
|
||||||
|
const int fWidth = frameBuffer.GetWidth();
|
||||||
|
const int fHeight = frameBuffer.GetHeight();
|
||||||
|
const int width = m_Width < fWidth ? m_Width : fWidth;
|
||||||
|
const int height = m_Height < fHeight ? m_Height : fHeight;
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < width; j++)
|
||||||
|
{
|
||||||
|
// 反转RGB显示
|
||||||
|
constexpr int channelCount = 3;
|
||||||
|
constexpr int rChannel = 2;
|
||||||
|
constexpr int gChannel = 1;
|
||||||
|
constexpr int bChannel = 0;
|
||||||
|
|
||||||
|
Math::Vec3 color = frameBuffer.GetColor(j, fHeight - i - 1);
|
||||||
|
const int pixelStart = (i * width + j) * channelCount;
|
||||||
|
const int rIndex = pixelStart + rChannel;
|
||||||
|
const int gIndex = pixelStart + gChannel;
|
||||||
|
const int bIndex = pixelStart + bChannel;
|
||||||
|
|
||||||
|
m_Buffer[rIndex] = Math::FloatToUchar(color.r);
|
||||||
|
m_Buffer[gIndex] = Math::FloatToUchar(color.g);
|
||||||
|
m_Buffer[bIndex] = Math::FloatToUchar(color.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT WindowsWindow::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
WindowsWindow* window = (WindowsWindow*)GetProp(hWnd, WINDOW_ENTRY_NAME);
|
||||||
|
if (window == nullptr) return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case WM_DESTROY: window->m_Closed = true; return 0;
|
||||||
|
case WM_KEYDOWN: KeyPressImpl(window, wParam, KEY_PRESS); return 0;
|
||||||
|
case WM_KEYUP: KeyPressImpl(window, wParam, KEY_RELEASE); return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsWindow::PollInputEvents()
|
||||||
|
{
|
||||||
|
MSG message;
|
||||||
|
while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
|
||||||
|
{
|
||||||
|
TranslateMessage(&message);
|
||||||
|
DispatchMessage(&message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
43
src/Platform/WindowsWindow.h
Normal file
43
src/Platform/WindowsWindow.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WINDOWSWINDOW_H
|
||||||
|
#define WINDOWSWINDOW_H
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include "../Core/Window/Window.h"
|
||||||
|
|
||||||
|
|
||||||
|
class WindowsWindow : public Window{
|
||||||
|
public:
|
||||||
|
WindowsWindow(const std::string& name, int width, int height);
|
||||||
|
virtual ~WindowsWindow();
|
||||||
|
|
||||||
|
virtual void Show() const override;
|
||||||
|
virtual void DrawFrameBuffer(const FrameBuffer& frameBuffer) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Init() override;
|
||||||
|
void Terminate() override;
|
||||||
|
static void PollInputEvents();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void Register();
|
||||||
|
static void UnRegister();
|
||||||
|
|
||||||
|
static void KeyPressImpl(WindowsWindow* window, WPARAM wparam, char state);
|
||||||
|
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
HWND m_hWnd;
|
||||||
|
HDC m_MemoryDC;
|
||||||
|
|
||||||
|
unsigned char* m_Buffer;
|
||||||
|
static bool s_Inited;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //WINDOWSWINDOW_H
|
||||||
50
src/Shader/DemoShader.cpp
Normal file
50
src/Shader/DemoShader.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "DemoShader.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
void DemoVertexShader(BlinnVaryings& varyings, const BlinnVertex& vertex, const BlinnUniform& uniform)
|
||||||
|
{
|
||||||
|
varyings.ClipPos = uniform.MVP * vertex.ModelPosition;
|
||||||
|
varyings.TexCoord = vertex.TexCoord;
|
||||||
|
varyings.WorldPos = uniform.Model * vertex.ModelPosition;
|
||||||
|
varyings.WorldNormal = uniform.ModelNormalToWorld * Math::Vec4{ vertex.ModelNormal, 0.0f};
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vec4 DemoFragmentShader(bool& discard, const BlinnVaryings& varyings, const BlinnUniform& uniform)
|
||||||
|
{
|
||||||
|
discard = false;
|
||||||
|
|
||||||
|
const Math::Vec3 cameraPos = uniform.Camera;
|
||||||
|
const Math::Vec3 lightPos = uniform.LightPos;
|
||||||
|
const Math::Vec3 worldPos = varyings.WorldPos;
|
||||||
|
|
||||||
|
Math::Vec3 worldNormal = Math::Normalize(varyings.WorldNormal);
|
||||||
|
const Math::Vec3 viewDir = Math::Normalize(cameraPos - worldPos);
|
||||||
|
const Math::Vec3 lightDir = Math::Normalize(lightPos - worldPos);
|
||||||
|
|
||||||
|
Math::Vec3 halfDir = Math::Normalize(viewDir + lightDir);
|
||||||
|
|
||||||
|
Math::Vec3 ambient = uniform.LightAmbient;
|
||||||
|
Math::Vec3 specularStrength {1.0f, 1.0f, 1.0f};
|
||||||
|
Math::Vec3 diffColor {1.0f , 1.0f , 1.0f};
|
||||||
|
if (uniform.Diffuse)
|
||||||
|
{
|
||||||
|
const Math::Vec2& texCoord = varyings.TexCoord;
|
||||||
|
diffColor = uniform.Diffuse->Sample(texCoord);
|
||||||
|
ambient = ambient * diffColor;
|
||||||
|
if (uniform.Specular)
|
||||||
|
specularStrength = uniform.Specular->Sample(texCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Math::Vec3 diffuse = std::max(0.0f, Math::Dot(worldNormal, lightDir)) * uniform.LightDiffuse * diffColor;
|
||||||
|
Math::Vec3 specular = (float)pow(std::max(0.0f, Math::Dot(halfDir, worldNormal)), uniform.Shininess) * uniform.LightSPecular * specularStrength;
|
||||||
|
|
||||||
|
Math::Vec3 result = (ambient + diffuse + specular);
|
||||||
|
|
||||||
|
return {result, 1.0f};
|
||||||
|
}
|
||||||
44
src/Shader/DemoShader.h
Normal file
44
src/Shader/DemoShader.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-28.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BLINNSHADER_H
|
||||||
|
#define BLINNSHADER_H
|
||||||
|
#include "../Core/Renderer/ShaderBase.h"
|
||||||
|
#include "../Core/Texture/Texture.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct BlinnVertex : public VertexBase
|
||||||
|
{
|
||||||
|
Math::Vec3 ModelNormal;
|
||||||
|
Math::Vec2 TexCoord {0.0f, 0.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlinnVaryings : public VaryingBase
|
||||||
|
{
|
||||||
|
Math::Vec3 WorldPos;
|
||||||
|
Math::Vec3 WorldNormal;
|
||||||
|
Math::Vec2 TexCoord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlinnUniform : public UniformBase
|
||||||
|
{
|
||||||
|
Math::Mat4 Model;
|
||||||
|
Math::Mat4 ModelNormalToWorld;
|
||||||
|
Math::Vec3 LightPos { 0.0f, 1.0f, 2.0f};
|
||||||
|
Math::Vec3 LightAmbient { 0.3f, 0.3f, 0.3f};
|
||||||
|
Math::Vec3 LightDiffuse { 0.5f, 0.5f, 0.5f};
|
||||||
|
Math::Vec3 LightSPecular { 1.0f, 1.0f, 1.0f};
|
||||||
|
Math::Vec3 ObjectColor { 1.0f, 1.0f, 1.0f};
|
||||||
|
Math::Vec3 Camera;
|
||||||
|
|
||||||
|
Texture* Diffuse = nullptr;
|
||||||
|
Texture* Specular = nullptr;
|
||||||
|
|
||||||
|
float Shininess = 64.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
void DemoVertexShader(BlinnVaryings& varyings, const BlinnVertex& vertex, const BlinnUniform& uniform);
|
||||||
|
Math::Vec4 DemoFragmentShader(bool& discard, const BlinnVaryings& varyings, const BlinnUniform& uniform);
|
||||||
|
|
||||||
|
#endif //BLINNSHADER_H
|
||||||
8
src/main.cpp
Normal file
8
src/main.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "Application/Application.h"
|
||||||
|
|
||||||
|
int main(const int argc, char **argv)
|
||||||
|
{
|
||||||
|
Application app("demo", 400, 400, {argc, argv});
|
||||||
|
app.Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
6
src/vendor/stb/stb_image.cpp
vendored
Normal file
6
src/vendor/stb/stb_image.cpp
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//
|
||||||
|
// Created by sfd on 25-7-29.
|
||||||
|
//
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
7988
src/vendor/stb/stb_image.h
vendored
Normal file
7988
src/vendor/stb/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user