Minor refactoring
This commit is contained in:
1 parent
0a5a3b653d
commit
592e008914
43 files changed
+3589
-3594
No files matched your search
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
struct MeshGroup
|
||||
{
|
||||
std::vector<std::shared_ptr<Texture>> textures;
|
||||
std::vector<VertexDataStruct> meshes;
|
||||
std::vector<VertexRenderStruct> renderMeshes;
|
||||
};
|
||||
|
||||
struct AnimatedModel
|
||||
{
|
||||
std::vector<MeshGroup> parts;
|
||||
|
||||
void RefreshRenderMeshes()
|
||||
{
|
||||
for (int i = 0; i < parts.size(); i++)
|
||||
{
|
||||
parts[i].renderMeshes.resize(parts[i].meshes.size());
|
||||
|
||||
for (int j = 0; j < parts[i].meshes.size(); j++)
|
||||
{
|
||||
parts[i].renderMeshes[j].AssignFrom(parts[i].meshes[j]);
|
||||
parts[i].renderMeshes[j].RefreshVBO();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifdef AUDIO
|
||||
|
||||
#include "AudioPlayerAsync.h"
|
||||
|
||||
|
||||
AudioPlayerAsync::AudioPlayerAsync() : worker(&AudioPlayerAsync::workerThread, this) {}
|
||||
|
||||
AudioPlayerAsync::~AudioPlayerAsync() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
stop = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
worker.join();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::stopAsync() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
audioPlayer->stop();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::resetAsync() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
audioPlayer.reset();
|
||||
audioPlayer = std::make_unique<AudioPlayer>();
|
||||
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::playSoundAsync(std::string soundName) {
|
||||
|
||||
soundNameMutex.lock();
|
||||
latestSoundName = soundName;
|
||||
soundNameMutex.unlock();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
if (audioPlayer) {
|
||||
audioPlayer->playSound(latestSoundName);
|
||||
}
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::playMusicAsync(std::string musicName) {
|
||||
|
||||
musicNameMutex.lock();
|
||||
latestMusicName = musicName;
|
||||
musicNameMutex.unlock();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
if (audioPlayer) {
|
||||
audioPlayer->playMusic(latestMusicName);
|
||||
}
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::workerThread() {
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock, [this]() { return !taskQueue.empty() || stop; });
|
||||
|
||||
if (stop && taskQueue.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
task = taskQueue.front();
|
||||
taskQueue.pop();
|
||||
}
|
||||
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include "cmakeaudioplayer/include/AudioPlayer.hpp"
|
||||
|
||||
|
||||
class AudioPlayerAsync {
|
||||
public:
|
||||
AudioPlayerAsync();
|
||||
~AudioPlayerAsync();
|
||||
|
||||
void resetAsync();
|
||||
|
||||
void playSoundAsync(std::string soundName);
|
||||
|
||||
void playMusicAsync(std::string musicName);
|
||||
|
||||
void stopAsync();
|
||||
|
||||
void exit()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
std::thread worker;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AudioPlayer> audioPlayer;
|
||||
//std::mutex audioPlayerMutex;
|
||||
|
||||
std::mutex soundNameMutex;
|
||||
std::mutex musicNameMutex;
|
||||
|
||||
std::string latestSoundName;
|
||||
std::string latestMusicName;
|
||||
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
std::queue<std::function<void()>> taskQueue;
|
||||
bool stop = false;
|
||||
|
||||
void workerThread();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,719 @@
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
int getIndexByValue(const std::string& name, const std::vector<std::string>& words)
|
||||
{
|
||||
for (int i = 0; i < words.size(); i++)
|
||||
{
|
||||
if (words[i] == name)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void BoneSystem::LoadFromFile(const std::string& fileName, const std::string& ZIPFileName)
|
||||
{
|
||||
std::ifstream filestream;
|
||||
std::istringstream zipStream;
|
||||
|
||||
if (!ZIPFileName.empty())
|
||||
{
|
||||
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||
std::string fileContents(fileData.begin(), fileData.end());
|
||||
zipStream.str(fileContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
filestream.open(fileName);
|
||||
}
|
||||
|
||||
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
||||
|
||||
//Skip first 5 lines
|
||||
std::string tempLine;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
}
|
||||
std::getline(f, tempLine);
|
||||
|
||||
static const std::regex pattern_count(R"(\d+)");
|
||||
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
|
||||
static const std::regex pattern_int(R"([-]?\d+)");
|
||||
static const std::regex pattern_boneChildren(R"(\'([^\']+)\')");
|
||||
static const std::regex pattern_bone_weight(R"(\'([^\']+)\'.*?([-]?\d+\.\d+))");
|
||||
|
||||
std::smatch match;
|
||||
|
||||
int numberBones;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberBones = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
std::vector<Bone> bones;
|
||||
std::vector<std::string> boneNames;
|
||||
std::vector<std::string> boneParentNames;
|
||||
std::unordered_map<std::string, std::vector<std::string>> boneChildren;
|
||||
|
||||
bones.resize(numberBones);
|
||||
boneNames.resize(numberBones);
|
||||
boneParentNames.resize(numberBones);
|
||||
|
||||
for (int i = 0; i < numberBones; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
std::string boneName = tempLine.substr(6);
|
||||
|
||||
boneNames[i] = boneName;
|
||||
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
bones[i].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
|
||||
|
||||
std::getline(f, tempLine); //skip tail
|
||||
|
||||
std::getline(f, tempLine); //len
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_float)) {
|
||||
std::string len_str = match.str();
|
||||
bones[i].boneLength = std::stof(len_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
//---------- matrix begin
|
||||
|
||||
std::getline(f, tempLine);
|
||||
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
bones[i].boneMatrixWorld.m[0] = floatValues[0];
|
||||
bones[i].boneMatrixWorld.m[0 + 1 * 3] = floatValues[1];
|
||||
bones[i].boneMatrixWorld.m[0 + 2 * 3] = floatValues[2];
|
||||
|
||||
|
||||
std::getline(f, tempLine);
|
||||
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
bones[i].boneMatrixWorld.m[1] = floatValues[0];
|
||||
bones[i].boneMatrixWorld.m[1 + 1 * 3] = floatValues[1];
|
||||
bones[i].boneMatrixWorld.m[1 + 2 * 3] = floatValues[2];
|
||||
|
||||
|
||||
std::getline(f, tempLine);
|
||||
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
bones[i].boneMatrixWorld.m[2] = floatValues[0];
|
||||
bones[i].boneMatrixWorld.m[2 + 1 * 3] = floatValues[1];
|
||||
bones[i].boneMatrixWorld.m[2 + 2 * 3] = floatValues[2];
|
||||
|
||||
//----------- matrix end
|
||||
std::getline(f, tempLine); //parent
|
||||
|
||||
if (tempLine == " Parent: None")
|
||||
{
|
||||
bones[i].parent = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string boneParent = tempLine.substr(10);
|
||||
boneParentNames[i] = boneParent;
|
||||
}
|
||||
|
||||
std::getline(f, tempLine); //children
|
||||
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_boneChildren)) {
|
||||
|
||||
boneChildren[boneName].push_back(match.str(1));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Now process all the bones:
|
||||
for (int i = 0; i < numberBones; i++)
|
||||
{
|
||||
std::string boneName = boneNames[i];
|
||||
std::string boneParent = boneParentNames[i];
|
||||
if (boneParent == "")
|
||||
{
|
||||
bones[i].parent = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bones[i].parent = getIndexByValue(boneParent, boneNames);
|
||||
}
|
||||
|
||||
for (int j = 0; j < boneChildren[boneName].size(); j++)
|
||||
{
|
||||
bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames));
|
||||
}
|
||||
|
||||
/*if (boneName == "Bone.020")
|
||||
{
|
||||
std::cout << i << std::endl;
|
||||
}*/
|
||||
}
|
||||
|
||||
startBones = bones;
|
||||
currentBones = bones;
|
||||
|
||||
///std::cout << "Hello!" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //vertice count
|
||||
int numberVertices;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberVertices = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
std::vector<Vector3f> vertices;
|
||||
|
||||
vertices.resize(numberVertices);
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
vertices[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]};
|
||||
}
|
||||
|
||||
|
||||
//==== process uv and normals begin
|
||||
|
||||
std::cout << "Hello x1" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===UV Coordinates:
|
||||
|
||||
std::getline(f, tempLine); //triangle count
|
||||
int numberTriangles;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberTriangles = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
|
||||
// Now process UVs
|
||||
std::vector<std::array<Vector2f, 3>> uvCoords;
|
||||
|
||||
uvCoords.resize(numberTriangles);
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine); //Face 0
|
||||
|
||||
int uvCount;
|
||||
std::getline(f, tempLine);
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
uvCount = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
if (uvCount != 3)
|
||||
{
|
||||
throw std::runtime_error("more than 3 uvs");
|
||||
}
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
if (floatValues.size() != 2)
|
||||
{
|
||||
throw std::runtime_error("more than 2 uvs---");
|
||||
}
|
||||
|
||||
uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::cout << "Hello eee" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===Normals:
|
||||
|
||||
|
||||
std::vector<Vector3f> normals;
|
||||
|
||||
normals.resize(numberVertices);
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
}
|
||||
|
||||
//==== process uv and normals end
|
||||
|
||||
std::getline(f, tempLine); //triangle count.
|
||||
//numberTriangles; //Need to check if new value is the same as was read before
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberTriangles = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
std::vector<std::array<int, 3>> triangles;
|
||||
|
||||
triangles.resize(numberTriangles);
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<int> intValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_int)) {
|
||||
intValues.push_back(std::stoi(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
triangles[i] = { intValues[0], intValues[1], intValues[2] };
|
||||
}
|
||||
|
||||
std::getline(f, tempLine);//=== Vertex Weights ===
|
||||
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight;
|
||||
localVerticesBoneWeight.resize(numberVertices);
|
||||
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine); //skip Vertex 0:
|
||||
std::getline(f, tempLine); //vertex group count
|
||||
int boneCount;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
boneCount = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
if (boneCount > MAX_BONE_COUNT)
|
||||
{
|
||||
throw std::runtime_error("more than 5 bones");
|
||||
}
|
||||
|
||||
float sumWeights = 0;
|
||||
|
||||
for (int j = 0; j < boneCount; j++)
|
||||
{
|
||||
std::getline(f, tempLine); //Group: 'Bone', Weight: 0.9929084181785583
|
||||
if (std::regex_search(tempLine, match, pattern_bone_weight)) {
|
||||
// ��������� ����� (��� �������)
|
||||
std::string word = match.str(1);
|
||||
double weight = std::stod(match.str(2));
|
||||
|
||||
int boneNumber = getIndexByValue(word, boneNames);
|
||||
localVerticesBoneWeight[i][j].boneIndex = boneNumber;
|
||||
localVerticesBoneWeight[i][j].weight = weight;
|
||||
sumWeights += weight;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No match found in the input string.");
|
||||
}
|
||||
}
|
||||
|
||||
//Normalize weights:
|
||||
for (int j = 0; j < boneCount; j++)
|
||||
{
|
||||
localVerticesBoneWeight[i][j].weight = localVerticesBoneWeight[i][j].weight / sumWeights;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::getline(f, tempLine);//=== Animation Keyframes ===
|
||||
std::getline(f, tempLine);//=== Bone Transforms per Keyframe ===
|
||||
|
||||
|
||||
|
||||
std::getline(f, tempLine);
|
||||
int numberKeyFrames;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberKeyFrames = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
animations.resize(1);
|
||||
|
||||
animations[0].keyFrames.resize(numberKeyFrames);
|
||||
|
||||
for (int i = 0; i < numberKeyFrames; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
int numberFrame;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberFrame = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
animations[0].keyFrames[i].frame = numberFrame;
|
||||
|
||||
animations[0].keyFrames[i].bones.resize(numberBones);
|
||||
|
||||
for (int j = 0; j < numberBones; j++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
std::string boneName = tempLine.substr(8);
|
||||
int boneNumber = getIndexByValue(boneName, boneNames);
|
||||
animations[0].keyFrames[i].bones[boneNumber] = startBones[boneNumber];
|
||||
|
||||
std::getline(f, tempLine); // Location: <Vector (0.0000, 0.0000, -0.0091)>
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
|
||||
std::getline(f, tempLine); // Rotation
|
||||
std::getline(f, tempLine); // Matrix
|
||||
|
||||
//=============== Matrix begin ==================
|
||||
std::getline(f, tempLine);
|
||||
|
||||
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0] = floatValues[0];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0 + 1 * 4] = floatValues[1];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0 + 2 * 4] = floatValues[2];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0 + 3 * 4] = floatValues[3];
|
||||
|
||||
|
||||
std::getline(f, tempLine);
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1] = floatValues[0];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1 + 1 * 4] = floatValues[1];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1 + 2 * 4] = floatValues[2];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1 + 3 * 4] = floatValues[3];
|
||||
|
||||
std::getline(f, tempLine);
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2] = floatValues[0];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2 + 1 * 4] = floatValues[1];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2 + 2 * 4] = floatValues[2];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2 + 3 * 4] = floatValues[3];
|
||||
|
||||
|
||||
std::getline(f, tempLine);
|
||||
b = tempLine.cbegin();
|
||||
e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3] = floatValues[0];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3 + 1 * 4] = floatValues[1];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3 + 2 * 4] = floatValues[2];
|
||||
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3 + 3 * 4] = floatValues[3];
|
||||
|
||||
//std::getline(f, tempLine);// ignore last matrix line
|
||||
|
||||
//=============== Matrix end ==================
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now let's process bone weights and vertices
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
|
||||
mesh.PositionData.push_back(vertices[triangles[i][0]]);
|
||||
mesh.PositionData.push_back(vertices[triangles[i][1]]);
|
||||
mesh.PositionData.push_back(vertices[triangles[i][2]]);
|
||||
|
||||
verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
|
||||
verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
|
||||
verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);
|
||||
|
||||
mesh.TexCoordData.push_back(uvCoords[i][0]);
|
||||
mesh.TexCoordData.push_back(uvCoords[i][1]);
|
||||
mesh.TexCoordData.push_back(uvCoords[i][2]);
|
||||
}
|
||||
|
||||
startMesh = mesh;
|
||||
}
|
||||
|
||||
void BoneSystem::Interpolate(int frame)
|
||||
{
|
||||
int startingFrame = -1;
|
||||
for (int i = 0; i < animations[0].keyFrames.size() - 1; i++)
|
||||
{
|
||||
int oldFrame = animations[0].keyFrames[i].frame;
|
||||
int nextFrame = animations[0].keyFrames[i + 1].frame;
|
||||
if (frame >= oldFrame && frame < nextFrame)
|
||||
{
|
||||
startingFrame = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startingFrame == -1)
|
||||
{
|
||||
throw std::runtime_error("Exception here");
|
||||
}
|
||||
|
||||
int modifiedFrameNumber = frame - animations[0].keyFrames[startingFrame].frame;
|
||||
|
||||
int diffFrames = animations[0].keyFrames[startingFrame + 1].frame - animations[0].keyFrames[startingFrame].frame;
|
||||
|
||||
float t = (modifiedFrameNumber + 0.f) / diffFrames;
|
||||
|
||||
std::vector<Bone>& oneFrameBones = animations[0].keyFrames[startingFrame].bones;
|
||||
std::vector<Bone>& nextFrameBones = animations[0].keyFrames[startingFrame+1].bones;
|
||||
|
||||
std::vector<Matrix4f> skinningMatrixForEachBone;
|
||||
//std::vector<Matrix3f> skinningMatrixForEachBone;
|
||||
skinningMatrixForEachBone.resize(currentBones.size());
|
||||
|
||||
|
||||
for (int i = 0; i < currentBones.size(); i++)
|
||||
{
|
||||
currentBones[i].boneStartWorld.v[0] = oneFrameBones[i].boneStartWorld.v[0] + t * (nextFrameBones[i].boneStartWorld.v[0] - oneFrameBones[i].boneStartWorld.v[0]);
|
||||
currentBones[i].boneStartWorld.v[1] = oneFrameBones[i].boneStartWorld.v[1] + t * (nextFrameBones[i].boneStartWorld.v[1] - oneFrameBones[i].boneStartWorld.v[1]);
|
||||
currentBones[i].boneStartWorld.v[2] = oneFrameBones[i].boneStartWorld.v[2] + t * (nextFrameBones[i].boneStartWorld.v[2] - oneFrameBones[i].boneStartWorld.v[2]);
|
||||
|
||||
Matrix3f oneFrameBonesMatrix;
|
||||
|
||||
oneFrameBonesMatrix.m[0] = oneFrameBones[i].boneMatrixWorld.m[0];
|
||||
oneFrameBonesMatrix.m[1] = oneFrameBones[i].boneMatrixWorld.m[1];
|
||||
oneFrameBonesMatrix.m[2] = oneFrameBones[i].boneMatrixWorld.m[2];
|
||||
|
||||
oneFrameBonesMatrix.m[3] = oneFrameBones[i].boneMatrixWorld.m[0 + 1*4];
|
||||
oneFrameBonesMatrix.m[4] = oneFrameBones[i].boneMatrixWorld.m[1 + 1*4];
|
||||
oneFrameBonesMatrix.m[5] = oneFrameBones[i].boneMatrixWorld.m[2 + 1*4];
|
||||
|
||||
oneFrameBonesMatrix.m[6] = oneFrameBones[i].boneMatrixWorld.m[0 + 2*4];
|
||||
oneFrameBonesMatrix.m[7] = oneFrameBones[i].boneMatrixWorld.m[1 + 2*4];
|
||||
oneFrameBonesMatrix.m[8] = oneFrameBones[i].boneMatrixWorld.m[2 + 2*4];
|
||||
|
||||
Matrix3f nextFrameBonesMatrix;
|
||||
|
||||
nextFrameBonesMatrix.m[0] = nextFrameBones[i].boneMatrixWorld.m[0];
|
||||
nextFrameBonesMatrix.m[1] = nextFrameBones[i].boneMatrixWorld.m[1];
|
||||
nextFrameBonesMatrix.m[2] = nextFrameBones[i].boneMatrixWorld.m[2];
|
||||
|
||||
nextFrameBonesMatrix.m[3] = nextFrameBones[i].boneMatrixWorld.m[0 + 1 * 4];
|
||||
nextFrameBonesMatrix.m[4] = nextFrameBones[i].boneMatrixWorld.m[1 + 1 * 4];
|
||||
nextFrameBonesMatrix.m[5] = nextFrameBones[i].boneMatrixWorld.m[2 + 1 * 4];
|
||||
|
||||
nextFrameBonesMatrix.m[6] = nextFrameBones[i].boneMatrixWorld.m[0 + 2 * 4];
|
||||
nextFrameBonesMatrix.m[7] = nextFrameBones[i].boneMatrixWorld.m[1 + 2 * 4];
|
||||
nextFrameBonesMatrix.m[8] = nextFrameBones[i].boneMatrixWorld.m[2 + 2 * 4];
|
||||
|
||||
Vector4f q1 = MatrixToQuat(oneFrameBonesMatrix);
|
||||
Vector4f q2 = MatrixToQuat(nextFrameBonesMatrix);
|
||||
Vector4f q1_norm = q1.normalized();
|
||||
Vector4f q2_norm = q2.normalized();
|
||||
|
||||
Vector4f result = slerp(q1_norm, q2_norm, t);
|
||||
|
||||
Matrix3f boneMatrixWorld3 = QuatToMatrix(result);
|
||||
|
||||
currentBones[i].boneMatrixWorld = MakeMatrix4x4(boneMatrixWorld3, currentBones[i].boneStartWorld);
|
||||
|
||||
Matrix4f currentBoneMatrixWorld4 = currentBones[i].boneMatrixWorld;
|
||||
Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
|
||||
|
||||
Matrix4f inverstedStartBoneMatrixWorld4 = InverseMatrix(startBoneMatrixWorld4);
|
||||
|
||||
skinningMatrixForEachBone[i] = MultMatrixMatrix(currentBoneMatrixWorld4, inverstedStartBoneMatrixWorld4);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
for (int i = 0; i < currentBones.size(); i++)
|
||||
{
|
||||
currentBones[i].boneStartWorld = oneFrameBones[i].boneStartWorld;
|
||||
currentBones[i].boneMatrixWorld = oneFrameBones[i].boneMatrixWorld;
|
||||
//Matrix4f currentBoneMatrixWorld4 = MakeMatrix4x4(currentBones[i].boneMatrixWorld, currentBones[i].boneStartWorld);
|
||||
//Matrix4f startBoneMatrixWorld4 = MakeMatrix4x4(animations[0].keyFrames[0].bones[i].boneMatrixWorld, animations[0].keyFrames[0].bones[i].boneStartWorld);
|
||||
Matrix4f currentBoneMatrixWorld4 = currentBones[i].boneMatrixWorld;
|
||||
Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
|
||||
Matrix4f inverstedStartBoneMatrixWorld4 = InverseMatrix(startBoneMatrixWorld4);
|
||||
skinningMatrixForEachBone[i] = MultMatrixMatrix(currentBoneMatrixWorld4, inverstedStartBoneMatrixWorld4);
|
||||
|
||||
if (i == 10)
|
||||
{
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
}*/
|
||||
|
||||
for (int i = 0; i < mesh.PositionData.size(); i++)
|
||||
{
|
||||
Vector4f originalPos = {
|
||||
startMesh.PositionData[i].v[0],
|
||||
startMesh.PositionData[i].v[1],
|
||||
startMesh.PositionData[i].v[2], 1.0};
|
||||
|
||||
Vector4f finalPos = Vector4f{0.f, 0.f, 0.f, 0.f};
|
||||
|
||||
bool vMoved = false;
|
||||
//Vector3f finalPos = Vector3f{ 0.f, 0.f, 0.f };
|
||||
|
||||
for (int j = 0; j < MAX_BONE_COUNT; j++)
|
||||
{
|
||||
if (verticesBoneWeight[i][j].weight != 0)
|
||||
{
|
||||
vMoved = true;
|
||||
//finalPos = finalPos + MultVectorMatrix(originalPos, skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex]) * verticesBoneWeight[i][j].weight;
|
||||
finalPos = finalPos + MultMatrixVector(skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex], originalPos) * verticesBoneWeight[i][j].weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(finalPos.v[0] - originalPos.v[0]) > 1 || abs(finalPos.v[1] - originalPos.v[1]) > 1 || abs(finalPos.v[2] - originalPos.v[2]) > 1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (!vMoved)
|
||||
{
|
||||
finalPos = originalPos;
|
||||
}
|
||||
|
||||
mesh.PositionData[i].v[0] = finalPos.v[0];
|
||||
mesh.PositionData[i].v[1] = finalPos.v[1];
|
||||
mesh.PositionData[i].v[2] = finalPos.v[2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
constexpr int MAX_BONE_COUNT = 6;
|
||||
struct Bone
|
||||
{
|
||||
Vector3f boneStartWorld;
|
||||
float boneLength;
|
||||
Matrix4f boneMatrixWorld;
|
||||
// boneVector = boneLength * (0, 1, 0) � ���� ��������
|
||||
// Then multiply by boneMatrixWorld � �� �������� �������� �����
|
||||
|
||||
int parent;
|
||||
std::vector<int> children;
|
||||
};
|
||||
|
||||
struct BoneWeight
|
||||
{
|
||||
int boneIndex = -1;
|
||||
float weight = 0;
|
||||
};
|
||||
|
||||
struct AnimationKeyFrame
|
||||
{
|
||||
int frame;
|
||||
std::vector<Bone> bones;
|
||||
};
|
||||
|
||||
struct Animation
|
||||
{
|
||||
std::vector<AnimationKeyFrame> keyFrames;
|
||||
};
|
||||
|
||||
struct BoneSystem
|
||||
{
|
||||
VertexDataStruct mesh;
|
||||
VertexDataStruct startMesh;
|
||||
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
|
||||
|
||||
Matrix4f armatureMatrix;
|
||||
|
||||
std::vector<Bone> startBones;
|
||||
std::vector<Bone> currentBones;
|
||||
|
||||
std::vector<Animation> animations;
|
||||
|
||||
void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
|
||||
void Interpolate(int frame);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "Environment.h"
|
||||
|
||||
#include "utils/Utils.h"
|
||||
#include <GL/gl.h>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
int Environment::windowHeaderHeight = 0;
|
||||
int Environment::width = 0;
|
||||
int Environment::height = 0;
|
||||
float Environment::zoom = 18.f;
|
||||
|
||||
bool Environment::leftPressed = false;
|
||||
bool Environment::rightPressed = false;
|
||||
bool Environment::upPressed = false;
|
||||
bool Environment::downPressed = false;
|
||||
|
||||
bool Environment::settings_inverseVertical = false;
|
||||
|
||||
SDL_Window* Environment::window = nullptr;
|
||||
|
||||
bool Environment::showMouse = false;
|
||||
|
||||
bool Environment::exitGameLoop = false;
|
||||
|
||||
Matrix3f Environment::shipMatrix = Matrix3f::Identity();
|
||||
Matrix3f Environment::inverseShipMatrix = Matrix3f::Identity();
|
||||
|
||||
|
||||
bool Environment::tapDownHold = false;
|
||||
Vector2f Environment::tapDownStartPos = { 0, 0 };
|
||||
Vector2f Environment::tapDownCurrentPos = { 0, 0 };
|
||||
|
||||
Vector3f Environment::shipPosition = {0,0,45000.f};
|
||||
|
||||
float Environment::shipVelocity = 0.f;
|
||||
|
||||
|
||||
const float Environment::CONST_Z_NEAR = 5.f;
|
||||
const float Environment::CONST_Z_FAR = 5000.f;
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "utils/ZLMath.h"
|
||||
#ifdef __linux__
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
#include "render/OpenGlExtensions.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
static int windowHeaderHeight;
|
||||
static int width;
|
||||
static int height;
|
||||
static float zoom;
|
||||
|
||||
static bool leftPressed;
|
||||
static bool rightPressed;
|
||||
static bool upPressed;
|
||||
static bool downPressed;
|
||||
|
||||
static bool settings_inverseVertical;
|
||||
|
||||
static Matrix3f shipMatrix;
|
||||
static Matrix3f inverseShipMatrix;
|
||||
|
||||
static SDL_Window* window;
|
||||
|
||||
static bool showMouse;
|
||||
static bool exitGameLoop;
|
||||
|
||||
|
||||
static bool tapDownHold;
|
||||
static Vector2f tapDownStartPos;
|
||||
static Vector2f tapDownCurrentPos;
|
||||
|
||||
static Vector3f shipPosition;
|
||||
static float shipVelocity;
|
||||
|
||||
static const float CONST_Z_NEAR;
|
||||
static const float CONST_Z_FAR;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
+782
@@ -0,0 +1,782 @@
|
||||
#include "Game.h"
|
||||
#include "AnimatedModel.h"
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include <iostream>
|
||||
#include "render/TextureManager.h"
|
||||
#include "TextModel.h"
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
#ifdef EMSCRIPTEN
|
||||
const char* CONST_ZIP_FILE = "space-game001.zip";
|
||||
#else
|
||||
const char* CONST_ZIP_FILE = "";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Vector4f generateRandomQuaternion(std::mt19937& gen)
|
||||
{
|
||||
|
||||
std::normal_distribution<> distrib(0.0, 1.0);
|
||||
|
||||
Vector4f randomQuat = {
|
||||
(float)distrib(gen),
|
||||
(float)distrib(gen),
|
||||
(float)distrib(gen),
|
||||
(float)distrib(gen)
|
||||
};
|
||||
|
||||
return randomQuat.normalized();
|
||||
}
|
||||
|
||||
|
||||
std::vector<BoxCoords> generateRandomBoxCoords(int N)
|
||||
{
|
||||
const float MIN_DISTANCE = 3.0f;
|
||||
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
|
||||
const float MIN_COORD = -100.0f;
|
||||
const float MAX_COORD = 100.0f;
|
||||
const int MAX_ATTEMPTS = 1000;
|
||||
std::vector<BoxCoords> boxCoordsArr;
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
|
||||
std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD);
|
||||
|
||||
int generatedCount = 0;
|
||||
|
||||
while (generatedCount < N)
|
||||
{
|
||||
bool accepted = false;
|
||||
int attempts = 0;
|
||||
|
||||
while (!accepted && attempts < MAX_ATTEMPTS)
|
||||
{
|
||||
Vector3f newPos(
|
||||
(float)distrib(gen),
|
||||
(float)distrib(gen),
|
||||
(float)distrib(gen)
|
||||
);
|
||||
|
||||
accepted = true;
|
||||
for (const auto& existingBox : boxCoordsArr)
|
||||
{
|
||||
|
||||
Vector3f diff = newPos - existingBox.pos;
|
||||
|
||||
float distanceSquared = diff.squaredNorm();
|
||||
|
||||
if (distanceSquared < MIN_DISTANCE_SQUARED)
|
||||
{
|
||||
accepted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (accepted)
|
||||
{
|
||||
Vector4f randomQuat = generateRandomQuaternion(gen);
|
||||
|
||||
Matrix3f randomMatrix = QuatToMatrix(randomQuat);
|
||||
|
||||
boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix });
|
||||
generatedCount++;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (!accepted) {
|
||||
std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return boxCoordsArr;
|
||||
}
|
||||
|
||||
Game::Game()
|
||||
: window(nullptr)
|
||||
, glContext(nullptr)
|
||||
, newTickCount(0)
|
||||
, lastTickCount(0)
|
||||
{
|
||||
projectiles.reserve(maxProjectiles);
|
||||
for (int i = 0; i < maxProjectiles; ++i) {
|
||||
projectiles.emplace_back(std::make_unique<Projectile>());
|
||||
}
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
if (glContext) {
|
||||
SDL_GL_DeleteContext(glContext);
|
||||
}
|
||||
if (window) {
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void Game::setup() {
|
||||
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
|
||||
ZL::BindOpenGlFunctions();
|
||||
ZL::CheckGlError();
|
||||
|
||||
// Initialize renderer
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColorPlanet", "./shaders/defaultColorPlanet.vertex", "./shaders/defaultColorPlanet_web.fragment", CONST_ZIP_FILE);
|
||||
|
||||
#else
|
||||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColorStones", "./shaders/defaultColor_fog_stones.vertex", "./shaders/defaultColor_fog_stones_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetBake", "./shaders/planet_bake.vertex", "./shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetStone", "./shaders/planet_stone.vertex", "./shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("planetLand", "./shaders/planet_land.vertex", "./shaders/planet_land_desktop.fragment", CONST_ZIP_FILE);
|
||||
|
||||
#endif
|
||||
|
||||
bool cfgLoaded = sparkEmitter.loadFromJsonFile("../config/spark_config.json", renderer, CONST_ZIP_FILE);
|
||||
bool projCfgLoaded = projectileEmitter.loadFromJsonFile("../config/spark_projectile_config.json", renderer, CONST_ZIP_FILE);
|
||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
uiManager.loadFromFile("../config/ui.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
|
||||
std::cerr << "Play button pressed: " << name << std::endl;
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
|
||||
Environment::exitGameLoop = true;
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
|
||||
if (uiManager.pushMenuFromFile("../config/settings.json", this->renderer, CONST_ZIP_FILE)) {
|
||||
|
||||
uiManager.setButtonCallback("Opt1", [this](const std::string& n) {
|
||||
std::cerr << "Opt1 pressed: " << n << std::endl;
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("Opt2", [this](const std::string& n) {
|
||||
std::cerr << "Opt2 pressed: " << n << std::endl;
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("backButton", [this](const std::string& n) {
|
||||
uiManager.popMenu();
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to open settings menu" << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
uiManager.setSliderCallback("musicVolumeSlider", [this](const std::string& name, float value) {
|
||||
std::cerr << "Music volume slider changed to: " << value << std::endl;
|
||||
musicVolume = value;
|
||||
Environment::shipVelocity = musicVolume * 20.0f;
|
||||
});
|
||||
|
||||
cubemapTexture = std::make_shared<Texture>(
|
||||
std::array<TextureDataStruct, 6>{
|
||||
CreateTextureDataFromBmp24("./resources/sky/space_rt.bmp", CONST_ZIP_FILE),
|
||||
CreateTextureDataFromBmp24("./resources/sky/space_lf.bmp", CONST_ZIP_FILE),
|
||||
CreateTextureDataFromBmp24("./resources/sky/space_up.bmp", CONST_ZIP_FILE),
|
||||
CreateTextureDataFromBmp24("./resources/sky/space_dn.bmp", CONST_ZIP_FILE),
|
||||
CreateTextureDataFromBmp24("./resources/sky/space_bk.bmp", CONST_ZIP_FILE),
|
||||
CreateTextureDataFromBmp24("./resources/sky/space_ft.bmp", CONST_ZIP_FILE)
|
||||
});
|
||||
|
||||
|
||||
cubemap.data = ZL::CreateCubemap(500);
|
||||
cubemap.RefreshVBO();
|
||||
|
||||
//Load texture
|
||||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/DefaultMaterial_BaseColor_shine.png", CONST_ZIP_FILE));
|
||||
spaceshipBase = LoadFromTextFile02("./resources/spaceship006.txt", CONST_ZIP_FILE);
|
||||
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
||||
//spaceshipBase.Move(Vector3f{ -0.52998, -13, 0 });
|
||||
//spaceshipBase.Move(Vector3f{ -0.52998, -10, 10 });
|
||||
|
||||
spaceship.AssignFrom(spaceshipBase);
|
||||
spaceship.RefreshVBO();
|
||||
|
||||
|
||||
//Boxes
|
||||
boxTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE));
|
||||
boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE);
|
||||
|
||||
boxCoordsArr = generateRandomBoxCoords(50);
|
||||
|
||||
boxRenderArr.resize(boxCoordsArr.size());
|
||||
|
||||
for (int i = 0; i < boxCoordsArr.size(); i++)
|
||||
{
|
||||
boxRenderArr[i].AssignFrom(boxBase);
|
||||
//boxRenderArr[i].data = CreateBaseConvexPolyhedron(1999);
|
||||
boxRenderArr[i].RefreshVBO();
|
||||
}
|
||||
|
||||
if (!cfgLoaded)
|
||||
{
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
|
||||
/* buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
|
||||
|
||||
button.data.PositionData.push_back({ 100, 100, 0 });
|
||||
button.data.PositionData.push_back({ 100, 150, 0 });
|
||||
button.data.PositionData.push_back({ 300, 150, 0 });
|
||||
button.data.PositionData.push_back({ 100, 100, 0 });
|
||||
button.data.PositionData.push_back({ 300, 150, 0 });
|
||||
button.data.PositionData.push_back({ 300, 100, 0 });
|
||||
|
||||
button.data.TexCoordData.push_back({ 0,0 });
|
||||
button.data.TexCoordData.push_back({ 0,1 });
|
||||
button.data.TexCoordData.push_back({ 1,1 });
|
||||
button.data.TexCoordData.push_back({ 0,0 });
|
||||
button.data.TexCoordData.push_back({ 1,1 });
|
||||
button.data.TexCoordData.push_back({ 1,0 });
|
||||
|
||||
button.RefreshVBO();*/
|
||||
/*
|
||||
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE));
|
||||
|
||||
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
|
||||
musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 });
|
||||
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
|
||||
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
|
||||
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
|
||||
musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 });
|
||||
|
||||
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
|
||||
musicVolumeBar.data.TexCoordData.push_back({ 0,1 });
|
||||
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
|
||||
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
|
||||
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
|
||||
musicVolumeBar.data.TexCoordData.push_back({ 1,0 });
|
||||
|
||||
musicVolumeBar.RefreshVBO();
|
||||
|
||||
|
||||
musicVolumeBarButtonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE));
|
||||
|
||||
float musicVolumeBarButtonButtonCenterY = 350.0f;
|
||||
|
||||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
|
||||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
|
||||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
|
||||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
|
||||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
|
||||
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
|
||||
|
||||
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
|
||||
musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 });
|
||||
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
|
||||
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
|
||||
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
|
||||
musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 });
|
||||
|
||||
musicVolumeBarButton.RefreshVBO();*/
|
||||
renderer.InitOpenGL();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
planetObject.init();
|
||||
|
||||
rockTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
|
||||
|
||||
}
|
||||
|
||||
void Game::drawCubemap(float skyPercent)
|
||||
{
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string envShaderName = "env";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
static const std::string skyPercentUniformName = "skyPercent";
|
||||
|
||||
renderer.shaderManager.PushShader(envShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1f(skyPercentUniformName, skyPercent);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(cubemap);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Game::drawShip()
|
||||
{
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string envShaderName = "env";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
renderer.PushMatrix();
|
||||
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
|
||||
renderer.TranslateMatrix({ 0, -Environment::zoom * 0.03f, 0 });
|
||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(spaceship);
|
||||
|
||||
for (const auto& p : projectiles) {
|
||||
if (p && p->isActive()) {
|
||||
p->draw(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Game::drawBoxes()
|
||||
{
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string envShaderName = "env";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
for (int i = 0; i < boxCoordsArr.size(); i++)
|
||||
{
|
||||
renderer.PushMatrix();
|
||||
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
renderer.TranslateMatrix({ 0.f, 0.f, 45000.f });
|
||||
renderer.TranslateMatrix(boxCoordsArr[i].pos);
|
||||
renderer.RotateMatrix(boxCoordsArr[i].m);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID());
|
||||
//glBindTexture(GL_TEXTURE_2D, rockTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(boxRenderArr[i]);
|
||||
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
/*void Game::UpdateVolumeKnob() {
|
||||
float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY);
|
||||
|
||||
auto& pos = musicVolumeBarButton.data.PositionData;
|
||||
|
||||
pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
|
||||
pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
|
||||
pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
|
||||
pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
|
||||
pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
|
||||
pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
|
||||
|
||||
musicVolumeBarButton.RefreshVBO();
|
||||
|
||||
}
|
||||
void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) {
|
||||
|
||||
int uiX = mouseX;
|
||||
int uiY = Environment::height - mouseY;
|
||||
Environment::shipVelocity = (musicVolume) * (20.0);
|
||||
if (uiY < volumeBarMinY || uiY > volumeBarMaxY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY);
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
musicVolume = t;
|
||||
UpdateVolumeKnob();
|
||||
}*/
|
||||
|
||||
void Game::drawUI()
|
||||
{
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string envShaderName = "env";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
//renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
|
||||
//renderer.PushMatrix();
|
||||
|
||||
//renderer.LoadIdentity();
|
||||
|
||||
//glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
|
||||
//renderer.DrawVertexRenderStruct(button);
|
||||
|
||||
|
||||
//glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
|
||||
//renderer.DrawVertexRenderStruct(musicVolumeBar);
|
||||
|
||||
//glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID());
|
||||
//renderer.DrawVertexRenderStruct(musicVolumeBarButton);
|
||||
|
||||
//renderer.PopMatrix();
|
||||
//renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
uiManager.draw(renderer);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Game::drawScene() {
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string envShaderName = "env";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
float skyPercent = 0.0;
|
||||
float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition);
|
||||
if (distance > 1900.f)
|
||||
{
|
||||
skyPercent = 0.0f;
|
||||
}
|
||||
else if (distance < 1000.f)
|
||||
{
|
||||
skyPercent = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
skyPercent = (1900.f - distance) / 900.f;
|
||||
}
|
||||
|
||||
|
||||
drawCubemap(skyPercent);
|
||||
planetObject.draw(renderer);
|
||||
if (planetObject.distanceToPlanetSurface(Environment::shipPosition) > 100.f)
|
||||
{
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
drawShip();
|
||||
drawBoxes();
|
||||
|
||||
drawUI();
|
||||
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Game::processTickCount() {
|
||||
|
||||
if (lastTickCount == 0) {
|
||||
lastTickCount = SDL_GetTicks64();
|
||||
return;
|
||||
}
|
||||
|
||||
newTickCount = SDL_GetTicks64();
|
||||
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
|
||||
size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ?
|
||||
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
|
||||
|
||||
//gameObjects.updateScene(delta);
|
||||
sparkEmitter.update(static_cast<float>(delta));
|
||||
planetObject.update(static_cast<float>(delta));
|
||||
|
||||
if (Environment::tapDownHold) {
|
||||
|
||||
float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0];
|
||||
float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1];
|
||||
|
||||
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
|
||||
{
|
||||
|
||||
float rotationPower = sqrtf(diffx * diffx + diffy * diffy);
|
||||
|
||||
//std::cout << rotationPower << std::endl;
|
||||
|
||||
float deltaAlpha = rotationPower * delta * M_PI / 500000.f;
|
||||
|
||||
Vector3f rotationDirection = { diffy, diffx, 0 };
|
||||
|
||||
rotationDirection = rotationDirection.normalized();
|
||||
|
||||
Vector4f rotateQuat = {
|
||||
rotationDirection.v[0] * sin(deltaAlpha * 0.5f),
|
||||
rotationDirection.v[1] * sin(deltaAlpha * 0.5f),
|
||||
rotationDirection.v[2] * sin(deltaAlpha * 0.5f),
|
||||
cos(deltaAlpha * 0.5f) };
|
||||
|
||||
Matrix3f rotateMat = QuatToMatrix(rotateQuat);
|
||||
|
||||
Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat);
|
||||
Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(Environment::shipVelocity) > 0.01f)
|
||||
{
|
||||
Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f };
|
||||
Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection);
|
||||
Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted;
|
||||
}
|
||||
|
||||
for (auto& p : projectiles) {
|
||||
if (p && p->isActive()) {
|
||||
p->update(static_cast<float>(delta), renderer);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vector3f> projCameraPoints;
|
||||
for (const auto& p : projectiles) {
|
||||
if (p && p->isActive()) {
|
||||
Vector3f worldPos = p->getPosition();
|
||||
Vector3f rel = worldPos - Environment::shipPosition;
|
||||
Vector3f camPos = MultMatrixVector(Environment::inverseShipMatrix, rel);
|
||||
projCameraPoints.push_back(camPos);
|
||||
}
|
||||
}
|
||||
if (!projCameraPoints.empty()) {
|
||||
projectileEmitter.setEmissionPoints(projCameraPoints);
|
||||
projectileEmitter.emit();
|
||||
}
|
||||
else {
|
||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
}
|
||||
|
||||
std::vector<Vector3f> shipCameraPoints;
|
||||
for (const auto& lp : shipLocalEmissionPoints) {
|
||||
Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f };
|
||||
shipCameraPoints.push_back(adjusted);
|
||||
}
|
||||
if (!shipCameraPoints.empty()) {
|
||||
sparkEmitter.setEmissionPoints(shipCameraPoints);
|
||||
}
|
||||
|
||||
sparkEmitter.update(static_cast<float>(delta));
|
||||
projectileEmitter.update(static_cast<float>(delta));
|
||||
|
||||
lastTickCount = newTickCount;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::fireProjectiles() {
|
||||
std::vector<Vector3f> localOffsets = {
|
||||
Vector3f{ -1.5f, 0.9f, 5.0f },
|
||||
Vector3f{ 1.5f, 0.9f, 5.0f }
|
||||
};
|
||||
|
||||
const float projectileSpeed = 60.0f;
|
||||
const float lifeMs = 5000.0f;
|
||||
const float size = 0.5f;
|
||||
|
||||
Vector3f localForward = { 0,0,-1 };
|
||||
Vector3f worldForward = MultMatrixVector(Environment::shipMatrix, localForward).normalized();
|
||||
|
||||
for (const auto& lo : localOffsets) {
|
||||
Vector3f worldPos = Environment::shipPosition + MultMatrixVector(Environment::shipMatrix, lo);
|
||||
Vector3f worldVel = worldForward * projectileSpeed;
|
||||
|
||||
for (auto& p : projectiles) {
|
||||
if (!p->isActive()) {
|
||||
p->init(worldPos, worldVel, lifeMs, size, projectileTexture, renderer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::render() {
|
||||
SDL_GL_MakeCurrent(ZL::Environment::window, glContext);
|
||||
ZL::CheckGlError();
|
||||
|
||||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
drawScene();
|
||||
processTickCount();
|
||||
|
||||
SDL_GL_SwapWindow(ZL::Environment::window);
|
||||
}
|
||||
void Game::update() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_QUIT) {
|
||||
Environment::exitGameLoop = true;
|
||||
}
|
||||
else if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||||
// 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè
|
||||
|
||||
int mx = event.button.x;
|
||||
int my = event.button.y;
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
uiManager.onMouseDown(uiX, uiY);
|
||||
|
||||
bool uiHandled = false;
|
||||
|
||||
if (event.button.button == SDL_BUTTON_LEFT && !uiManager.isUiInteraction()) {
|
||||
uint64_t now = SDL_GetTicks64();
|
||||
if (now - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
||||
lastProjectileFireTime = now;
|
||||
fireProjectiles();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& button : uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
|
||||
(void)button;
|
||||
}
|
||||
|
||||
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
|
||||
for (const auto& slider : uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<std::shared_ptr<UiSlider>>{}) {
|
||||
(void)slider;
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (!uiManager.isUiInteraction()) {
|
||||
Environment::tapDownHold = true;
|
||||
// Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
|
||||
Environment::tapDownStartPos.v[0] = mx;
|
||||
Environment::tapDownStartPos.v[1] = my;
|
||||
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
|
||||
Environment::tapDownCurrentPos.v[0] = mx;
|
||||
Environment::tapDownCurrentPos.v[1] = my;
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_MOUSEBUTTONUP) {
|
||||
// 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
|
||||
int mx = event.button.x;
|
||||
int my = event.button.y;
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
uiManager.onMouseUp(uiX, uiY);
|
||||
|
||||
if (!uiManager.isUiInteraction()) {
|
||||
Environment::tapDownHold = false;
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_MOUSEMOTION) {
|
||||
// 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
|
||||
int mx = event.motion.x;
|
||||
int my = event.motion.y;
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
uiManager.onMouseMove(uiX, uiY);
|
||||
|
||||
if (Environment::tapDownHold && !uiManager.isUiInteraction()) {
|
||||
Environment::tapDownCurrentPos.v[0] = mx;
|
||||
Environment::tapDownCurrentPos.v[1] = my;
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_MOUSEWHEEL) {
|
||||
static const float zoomstep = 2.0f;
|
||||
if (event.wheel.y > 0) {
|
||||
Environment::zoom -= zoomstep;
|
||||
}
|
||||
else if (event.wheel.y < 0) {
|
||||
Environment::zoom += zoomstep;
|
||||
}
|
||||
if (Environment::zoom < zoomstep) {
|
||||
Environment::zoom = zoomstep;
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_KEYUP)
|
||||
{
|
||||
if (event.key.keysym.sym == SDLK_i)
|
||||
{
|
||||
Environment::shipVelocity += 500.f;
|
||||
}
|
||||
if (event.key.keysym.sym == SDLK_k)
|
||||
{
|
||||
Environment::shipVelocity -= 500.f;
|
||||
}
|
||||
if (event.key.keysym.sym == SDLK_o)
|
||||
{
|
||||
Environment::shipVelocity += 50.f;
|
||||
}
|
||||
if (event.key.keysym.sym == SDLK_l)
|
||||
{
|
||||
Environment::shipVelocity -= 50.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "Environment.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include "SparkEmitter.h"
|
||||
#include "planet/PlanetObject.h"
|
||||
#include "UiManager.h"
|
||||
#include "Projectile.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
|
||||
struct BoxCoords
|
||||
{
|
||||
Vector3f pos;
|
||||
Matrix3f m;
|
||||
};
|
||||
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
|
||||
void setup();
|
||||
void update();
|
||||
void render();
|
||||
|
||||
|
||||
bool shouldExit() const { return Environment::exitGameLoop; }
|
||||
|
||||
private:
|
||||
void processTickCount();
|
||||
void drawScene();
|
||||
void drawCubemap(float skyPercent);
|
||||
void drawShip();
|
||||
void drawBoxes();
|
||||
void drawUI();
|
||||
|
||||
void fireProjectiles();
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
Renderer renderer;
|
||||
|
||||
size_t newTickCount;
|
||||
size_t lastTickCount;
|
||||
|
||||
std::shared_ptr<Texture> rockTexture;
|
||||
|
||||
|
||||
|
||||
std::vector<BoxCoords> boxCoordsArr;
|
||||
std::vector<VertexRenderStruct> boxRenderArr;
|
||||
|
||||
|
||||
//std::shared_ptr<Texture> buttonTexture;
|
||||
//VertexRenderStruct button;
|
||||
|
||||
//std::shared_ptr<Texture> musicVolumeBarTexture;
|
||||
//VertexRenderStruct musicVolumeBar;
|
||||
|
||||
//std::shared_ptr<Texture> musicVolumeBarButtonTexture;
|
||||
//VertexRenderStruct musicVolumeBarButton;
|
||||
|
||||
|
||||
//bool isDraggingVolume = false;
|
||||
|
||||
float musicVolume = 0.0f;
|
||||
float volumeBarMinX = 1190.0f;
|
||||
float volumeBarMaxX = 1200.0f;
|
||||
float volumeBarMinY = 100.0f;
|
||||
float volumeBarMaxY = 600.0f;
|
||||
//float musicVolumeBarButtonButtonCenterX = 1195.0f;
|
||||
//float musicVolumeBarButtonButtonRadius = 25.0f;
|
||||
//void UpdateVolumeFromMouse(int mouseX, int mouseY);
|
||||
//void UpdateVolumeKnob();
|
||||
|
||||
|
||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||
|
||||
std::shared_ptr<Texture> sparkTexture;
|
||||
std::shared_ptr<Texture> spaceshipTexture;
|
||||
std::shared_ptr<Texture> cubemapTexture;
|
||||
VertexDataStruct spaceshipBase;
|
||||
VertexRenderStruct spaceship;
|
||||
|
||||
VertexRenderStruct cubemap;
|
||||
|
||||
std::shared_ptr<Texture> boxTexture;
|
||||
VertexDataStruct boxBase;
|
||||
|
||||
SparkEmitter sparkEmitter;
|
||||
SparkEmitter projectileEmitter;
|
||||
PlanetObject planetObject;
|
||||
UiManager uiManager;
|
||||
|
||||
std::vector<std::unique_ptr<Projectile>> projectiles;
|
||||
std::shared_ptr<Texture> projectileTexture;
|
||||
float projectileCooldownMs = 500.0f;
|
||||
uint64_t lastProjectileFireTime = 0;
|
||||
int maxProjectiles = 32;
|
||||
std::vector<Vector3f> shipLocalEmissionPoints;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "Projectile.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
Projectile::Projectile()
|
||||
: pos({ 0,0,0 })
|
||||
, vel({ 0,0,0 })
|
||||
, life(0.0f)
|
||||
, maxLife(0.0f)
|
||||
, active(false)
|
||||
, size(0.5f)
|
||||
, texture(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void Projectile::init(const Vector3f& startPos, const Vector3f& startVel, float lifeMs, float s, std::shared_ptr<Texture> tex, Renderer& renderer) {
|
||||
pos = startPos;
|
||||
vel = startVel;
|
||||
life = 0.0f;
|
||||
maxLife = lifeMs;
|
||||
size = s;
|
||||
texture = tex;
|
||||
active = true;
|
||||
|
||||
rebuildMesh(renderer);
|
||||
mesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void Projectile::update(float deltaMs, Renderer& renderer) {
|
||||
if (!active) return;
|
||||
|
||||
pos = pos + vel * (deltaMs / 1000.0f);
|
||||
life += deltaMs;
|
||||
if (life >= maxLife) {
|
||||
active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
rebuildMesh(renderer);
|
||||
mesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void Projectile::rebuildMesh(Renderer&) {
|
||||
float half = size * 0.5f;
|
||||
|
||||
mesh.data.PositionData.clear();
|
||||
mesh.data.TexCoordData.clear();
|
||||
|
||||
mesh.data.PositionData.push_back({ pos.v[0] - half, pos.v[1] - half, pos.v[2] });
|
||||
mesh.data.PositionData.push_back({ pos.v[0] - half, pos.v[1] + half, pos.v[2] });
|
||||
mesh.data.PositionData.push_back({ pos.v[0] + half, pos.v[1] + half, pos.v[2] });
|
||||
|
||||
mesh.data.PositionData.push_back({ pos.v[0] - half, pos.v[1] - half, pos.v[2] });
|
||||
mesh.data.PositionData.push_back({ pos.v[0] + half, pos.v[1] + half, pos.v[2] });
|
||||
mesh.data.PositionData.push_back({ pos.v[0] + half, pos.v[1] - half, pos.v[2] });
|
||||
|
||||
mesh.data.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
mesh.data.TexCoordData.push_back({ 0.0f, 1.0f });
|
||||
mesh.data.TexCoordData.push_back({ 1.0f, 1.0f });
|
||||
mesh.data.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
mesh.data.TexCoordData.push_back({ 1.0f, 1.0f });
|
||||
mesh.data.TexCoordData.push_back({ 1.0f, 0.0f });
|
||||
}
|
||||
|
||||
void Projectile::draw(Renderer& renderer) const {
|
||||
if (!active || !texture) return;
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(mesh);
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include <memory>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class Projectile {
|
||||
public:
|
||||
Projectile();
|
||||
~Projectile() = default;
|
||||
|
||||
void init(const Vector3f& startPos, const Vector3f& startVel, float lifeMs, float size, std::shared_ptr<Texture> tex, Renderer& renderer);
|
||||
void update(float deltaMs, Renderer& renderer);
|
||||
void draw(Renderer& renderer) const;
|
||||
|
||||
bool isActive() const { return active; }
|
||||
|
||||
Vector3f getPosition() const { return pos; }
|
||||
|
||||
private:
|
||||
Vector3f pos;
|
||||
Vector3f vel;
|
||||
float life;
|
||||
float maxLife;
|
||||
bool active;
|
||||
float size;
|
||||
VertexRenderStruct mesh;
|
||||
std::shared_ptr<Texture> texture;
|
||||
|
||||
void rebuildMesh(Renderer& renderer);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,523 @@
|
||||
#include "SparkEmitter.h"
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
//#include "renderer/OpenGlExtensions.h"
|
||||
#include <fstream>
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <iostream>
|
||||
#include "Environment.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
SparkEmitter::SparkEmitter()
|
||||
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200),
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
lastEmissionTime = std::chrono::steady_clock::now();
|
||||
|
||||
sparkQuad.data = VertexDataStruct();
|
||||
}
|
||||
|
||||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
|
||||
: emissionPoints(positions), emissionRate(rate), isActive(true),
|
||||
drawDataDirty(true), maxParticles(positions.size() * 100),
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
lastEmissionTime = std::chrono::steady_clock::now();
|
||||
|
||||
sparkQuad.data = VertexDataStruct();
|
||||
}
|
||||
|
||||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions,
|
||||
std::shared_ptr<Texture> tex,
|
||||
float rate)
|
||||
: emissionPoints(positions), texture(tex), emissionRate(rate),
|
||||
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100),
|
||||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
lastEmissionTime = std::chrono::steady_clock::now();
|
||||
|
||||
sparkQuad.data = VertexDataStruct();
|
||||
}
|
||||
|
||||
void SparkEmitter::setTexture(std::shared_ptr<Texture> tex) {
|
||||
texture = tex;
|
||||
}
|
||||
|
||||
void SparkEmitter::prepareDrawData() {
|
||||
if (!drawDataDirty) return;
|
||||
|
||||
drawPositions.clear();
|
||||
drawTexCoords.clear();
|
||||
|
||||
if (getActiveParticleCount() == 0) {
|
||||
drawDataDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<const SparkParticle*, float>> sortedParticles;
|
||||
sortedParticles.reserve(getActiveParticleCount());
|
||||
|
||||
for (const auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
sortedParticles.push_back({ &particle, particle.position.v[2] });
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(sortedParticles.begin(), sortedParticles.end(),
|
||||
[](const auto& a, const auto& b) {
|
||||
return a.second > b.second;
|
||||
});
|
||||
|
||||
for (const auto& [particlePtr, depth] : sortedParticles) {
|
||||
const auto& particle = *particlePtr;
|
||||
Vector3f pos = particle.position;
|
||||
float size = particleSize * particle.scale;
|
||||
|
||||
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] - size, pos.v[1] + size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] + size, pos.v[1] - size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||
}
|
||||
|
||||
drawDataDirty = false;
|
||||
}
|
||||
|
||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (getActiveParticleCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
prepareDrawData();
|
||||
|
||||
if (drawPositions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sparkQuad.data.PositionData = drawPositions;
|
||||
sparkQuad.data.TexCoordData = drawTexCoords;
|
||||
sparkQuad.RefreshVBO();
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(shaderProgramName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float aspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0, 0, -1.0f * zoom });
|
||||
|
||||
renderer.DrawVertexRenderStruct(sparkQuad);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
void SparkEmitter::update(float deltaTimeMs) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
auto currentTime = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
currentTime - lastEmissionTime).count();
|
||||
|
||||
if (isActive && elapsed >= emissionRate) {
|
||||
emit();
|
||||
lastEmissionTime = currentTime;
|
||||
drawDataDirty = true;
|
||||
}
|
||||
|
||||
bool anyChanged = false;
|
||||
for (auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
Vector3f oldPosition = particle.position;
|
||||
float oldScale = particle.scale;
|
||||
|
||||
particle.position.v[0] += particle.velocity.v[0] * deltaTimeMs / 1000.0f;
|
||||
particle.position.v[1] += particle.velocity.v[1] * deltaTimeMs / 1000.0f;
|
||||
particle.position.v[2] += particle.velocity.v[2] * deltaTimeMs / 1000.0f;
|
||||
|
||||
particle.lifeTime += deltaTimeMs;
|
||||
|
||||
if (particle.lifeTime >= particle.maxLifeTime) {
|
||||
particle.active = false;
|
||||
anyChanged = true;
|
||||
}
|
||||
else {
|
||||
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
|
||||
particle.scale = 1.0f - lifeRatio * 0.8f;
|
||||
|
||||
if (oldPosition.v[0] != particle.position.v[0] ||
|
||||
oldPosition.v[1] != particle.position.v[1] ||
|
||||
oldPosition.v[2] != particle.position.v[2] ||
|
||||
oldScale != particle.scale) {
|
||||
anyChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyChanged) {
|
||||
drawDataDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SparkEmitter::emit() {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (emissionPoints.empty()) return;
|
||||
bool emitted = false;
|
||||
|
||||
for (int i = 0; i < emissionPoints.size(); ++i) {
|
||||
bool particleFound = false;
|
||||
|
||||
for (auto& particle : particles) {
|
||||
if (!particle.active) {
|
||||
initParticle(particle, i);
|
||||
particle.active = true;
|
||||
particle.lifeTime = 0;
|
||||
particle.position = emissionPoints[i];
|
||||
particle.emitterIndex = i;
|
||||
particleFound = true;
|
||||
emitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!particleFound && !particles.empty()) {
|
||||
size_t oldestIndex = 0;
|
||||
float maxLifeTime = 0;
|
||||
|
||||
for (size_t j = 0; j < particles.size(); ++j) {
|
||||
if (particles[j].lifeTime > maxLifeTime) {
|
||||
maxLifeTime = particles[j].lifeTime;
|
||||
oldestIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
initParticle(particles[oldestIndex], i);
|
||||
particles[oldestIndex].active = true;
|
||||
particles[oldestIndex].lifeTime = 0;
|
||||
particles[oldestIndex].position = emissionPoints[i];
|
||||
particles[oldestIndex].emitterIndex = i;
|
||||
emitted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (emitted) {
|
||||
drawDataDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SparkEmitter::setEmissionPoints(const std::vector<Vector3f>& positions) {
|
||||
emissionPoints = positions;
|
||||
drawDataDirty = true;
|
||||
}
|
||||
|
||||
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
particle.velocity = getRandomVelocity(emitterIndex);
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<float> scaleDist(scaleRange.min, scaleRange.max);
|
||||
particle.scale = scaleDist(gen);
|
||||
|
||||
std::uniform_real_distribution<float> lifeDist(lifeTimeRange.min, lifeTimeRange.max);
|
||||
particle.maxLifeTime = lifeDist(gen);
|
||||
particle.emitterIndex = emitterIndex;
|
||||
}
|
||||
|
||||
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
|
||||
if (!configured) {
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<float> angleDist(0.0f, 2.0f * static_cast<float>(M_PI));
|
||||
std::uniform_real_distribution<float> speedDist(speedRange.min, speedRange.max);
|
||||
std::uniform_real_distribution<float> zSpeedDist(zSpeedRange.min, zSpeedRange.max);
|
||||
|
||||
float angle = angleDist(gen);
|
||||
float speed = speedDist(gen);
|
||||
float zSpeed = zSpeedDist(gen);
|
||||
|
||||
// Теперь biasX берется из JSON
|
||||
if (emitterIndex == 0) {
|
||||
return Vector3f{
|
||||
cosf(angle) * speed - biasX,
|
||||
sinf(angle) * speed,
|
||||
zSpeed
|
||||
};
|
||||
}
|
||||
else {
|
||||
return Vector3f{
|
||||
cosf(angle) * speed + biasX,
|
||||
sinf(angle) * speed,
|
||||
zSpeed
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<SparkParticle>& SparkEmitter::getParticles() const {
|
||||
return particles;
|
||||
}
|
||||
|
||||
size_t SparkEmitter::getActiveParticleCount() const {
|
||||
size_t count = 0;
|
||||
for (const auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool SparkEmitter::loadFromJsonFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
std::cout << "Loading spark config from: " << path << std::endl;
|
||||
std::ifstream in(path);
|
||||
if (!in.is_open()) {
|
||||
std::cerr << "Failed to open JSON file: " << path << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
json j;
|
||||
try {
|
||||
in >> j;
|
||||
std::cout << "JSON parsed successfully" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
std::cout << "JSON content: " << j.dump(2) << std::endl;
|
||||
|
||||
auto requireKey = [&](const std::string& key) {
|
||||
if (!j.contains(key)) {
|
||||
std::cerr << "Missing required key in spark JSON: " << key << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
};
|
||||
|
||||
requireKey("emissionPoints");
|
||||
requireKey("texture");
|
||||
requireKey("speedRange");
|
||||
requireKey("zSpeedRange");
|
||||
requireKey("scaleRange");
|
||||
requireKey("lifeTimeRange");
|
||||
requireKey("emissionRate");
|
||||
requireKey("maxParticles");
|
||||
requireKey("particleSize");
|
||||
requireKey("biasX");
|
||||
requireKey("shaderProgramName");
|
||||
|
||||
emissionRate = j["emissionRate"].get<float>();
|
||||
|
||||
maxParticles = j["maxParticles"].get<int>();
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
std::cout << "Max particles: " << maxParticles << std::endl;
|
||||
|
||||
particleSize = j["particleSize"].get<float>();
|
||||
std::cout << "Particle size: " << particleSize << std::endl;
|
||||
|
||||
biasX = j["biasX"].get<float>();
|
||||
std::cout << "Bias X: " << biasX << std::endl;
|
||||
|
||||
// emissionPoints
|
||||
std::vector<Vector3f> points;
|
||||
if (j.contains("emissionPoints") && j["emissionPoints"].is_array()) {
|
||||
for (const auto& el : j["emissionPoints"]) {
|
||||
if (el.contains("position") && el["position"].is_array()) {
|
||||
auto arr = el["position"];
|
||||
points.push_back(Vector3f{ arr[0].get<float>(), arr[1].get<float>(), arr[2].get<float>() });
|
||||
std::cout << "Fixed point: [" << arr[0] << ", " << arr[1] << ", " << arr[2] << "]" << std::endl;
|
||||
}
|
||||
else if (el.contains("positionRange") && el["positionRange"].is_object()) {
|
||||
auto pr = el["positionRange"];
|
||||
auto minArr = pr["min"];
|
||||
auto maxArr = pr["max"];
|
||||
int count = 1;
|
||||
if (el.contains("count")) count = el["count"].get<int>();
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<float> dx(minArr[0].get<float>(), maxArr[0].get<float>());
|
||||
std::uniform_real_distribution<float> dy(minArr[1].get<float>(), maxArr[1].get<float>());
|
||||
std::uniform_real_distribution<float> dz(minArr[2].get<float>(), maxArr[2].get<float>());
|
||||
|
||||
for (int k = 0; k < count; ++k) {
|
||||
Vector3f randomPoint{ dx(gen), dy(gen), dz(gen) };
|
||||
points.push_back(randomPoint);
|
||||
std::cout << "Random point " << k + 1 << ": [" << randomPoint.v[0]
|
||||
<< ", " << randomPoint.v[1] << ", " << randomPoint.v[2] << "]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!points.empty()) {
|
||||
setEmissionPoints(points);
|
||||
std::cout << "Total emission points: " << emissionPoints.size() << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Emission points parsed but empty" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "emissionPoints is not array" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
// Ranges
|
||||
if (j.contains("speedRange") && j["speedRange"].is_array()) {
|
||||
auto a = j["speedRange"];
|
||||
speedRange.min = a[0].get<float>();
|
||||
speedRange.max = a[1].get<float>();
|
||||
std::cout << "Speed range: [" << speedRange.min << ", " << speedRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "speedRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("zSpeedRange") && j["zSpeedRange"].is_array()) {
|
||||
auto a = j["zSpeedRange"];
|
||||
zSpeedRange.min = a[0].get<float>();
|
||||
zSpeedRange.max = a[1].get<float>();
|
||||
std::cout << "Z speed range: [" << zSpeedRange.min << ", " << zSpeedRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "zSpeedRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("scaleRange") && j["scaleRange"].is_array()) {
|
||||
auto a = j["scaleRange"];
|
||||
scaleRange.min = a[0].get<float>();
|
||||
scaleRange.max = a[1].get<float>();
|
||||
std::cout << "Scale range: [" << scaleRange.min << ", " << scaleRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "scaleRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("lifeTimeRange") && j["lifeTimeRange"].is_array()) {
|
||||
auto a = j["lifeTimeRange"];
|
||||
lifeTimeRange.min = a[0].get<float>();
|
||||
lifeTimeRange.max = a[1].get<float>();
|
||||
std::cout << "Life time range: [" << lifeTimeRange.min << ", " << lifeTimeRange.max << "]" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "lifeTimeRange missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
// texture
|
||||
if (j.contains("texture") && j["texture"].is_string()) {
|
||||
std::string texPath = j["texture"].get<std::string>();
|
||||
std::cout << "Loading texture: " << texPath << std::endl;
|
||||
|
||||
try {
|
||||
auto texData = CreateTextureDataFromPng(texPath.c_str(), zipFile.c_str());
|
||||
texture = std::make_shared<Texture>(texData);
|
||||
std::cout << "Texture loaded successfully, ID: " << texture->getTexID() << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Texture load error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "No texture specified or invalid type in JSON" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
// shaders
|
||||
if (j.contains("shaderProgramName") && j["shaderProgramName"].is_string()) {
|
||||
shaderProgramName = j["shaderProgramName"].get<std::string>();
|
||||
std::cout << "Shader program name: " << shaderProgramName << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "shaderProgramName missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
|
||||
if (j.contains("vertexShader") && j.contains("fragmentShader")
|
||||
&& j["vertexShader"].is_string() && j["fragmentShader"].is_string()) {
|
||||
std::string v = j["vertexShader"].get<std::string>();
|
||||
std::string f = j["fragmentShader"].get<std::string>();
|
||||
std::cout << "Loading shaders - vertex: " << v << ", fragment: " << f << std::endl;
|
||||
|
||||
try {
|
||||
renderer.shaderManager.AddShaderFromFiles(shaderProgramName, v, f, zipFile.c_str());
|
||||
std::cout << "Shaders loaded successfully" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Shader load error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load spark emitter config file!");
|
||||
}
|
||||
}
|
||||
|
||||
drawDataDirty = true;
|
||||
configured = true;
|
||||
std::cout << "SparkEmitter configuration loaded successfully!" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct SparkParticle {
|
||||
Vector3f position;
|
||||
Vector3f velocity;
|
||||
float scale;
|
||||
float lifeTime;
|
||||
float maxLifeTime;
|
||||
bool active;
|
||||
int emitterIndex;
|
||||
|
||||
SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f),
|
||||
lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) {
|
||||
}
|
||||
};
|
||||
|
||||
class SparkEmitter {
|
||||
private:
|
||||
std::vector<SparkParticle> particles;
|
||||
std::vector<Vector3f> emissionPoints;
|
||||
std::chrono::steady_clock::time_point lastEmissionTime;
|
||||
float emissionRate;
|
||||
bool isActive;
|
||||
|
||||
std::vector<Vector3f> drawPositions;
|
||||
std::vector<Vector2f> drawTexCoords;
|
||||
bool drawDataDirty;
|
||||
VertexRenderStruct sparkQuad;
|
||||
std::shared_ptr<Texture> texture;
|
||||
|
||||
int maxParticles;
|
||||
float particleSize;
|
||||
float biasX;
|
||||
|
||||
// Ranges (used when config supplies intervals)
|
||||
struct FloatRange { float min; float max; };
|
||||
FloatRange speedRange; // XY speed
|
||||
FloatRange zSpeedRange; // Z speed
|
||||
FloatRange scaleRange;
|
||||
FloatRange lifeTimeRange;
|
||||
|
||||
std::string shaderProgramName;
|
||||
|
||||
bool configured;
|
||||
void prepareDrawData();
|
||||
|
||||
public:
|
||||
SparkEmitter();
|
||||
SparkEmitter(const std::vector<Vector3f>& positions, float rate = 100.0f);
|
||||
SparkEmitter(const std::vector<Vector3f>& positions,
|
||||
std::shared_ptr<Texture> tex,
|
||||
float rate = 100.0f);
|
||||
|
||||
void setEmissionPoints(const std::vector<Vector3f>& positions);
|
||||
void setTexture(std::shared_ptr<Texture> tex);
|
||||
void setEmissionRate(float rate) { emissionRate = rate; }
|
||||
void setShaderProgramName(const std::string& name) { shaderProgramName = name; }
|
||||
void setMaxParticles(int max) { maxParticles = max; particles.resize(maxParticles); }
|
||||
void setParticleSize(float size) { particleSize = size; }
|
||||
void setBiasX(float bias) { biasX = bias; }
|
||||
|
||||
bool loadFromJsonFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
|
||||
void update(float deltaTimeMs);
|
||||
void emit();
|
||||
|
||||
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
|
||||
|
||||
const std::vector<SparkParticle>& getParticles() const;
|
||||
size_t getActiveParticleCount() const;
|
||||
std::shared_ptr<Texture> getTexture() const { return texture; }
|
||||
|
||||
private:
|
||||
void initParticle(SparkParticle& particle, int emitterIndex);
|
||||
Vector3f getRandomVelocity(int emitterIndex);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,390 @@
|
||||
#include "TextModel.h"
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
|
||||
VertexDataStruct LoadFromTextFile(const std::string& fileName, const std::string& ZIPFileName)
|
||||
{
|
||||
VertexDataStruct result;
|
||||
std::ifstream filestream;
|
||||
std::istringstream zipStream;
|
||||
|
||||
if (!ZIPFileName.empty())
|
||||
{
|
||||
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||
std::string fileContents(fileData.begin(), fileData.end());
|
||||
zipStream.str(fileContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
filestream.open(fileName);
|
||||
}
|
||||
|
||||
// Создаем ссылку f на нужный поток – после этого код ниже остается без изменений
|
||||
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
||||
|
||||
|
||||
//Skip first 5 lines
|
||||
std::string tempLine;
|
||||
|
||||
std::getline(f, tempLine);
|
||||
|
||||
static const std::regex pattern_count(R"(\d+)");
|
||||
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
|
||||
static const std::regex pattern_int(R"([-]?\d+)");
|
||||
|
||||
|
||||
std::smatch match;
|
||||
|
||||
int numberVertices;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberVertices = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
std::vector<Vector3f> vertices;
|
||||
|
||||
vertices.resize(numberVertices);
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
vertices[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
}
|
||||
|
||||
std::cout << "UV Coordinates" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===UV Coordinates:
|
||||
|
||||
std::getline(f, tempLine); //triangle count
|
||||
int numberTriangles;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberTriangles = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
|
||||
// Now process UVs
|
||||
std::vector<std::array<Vector2f, 3>> uvCoords;
|
||||
|
||||
uvCoords.resize(numberTriangles);
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine); //Face 0
|
||||
|
||||
int uvCount;
|
||||
std::getline(f, tempLine);
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
uvCount = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
if (uvCount != 3)
|
||||
{
|
||||
throw std::runtime_error("more than 3 uvs");
|
||||
}
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
if (floatValues.size() != 2)
|
||||
{
|
||||
throw std::runtime_error("more than 2 uvs---");
|
||||
}
|
||||
|
||||
uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] };
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Normals go" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===Normals:
|
||||
|
||||
|
||||
std::vector<Vector3f> normals;
|
||||
|
||||
normals.resize(numberVertices);
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
}
|
||||
|
||||
std::cout << "Triangles go:" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===Triangles: 3974
|
||||
|
||||
|
||||
std::vector<std::array<int, 3>> triangles;
|
||||
|
||||
triangles.resize(numberTriangles);
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<int> intValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_int)) {
|
||||
intValues.push_back(std::stoi(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
triangles[i] = { intValues[0], intValues[1], intValues[2] };
|
||||
}
|
||||
|
||||
|
||||
std::cout << "Process vertices" << std::endl;
|
||||
|
||||
|
||||
|
||||
// Now let's process vertices
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
|
||||
result.PositionData.push_back(vertices[triangles[i][0]]);
|
||||
result.PositionData.push_back(vertices[triangles[i][1]]);
|
||||
result.PositionData.push_back(vertices[triangles[i][2]]);
|
||||
|
||||
/*
|
||||
result.NormalData.push_back(normals[triangles[i][0]]);
|
||||
result.NormalData.push_back(normals[triangles[i][1]]);
|
||||
result.NormalData.push_back(normals[triangles[i][2]]);
|
||||
*/
|
||||
result.TexCoordData.push_back(uvCoords[i][0]);
|
||||
result.TexCoordData.push_back(uvCoords[i][1]);
|
||||
result.TexCoordData.push_back(uvCoords[i][2]);
|
||||
|
||||
}
|
||||
|
||||
//Swap from Blender format to OpenGL format
|
||||
for (int i = 0; i < result.PositionData.size(); i++)
|
||||
{
|
||||
Vector3f tempVec = result.PositionData[i];
|
||||
result.PositionData[i].v[0] = tempVec.v[1];
|
||||
result.PositionData[i].v[1] = tempVec.v[2];
|
||||
result.PositionData[i].v[2] = tempVec.v[0];
|
||||
|
||||
/*
|
||||
tempVec = result.NormalData[i];
|
||||
result.NormalData[i].v[0] = tempVec.v[1];
|
||||
result.NormalData[i].v[1] = tempVec.v[2];
|
||||
result.NormalData[i].v[2] = tempVec.v[0];*/
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName)
|
||||
{
|
||||
VertexDataStruct result;
|
||||
std::ifstream filestream;
|
||||
std::istringstream zipStream;
|
||||
|
||||
// --- 1. Открытие потока (без изменений) ---
|
||||
if (!ZIPFileName.empty())
|
||||
{
|
||||
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||
std::string fileContents(fileData.begin(), fileData.end());
|
||||
zipStream.str(fileContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
filestream.open(fileName);
|
||||
if (!filestream.is_open()) {
|
||||
throw std::runtime_error("Failed to open file: " + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
||||
|
||||
std::string tempLine;
|
||||
std::smatch match;
|
||||
|
||||
// Обновленные регулярки
|
||||
// pattern_float стал чуть надежнее для чисел вида "0" или "-1" без точки, если вдруг Python округлит до int
|
||||
static const std::regex pattern_count(R"(\d+)");
|
||||
static const std::regex pattern_float(R"([-]?\d+(\.\d+)?)");
|
||||
static const std::regex pattern_int(R"([-]?\d+)");
|
||||
|
||||
// --- 2. Парсинг Вершин (Pos + Norm + UV) ---
|
||||
|
||||
// Ищем заголовок ===Vertices
|
||||
while (std::getline(f, tempLine)) {
|
||||
if (tempLine.find("===Vertices") != std::string::npos) break;
|
||||
}
|
||||
|
||||
int numberVertices = 0;
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
numberVertices = std::stoi(match.str());
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Vertices header not found or invalid.");
|
||||
}
|
||||
|
||||
// Временные буферы для хранения "уникальных" вершин перед разверткой по индексам
|
||||
std::vector<Vector3f> tempPositions(numberVertices);
|
||||
std::vector<Vector3f> tempNormals(numberVertices);
|
||||
std::vector<Vector2f> tempUVs(numberVertices);
|
||||
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
// Строка вида: V 0: Pos(x, y, z) Norm(x, y, z) UV(u, v)
|
||||
|
||||
std::vector<float> floatValues;
|
||||
floatValues.reserve(8); // Ожидаем ровно 8 чисел (3 pos + 3 norm + 2 uv)
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
// Проверка целостности строки (ID вершины regex может поймать первым, но нас интересуют данные)
|
||||
// Обычно ID идет первым (0), потом 3+3+2 float. Итого 9 чисел, если считать ID.
|
||||
// Ваш Python пишет "V 0:", regex поймает 0. Потом 8 флоатов.
|
||||
|
||||
// Если regex ловит ID вершины как float (что вероятно), нам нужно смещение.
|
||||
// ID - floatValues[0]
|
||||
// Pos - [1], [2], [3]
|
||||
// Norm - [4], [5], [6]
|
||||
// UV - [7], [8]
|
||||
|
||||
if (floatValues.size() < 9) {
|
||||
throw std::runtime_error("Malformed vertex line at index " + std::to_string(i));
|
||||
}
|
||||
|
||||
tempPositions[i] = Vector3f{ floatValues[1], floatValues[2], floatValues[3] };
|
||||
tempNormals[i] = Vector3f{ floatValues[4], floatValues[5], floatValues[6] };
|
||||
tempUVs[i] = Vector2f{ floatValues[7], floatValues[8] };
|
||||
}
|
||||
|
||||
// --- 3. Парсинг Треугольников (Индексов) ---
|
||||
|
||||
// Пропускаем пустые строки до заголовка треугольников
|
||||
while (std::getline(f, tempLine)) {
|
||||
if (tempLine.find("===Triangles") != std::string::npos) break;
|
||||
}
|
||||
|
||||
int numberTriangles = 0;
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
numberTriangles = std::stoi(match.str());
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Triangles header not found.");
|
||||
}
|
||||
|
||||
// Резервируем память в result, чтобы избежать лишних аллокаций
|
||||
result.PositionData.reserve(numberTriangles * 3);
|
||||
result.NormalData.reserve(numberTriangles * 3);
|
||||
result.TexCoordData.reserve(numberTriangles * 3);
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
// Строка вида: Tri: 0 1 2
|
||||
|
||||
std::vector<int> indices;
|
||||
indices.reserve(3);
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_int)) {
|
||||
indices.push_back(std::stoi(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
if (indices.size() != 3) {
|
||||
throw std::runtime_error("Malformed triangle line at index " + std::to_string(i));
|
||||
}
|
||||
|
||||
// --- 4. Заполнение VertexDataStruct (Flattening) ---
|
||||
// Берем данные из временных буферов по индексам и кладем в итоговый массив
|
||||
|
||||
for (int k = 0; k < 3; k++) {
|
||||
int idx = indices[k];
|
||||
result.PositionData.push_back(tempPositions[idx]);
|
||||
result.NormalData.push_back(tempNormals[idx]);
|
||||
result.TexCoordData.push_back(tempUVs[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 5. Конвертация координат (Blender -> OpenGL/Engine) ---
|
||||
// Сохраняем вашу логику смены осей: X->Z, Y->X, Z->Y
|
||||
|
||||
for (size_t i = 0; i < result.PositionData.size(); i++)
|
||||
{
|
||||
Vector3f originalPos = result.PositionData[i];
|
||||
result.PositionData[i].v[0] = originalPos.v[1]; // New X = Old Y
|
||||
result.PositionData[i].v[1] = originalPos.v[2]; // New Y = Old Z
|
||||
result.PositionData[i].v[2] = originalPos.v[0]; // New Z = Old X
|
||||
|
||||
Vector3f originalNorm = result.NormalData[i];
|
||||
result.NormalData[i].v[0] = originalNorm.v[1];
|
||||
result.NormalData[i].v[1] = originalNorm.v[2];
|
||||
result.NormalData[i].v[2] = originalNorm.v[0];
|
||||
}
|
||||
|
||||
std::cout << "Model loaded: " << numberVertices << " verts, " << numberTriangles << " tris." << std::endl;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
VertexDataStruct LoadFromTextFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
#include "UiManager.h"
|
||||
#include "utils/Utils.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
void UiButton::buildMesh() {
|
||||
mesh.data.PositionData.clear();
|
||||
mesh.data.TexCoordData.clear();
|
||||
|
||||
float x0 = rect.x;
|
||||
float y0 = rect.y;
|
||||
float x1 = rect.x + rect.w;
|
||||
float y1 = rect.y + rect.h;
|
||||
|
||||
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
mesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
mesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
|
||||
mesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiButton::draw(Renderer& renderer) const {
|
||||
if (!texNormal) return;
|
||||
const std::shared_ptr<Texture>* tex = &texNormal;
|
||||
switch (state) {
|
||||
case ButtonState::Normal: tex = &texNormal; break;
|
||||
case ButtonState::Hover: tex = &texHover; break;
|
||||
case ButtonState::Pressed: tex = &texPressed; break;
|
||||
}
|
||||
if (!(*tex)) return;
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, (*tex)->getTexID());
|
||||
renderer.DrawVertexRenderStruct(mesh);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
}
|
||||
|
||||
void UiSlider::buildTrackMesh() {
|
||||
trackMesh.data.PositionData.clear();
|
||||
trackMesh.data.TexCoordData.clear();
|
||||
|
||||
float x0 = rect.x;
|
||||
float y0 = rect.y;
|
||||
float x1 = rect.x + rect.w;
|
||||
float y1 = rect.y + rect.h;
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
trackMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
trackMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
|
||||
trackMesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiSlider::buildKnobMesh() {
|
||||
knobMesh.data.PositionData.clear();
|
||||
knobMesh.data.TexCoordData.clear();
|
||||
|
||||
float kw = vertical ? rect.w * 4.0f : rect.w * 0.5f;
|
||||
float kh = vertical ? rect.w * 4.0f : rect.h * 0.5f;
|
||||
|
||||
float cx = rect.x + rect.w * 0.5f;
|
||||
float cy = rect.y + (vertical ? (value * rect.h) : (rect.h * 0.5f));
|
||||
|
||||
float x0 = cx - kw * 0.5f;
|
||||
float y0 = cy - kh * 0.5f;
|
||||
float x1 = cx + kw * 0.5f;
|
||||
float y1 = cy + kh * 0.5f;
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
|
||||
knobMesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiSlider::draw(Renderer& renderer) const {
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
if (texTrack) {
|
||||
glBindTexture(GL_TEXTURE_2D, texTrack->getTexID());
|
||||
renderer.DrawVertexRenderStruct(trackMesh);
|
||||
}
|
||||
if (texKnob) {
|
||||
glBindTexture(GL_TEXTURE_2D, texKnob->getTexID());
|
||||
renderer.DrawVertexRenderStruct(knobMesh);
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
}
|
||||
|
||||
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
std::ifstream in(path);
|
||||
if (!in.is_open()) {
|
||||
std::cerr << "UiManager: failed to open " << path << std::endl;
|
||||
throw std::runtime_error("Failed to load UI file: " + path);
|
||||
}
|
||||
|
||||
json j;
|
||||
try {
|
||||
in >> j;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
|
||||
throw std::runtime_error("Failed to load UI file: " + path);
|
||||
}
|
||||
|
||||
if (!j.contains("root") || !j["root"].is_object()) {
|
||||
std::cerr << "UiManager: root node missing or invalid" << std::endl;
|
||||
throw std::runtime_error("Failed to load UI file: " + path);
|
||||
}
|
||||
|
||||
root = parseNode(j["root"], renderer, zipFile);
|
||||
layoutNode(root);
|
||||
buttons.clear();
|
||||
sliders.clear();
|
||||
collectButtonsAndSliders(root);
|
||||
|
||||
for (auto& b : buttons) {
|
||||
b->buildMesh();
|
||||
}
|
||||
for (auto& s : sliders) {
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<UiNode> UiManager::parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
|
||||
auto node = std::make_shared<UiNode>();
|
||||
if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get<std::string>();
|
||||
if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get<std::string>();
|
||||
|
||||
if (j.contains("x")) node->rect.x = j["x"].get<float>();
|
||||
if (j.contains("y")) node->rect.y = j["y"].get<float>();
|
||||
if (j.contains("width")) node->rect.w = j["width"].get<float>();
|
||||
if (j.contains("height")) node->rect.h = j["height"].get<float>();
|
||||
|
||||
if (j.contains("orientation") && j["orientation"].is_string()) node->orientation = j["orientation"].get<std::string>();
|
||||
if (j.contains("spacing")) node->spacing = j["spacing"].get<float>();
|
||||
|
||||
if (node->type == "Button") {
|
||||
auto btn = std::make_shared<UiButton>();
|
||||
btn->name = node->name;
|
||||
btn->rect = node->rect;
|
||||
|
||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
|
||||
throw std::runtime_error("UI button textures missing");
|
||||
}
|
||||
auto t = j["textures"];
|
||||
auto loadTex = [&](const std::string& key)->std::shared_ptr<Texture> {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str());
|
||||
return std::make_shared<Texture>(data);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: failed load texture " << path << " : " << e.what() << std::endl;
|
||||
throw std::runtime_error("UI texture load failed: " + path);
|
||||
}
|
||||
};
|
||||
|
||||
btn->texNormal = loadTex("normal");
|
||||
btn->texHover = loadTex("hover");
|
||||
btn->texPressed = loadTex("pressed");
|
||||
|
||||
node->button = btn;
|
||||
}
|
||||
else if (node->type == "Slider") {
|
||||
auto s = std::make_shared<UiSlider>();
|
||||
s->name = node->name;
|
||||
s->rect = node->rect;
|
||||
|
||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
|
||||
throw std::runtime_error("UI slider textures missing");
|
||||
}
|
||||
auto t = j["textures"];
|
||||
auto loadTex = [&](const std::string& key)->std::shared_ptr<Texture> {
|
||||
if (!t.contains(key) || !t[key].is_string()) return nullptr;
|
||||
std::string path = t[key].get<std::string>();
|
||||
try {
|
||||
auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str());
|
||||
return std::make_shared<Texture>(data);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: failed load texture " << path << " : " << e.what() << std::endl;
|
||||
throw std::runtime_error("UI texture load failed: " + path);
|
||||
}
|
||||
};
|
||||
|
||||
s->texTrack = loadTex("track");
|
||||
s->texKnob = loadTex("knob");
|
||||
|
||||
if (j.contains("value")) s->value = j["value"].get<float>();
|
||||
if (j.contains("orientation")) {
|
||||
std::string orient = j["orientation"].get<std::string>();
|
||||
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
|
||||
s->vertical = (orient != "horizontal");
|
||||
}
|
||||
|
||||
node->slider = s;
|
||||
}
|
||||
|
||||
if (j.contains("children") && j["children"].is_array()) {
|
||||
for (const auto& ch : j["children"]) {
|
||||
node->children.push_back(parseNode(ch, renderer, zipFile));
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node) {
|
||||
for (auto& child : node->children) {
|
||||
child->rect.x += node->rect.x;
|
||||
child->rect.y += node->rect.y;
|
||||
}
|
||||
|
||||
if (node->type == "LinearLayout") {
|
||||
std::string orient = node->orientation;
|
||||
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
|
||||
|
||||
float cursorX = node->rect.x;
|
||||
float cursorY = node->rect.y;
|
||||
for (auto& child : node->children) {
|
||||
if (orient == "horizontal") {
|
||||
child->rect.x = cursorX;
|
||||
child->rect.y = node->rect.y;
|
||||
cursorX += child->rect.w + node->spacing;
|
||||
}
|
||||
else {
|
||||
child->rect.x = node->rect.x;
|
||||
child->rect.y = cursorY;
|
||||
cursorY += child->rect.h + node->spacing;
|
||||
}
|
||||
layoutNode(child);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto& child : node->children) {
|
||||
layoutNode(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
|
||||
if (node->button) {
|
||||
buttons.push_back(node->button);
|
||||
}
|
||||
if (node->slider) {
|
||||
sliders.push_back(node->slider);
|
||||
}
|
||||
for (auto& c : node->children) collectButtonsAndSliders(c);
|
||||
}
|
||||
|
||||
bool UiManager::setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb) {
|
||||
auto b = findButton(name);
|
||||
if (!b) {
|
||||
std::cerr << "UiManager: setButtonCallback failed, button not found: " << name << std::endl;
|
||||
return false;
|
||||
}
|
||||
b->onClick = std::move(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
|
||||
const std::string& trackPath, const std::string& knobPath, float initialValue, bool vertical) {
|
||||
|
||||
auto s = std::make_shared<UiSlider>();
|
||||
s->name = name;
|
||||
s->rect = rect;
|
||||
s->value = std::clamp(initialValue, 0.0f, 1.0f);
|
||||
s->vertical = vertical;
|
||||
|
||||
try {
|
||||
if (!trackPath.empty()) {
|
||||
auto data = CreateTextureDataFromPng(trackPath.c_str(), zipFile.c_str());
|
||||
s->texTrack = std::make_shared<Texture>(data);
|
||||
}
|
||||
if (!knobPath.empty()) {
|
||||
auto data = CreateTextureDataFromPng(knobPath.c_str(), zipFile.c_str());
|
||||
s->texKnob = std::make_shared<Texture>(data);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: addSlider failed to load textures: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
|
||||
sliders.push_back(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiSlider> UiManager::findSlider(const std::string& name) {
|
||||
for (auto& s : sliders) if (s->name == name) return s;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UiManager::setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb) {
|
||||
auto s = findSlider(name);
|
||||
if (!s) {
|
||||
std::cerr << "UiManager: setSliderCallback failed, slider not found: " << name << std::endl;
|
||||
return false;
|
||||
}
|
||||
s->onValueChanged = std::move(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::setSliderValue(const std::string& name, float value) {
|
||||
auto s = findSlider(name);
|
||||
if (!s) return false;
|
||||
value = std::clamp(value, 0.0f, 1.0f);
|
||||
if (fabs(s->value - value) < 1e-6f) return true;
|
||||
s->value = value;
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
MenuState prev;
|
||||
prev.root = root;
|
||||
prev.buttons = buttons;
|
||||
prev.sliders = sliders;
|
||||
prev.pressedButton = pressedButton;
|
||||
prev.pressedSlider = pressedSlider;
|
||||
prev.path = "";
|
||||
|
||||
try {
|
||||
loadFromFile(path, renderer, zipFile);
|
||||
menuStack.push_back(std::move(prev));
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: pushMenuFromFile failed to load " << path << " : " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UiManager::popMenu() {
|
||||
if (menuStack.empty()) {
|
||||
std::cerr << "UiManager: popMenu called but menu stack is empty" << std::endl;
|
||||
return false;
|
||||
}
|
||||
auto s = menuStack.back();
|
||||
menuStack.pop_back();
|
||||
|
||||
root = s.root;
|
||||
buttons = s.buttons;
|
||||
sliders = s.sliders;
|
||||
pressedButton = s.pressedButton;
|
||||
pressedSlider = s.pressedSlider;
|
||||
|
||||
for (auto& b : buttons) {
|
||||
if (b) b->buildMesh();
|
||||
}
|
||||
for (auto& sl : sliders) {
|
||||
if (sl) { sl->buildTrackMesh(); sl->buildKnobMesh(); }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UiManager::clearMenuStack() {
|
||||
menuStack.clear();
|
||||
}
|
||||
|
||||
void UiManager::draw(Renderer& renderer) {
|
||||
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
for (const auto& b : buttons) {
|
||||
b->draw(renderer);
|
||||
}
|
||||
for (const auto& s : sliders) {
|
||||
s->draw(renderer);
|
||||
}
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
}
|
||||
|
||||
void UiManager::onMouseMove(int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
if (b->rect.contains((float)x, (float)y)) {
|
||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
||||
}
|
||||
else {
|
||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal;
|
||||
}
|
||||
}
|
||||
|
||||
if (pressedSlider) {
|
||||
auto s = pressedSlider;
|
||||
float t;
|
||||
if (s->vertical) {
|
||||
t = (y - s->rect.y) / s->rect.h;
|
||||
}
|
||||
else {
|
||||
t = (x - s->rect.x) / s->rect.w;
|
||||
}
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseDown(int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
if (b->rect.contains((float)x, (float)y)) {
|
||||
b->state = ButtonState::Pressed;
|
||||
pressedButton = b;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& s : sliders) {
|
||||
if (s->rect.contains((float)x, (float)y)) {
|
||||
pressedSlider = s;
|
||||
float t;
|
||||
if (s->vertical) {
|
||||
t = (y - s->rect.y) / s->rect.h;
|
||||
}
|
||||
else {
|
||||
t = (x - s->rect.x) / s->rect.w;
|
||||
}
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseUp(int x, int y) {
|
||||
for (auto& b : buttons) {
|
||||
bool contains = b->rect.contains((float)x, (float)y);
|
||||
if (b->state == ButtonState::Pressed) {
|
||||
if (contains && pressedButton == b) {
|
||||
if (b->onClick) {
|
||||
b->onClick(b->name);
|
||||
}
|
||||
}
|
||||
b->state = contains ? ButtonState::Hover : ButtonState::Normal;
|
||||
}
|
||||
}
|
||||
pressedButton.reset();
|
||||
|
||||
if (pressedSlider) {
|
||||
pressedSlider.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) {
|
||||
for (auto& b : buttons) if (b->name == name) return b;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include "Environment.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct UiRect {
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float w = 0;
|
||||
float h = 0;
|
||||
bool contains(float px, float py) const {
|
||||
return px >= x && px <= x + w && py >= y && py <= y + h;
|
||||
}
|
||||
};
|
||||
|
||||
enum class ButtonState {
|
||||
Normal,
|
||||
Hover,
|
||||
Pressed
|
||||
};
|
||||
|
||||
struct UiButton {
|
||||
std::string name;
|
||||
UiRect rect;
|
||||
std::shared_ptr<Texture> texNormal;
|
||||
std::shared_ptr<Texture> texHover;
|
||||
std::shared_ptr<Texture> texPressed;
|
||||
ButtonState state = ButtonState::Normal;
|
||||
|
||||
VertexRenderStruct mesh;
|
||||
|
||||
std::function<void(const std::string&)> onClick;
|
||||
|
||||
void buildMesh();
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiSlider {
|
||||
std::string name;
|
||||
UiRect rect;
|
||||
std::shared_ptr<Texture> texTrack;
|
||||
std::shared_ptr<Texture> texKnob;
|
||||
|
||||
VertexRenderStruct trackMesh;
|
||||
VertexRenderStruct knobMesh;
|
||||
|
||||
float value = 0.0f;
|
||||
bool vertical = true;
|
||||
|
||||
std::function<void(const std::string&, float)> onValueChanged;
|
||||
|
||||
void buildTrackMesh();
|
||||
void buildKnobMesh();
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiNode {
|
||||
std::string type;
|
||||
UiRect rect;
|
||||
std::string name;
|
||||
std::vector<std::shared_ptr<UiNode>> children;
|
||||
std::shared_ptr<UiButton> button;
|
||||
std::shared_ptr<UiSlider> slider;
|
||||
std::string orientation = "vertical";
|
||||
float spacing = 0.0f;
|
||||
};
|
||||
|
||||
class UiManager {
|
||||
public:
|
||||
UiManager() = default;
|
||||
|
||||
void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
|
||||
void draw(Renderer& renderer);
|
||||
|
||||
void onMouseMove(int x, int y);
|
||||
void onMouseDown(int x, int y);
|
||||
void onMouseUp(int x, int y);
|
||||
|
||||
bool isUiInteraction() const {
|
||||
return pressedButton != nullptr || pressedSlider != nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiButton> findButton(const std::string& name);
|
||||
|
||||
bool setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb);
|
||||
|
||||
bool addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
|
||||
const std::string& trackPath, const std::string& knobPath, float initialValue = 0.0f, bool vertical = true);
|
||||
|
||||
std::shared_ptr<UiSlider> findSlider(const std::string& name);
|
||||
bool setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb);
|
||||
bool setSliderValue(const std::string& name, float value); // programmatic set (clamped 0..1)
|
||||
|
||||
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
bool popMenu();
|
||||
void clearMenuStack();
|
||||
|
||||
private:
|
||||
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
|
||||
void layoutNode(const std::shared_ptr<UiNode>& node);
|
||||
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
|
||||
|
||||
std::shared_ptr<UiNode> root;
|
||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||
|
||||
std::shared_ptr<UiButton> pressedButton;
|
||||
std::shared_ptr<UiSlider> pressedSlider;
|
||||
|
||||
struct MenuState {
|
||||
std::shared_ptr<UiNode> root;
|
||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||
std::shared_ptr<UiButton> pressedButton;
|
||||
std::shared_ptr<UiSlider> pressedSlider;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
std::vector<MenuState> menuStack;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
Vendored
+25526
File diff suppressed because it is too large.
Load diff
+12146
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,88 @@
|
||||
#include "Game.h"
|
||||
#include "Environment.h"
|
||||
#include <iostream>
|
||||
|
||||
ZL::Game game;
|
||||
void MainLoop() {
|
||||
game.update();
|
||||
}
|
||||
int main(int argc, char* argv[]) {
|
||||
try
|
||||
{
|
||||
constexpr int CONST_WIDTH = 1280;
|
||||
constexpr int CONST_HEIGHT = 720;
|
||||
|
||||
ZL::Environment::width = CONST_WIDTH;
|
||||
ZL::Environment::height = CONST_HEIGHT;
|
||||
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
|
||||
SDL_Window* win = SDL_CreateWindow("Space Ship Game",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
CONST_WIDTH, CONST_HEIGHT,
|
||||
SDL_WINDOW_OPENGL);
|
||||
|
||||
if (!win) {
|
||||
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GLContext glContext = SDL_GL_CreateContext(win);
|
||||
if (!glContext) {
|
||||
std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Привязка контекста к окну — важно!
|
||||
SDL_GL_MakeCurrent(win, glContext);
|
||||
|
||||
ZL::Environment::window = win;
|
||||
#else
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
SDL_Log("SDL init failed: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
ZL::Environment::window = SDL_CreateWindow(
|
||||
"Space Ship Game",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
CONST_WIDTH, CONST_HEIGHT,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||||
);
|
||||
|
||||
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||||
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
|
||||
#endif
|
||||
|
||||
game.setup();
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||
#else
|
||||
while (!game.shouldExit()) {
|
||||
game.update();
|
||||
SDL_Delay(2);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
#include "PlanetData.h"
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
const float PlanetData::PLANET_RADIUS = 20000.f;
|
||||
const Vector3f PlanetData::PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f };
|
||||
|
||||
// --- Êîíñòàíòû äèàïàçîíîâ (ïåðåíåñåíû èç PlanetObject.cpp) ---
|
||||
|
||||
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2) {
|
||||
return id1 < id2 ? id1 + "_" + id2 : id2 + "_" + id1;
|
||||
}
|
||||
|
||||
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ïðîåêöèè (ëîêàëüíàÿ)
|
||||
static Vector3f projectPointOnPlane(const Vector3f& P, const Vector3f& A, const Vector3f& B, const Vector3f& C) {
|
||||
Vector3f AB = B + A * (-1.0f);
|
||||
Vector3f AC = C + A * (-1.0f);
|
||||
Vector3f N = AB.cross(AC).normalized();
|
||||
Vector3f AP = P + A * (-1.0f);
|
||||
float distance = N.dot(AP);
|
||||
return P + N * (-distance);
|
||||
}
|
||||
|
||||
PlanetData::PlanetData()
|
||||
: perlin(77777)
|
||||
, colorPerlin(123123)
|
||||
, currentLod(0)
|
||||
{
|
||||
currentLod = planetMeshLods.size() - 1; // Start with max LOD
|
||||
|
||||
initialVertexMap = {
|
||||
{{ 0.0f, 1.0f, 0.0f}, "A"},
|
||||
{{ 0.0f, -1.0f, 0.0f}, "B"},
|
||||
{{ 1.0f, 0.0f, 0.0f}, "C"},
|
||||
{{-1.0f, 0.0f, 0.0f}, "D"},
|
||||
{{ 0.0f, 0.0f, 1.0f}, "E"},
|
||||
{{ 0.0f, 0.0f, -1.0f}, "F"}
|
||||
};
|
||||
}
|
||||
|
||||
void PlanetData::init() {
|
||||
for (int i = 0; i < planetMeshLods.size(); i++) {
|
||||
planetMeshLods[i] = generateSphere(i, 0.025f);
|
||||
planetMeshLods[i].Scale(PLANET_RADIUS);
|
||||
planetMeshLods[i].Move(PLANET_CENTER_OFFSET);
|
||||
}
|
||||
|
||||
for (int i = 0; i < planetMeshLodsNoDist.size(); i++) {
|
||||
planetMeshLodsNoDist[i] = generateSphere(i, 0);
|
||||
planetMeshLodsNoDist[i].Scale(PLANET_RADIUS);
|
||||
planetMeshLodsNoDist[i].Move(PLANET_CENTER_OFFSET);
|
||||
}
|
||||
|
||||
planetAtmosphereLod = generateSphere(5, 0);
|
||||
planetAtmosphereLod.Scale(PLANET_RADIUS * 1.03);
|
||||
planetAtmosphereLod.Move(PLANET_CENTER_OFFSET);
|
||||
}
|
||||
|
||||
const LodLevel& PlanetData::getLodLevel(int level) const {
|
||||
return planetMeshLods.at(level);
|
||||
}
|
||||
|
||||
const LodLevel& PlanetData::getLodLevelNoDist(int level) const {
|
||||
return planetMeshLodsNoDist.at(level);
|
||||
}
|
||||
|
||||
const LodLevel& PlanetData::getAtmosphereLod() const {
|
||||
return planetAtmosphereLod;
|
||||
}
|
||||
|
||||
int PlanetData::getCurrentLodIndex() const {
|
||||
return currentLod;
|
||||
}
|
||||
|
||||
int PlanetData::getMaxLodIndex() const {
|
||||
return static_cast<int>(planetMeshLods.size() - 1);
|
||||
}
|
||||
|
||||
std::pair<float, float> PlanetData::calculateZRange(float dToPlanetSurface) {
|
||||
|
||||
|
||||
float currentZNear;
|
||||
float currentZFar;
|
||||
float alpha;
|
||||
|
||||
if (dToPlanetSurface > 2000)
|
||||
{
|
||||
currentZNear = 1000;
|
||||
currentZFar = currentZNear * 100;
|
||||
}
|
||||
else if (dToPlanetSurface > 1200)
|
||||
{
|
||||
currentZNear = 500;
|
||||
currentZFar = currentZNear * 100;
|
||||
}
|
||||
else if (dToPlanetSurface > 650)
|
||||
{
|
||||
currentZNear = 250;
|
||||
currentZFar = currentZNear * 100;
|
||||
}
|
||||
else if (dToPlanetSurface > 160)
|
||||
{
|
||||
currentZNear = 125;
|
||||
currentZFar = currentZNear * 150;
|
||||
}
|
||||
else if (dToPlanetSurface > 100)
|
||||
{
|
||||
currentZNear = 65;
|
||||
currentZFar = currentZNear * 170;
|
||||
}
|
||||
else if (dToPlanetSurface > 40)
|
||||
{
|
||||
currentZNear = 32;
|
||||
currentZFar = 10000.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentZNear = 16;
|
||||
currentZFar = 5000.f;
|
||||
}
|
||||
|
||||
return { currentZNear, currentZFar };
|
||||
}
|
||||
|
||||
|
||||
float PlanetData::distanceToPlanetSurface(const Vector3f& viewerPosition) {
|
||||
Vector3f shipLocalPosition = viewerPosition - PLANET_CENTER_OFFSET;
|
||||
|
||||
// Èñïîëüçóåì getTrianglesUnderCamera äëÿ ïîèñêà
|
||||
// ÂÍÈÌÀÍÈÅ: Çäåñü ðåêóðñèÿ ëîãèêè. Ìåòîä getTrianglesUnderCamera èñïîëüçóåò âíóòðè viewerPosition.
|
||||
// ×òîáû íå äóáëèðîâàòü êîä, ïåðåïèøåì ëîãèêó ïîèñêà çäåñü èëè áóäåì èñïîëüçîâàòü óæå íàéäåííûé ðàíåå.
|
||||
// Äëÿ ïðîñòîòû îñòàâèì ëîãèêó êàê â îðèãèíàëå, íî àäàïòèðîâàííóþ.
|
||||
|
||||
// ÂÀÆÍÎ: getTrianglesUnderCamera - ýòî "òÿæåëàÿ" ôóíêöèÿ. Â îðèãèíàëå îíà âûçûâàëàñü âíóòðè distanceToPlanetSurface.
|
||||
// Íî îíà òðåáóåò LOD. Èñïîëüçóåì MAX LOD äëÿ òî÷íîñòè.
|
||||
|
||||
// Ðåêóðñèâíûé ïîèñê òðåóãîëüíèêîâ ïîä êàìåðîé.
|
||||
// Çäåñü íàì íóæíî ðåàëèçîâàòü àíàëîã triangleUnderCamera, íî íå çàâèñÿùèé îò ãëîáàëüíîãî ñîñòîÿíèÿ.
|
||||
// Â îðèãèíàëå ôóíêöèÿ triangleUnderCamera áûëà ðåêóðñèâíîé.
|
||||
// Íèæå ÿ ðåàëèçóþ èòåðàòèâíûé èëè ðåêóðñèâíûé ïîäõîä âíóòðè getTrianglesUnderCamera.
|
||||
|
||||
// Âðåìåííîå ðåøåíèå: ïîëíàÿ êîïèÿ ëîãèêè ïîèñêà òðåóãîëüíèêà
|
||||
// Íî íàì íóæåí äîñòóï ê ìåòîäó.
|
||||
std::vector<int> targetTriangles = getTrianglesUnderCamera(viewerPosition);
|
||||
|
||||
if (targetTriangles.empty()) {
|
||||
return (shipLocalPosition.length() - PLANET_RADIUS);
|
||||
}
|
||||
|
||||
float lowestDistance;
|
||||
|
||||
int tri_index = targetTriangles[0];
|
||||
const auto& posData = planetMeshLods[currentLod].vertexData.PositionData;
|
||||
|
||||
size_t data_index = tri_index * 3;
|
||||
if (data_index + 2 >= posData.size()) {
|
||||
return (shipLocalPosition.length() - PLANET_RADIUS);
|
||||
}
|
||||
|
||||
const Vector3f& A = posData[data_index];
|
||||
const Vector3f& B = posData[data_index + 1];
|
||||
const Vector3f& C = posData[data_index + 2];
|
||||
|
||||
Vector3f P_proj = projectPointOnPlane(shipLocalPosition, A, B, C);
|
||||
Vector3f P_closest = P_proj;
|
||||
|
||||
lowestDistance = (shipLocalPosition - P_closest).length();
|
||||
|
||||
if (targetTriangles.size() <= 1)
|
||||
{
|
||||
return lowestDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < targetTriangles.size(); i++)
|
||||
{
|
||||
int tri_index = targetTriangles[i];
|
||||
const auto& posData = planetMeshLods[currentLod].vertexData.PositionData;
|
||||
|
||||
size_t data_index = tri_index * 3;
|
||||
if (data_index + 2 >= posData.size()) {
|
||||
return (shipLocalPosition.length() - PLANET_RADIUS);
|
||||
}
|
||||
|
||||
const Vector3f& A = posData[data_index];
|
||||
const Vector3f& B = posData[data_index + 1];
|
||||
const Vector3f& C = posData[data_index + 2];
|
||||
|
||||
Vector3f P_proj = projectPointOnPlane(shipLocalPosition, A, B, C);
|
||||
Vector3f P_closest = P_proj;
|
||||
|
||||
if (lowestDistance < (shipLocalPosition - P_closest).length())
|
||||
{
|
||||
lowestDistance = (shipLocalPosition - P_closest).length();
|
||||
}
|
||||
}
|
||||
|
||||
return lowestDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ðåàëèçàöèÿ getTrianglesUnderCamera (áûâøàÿ triangleUnderCamera) ---
|
||||
// Âñïîìîãàòåëüíàÿ ðåêóðñèâíàÿ ôóíêöèÿ, ñêðûòàÿ îò ïóáëè÷íîãî API
|
||||
static std::vector<int> recursiveTriangleSearch(int lod, const Vector3f& pos, const std::array<LodLevel, MAX_LOD_LEVELS>& meshes) {
|
||||
std::vector<int> r;
|
||||
// Ëîãèêà óðîâíÿ 0 (áàçîâûé îêòàýäð)
|
||||
if (lod == 0) {
|
||||
if (pos.v[1] >= 0) {
|
||||
if (pos.v[0] >= 0 && pos.v[2] >= 0) r.push_back(0);
|
||||
if (pos.v[0] >= 0 && pos.v[2] <= 0) r.push_back(1);
|
||||
if (pos.v[0] <= 0 && pos.v[2] <= 0) r.push_back(2);
|
||||
if (pos.v[0] <= 0 && pos.v[2] >= 0) r.push_back(3);
|
||||
}
|
||||
if (pos.v[1] <= 0) {
|
||||
if (pos.v[0] >= 0 && pos.v[2] >= 0) r.push_back(4);
|
||||
if (pos.v[0] <= 0 && pos.v[2] >= 0) r.push_back(5);
|
||||
if (pos.v[0] <= 0 && pos.v[2] <= 0) r.push_back(6);
|
||||
if (pos.v[0] >= 0 && pos.v[2] <= 0) r.push_back(7);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Ðåêóðñèâíûé øàã
|
||||
std::vector<int> r0 = recursiveTriangleSearch(lod - 1, pos, meshes);
|
||||
for (int tri0 : r0) {
|
||||
Vector3f a = meshes[lod - 1].vertexData.PositionData[tri0 * 3];
|
||||
Vector3f b = meshes[lod - 1].vertexData.PositionData[tri0 * 3 + 1];
|
||||
Vector3f c = meshes[lod - 1].vertexData.PositionData[tri0 * 3 + 2];
|
||||
|
||||
std::vector<int> result = PlanetData::find_sub_triangle_spherical(a, b, c, pos);
|
||||
for (int trix : result) {
|
||||
r.push_back(tri0 * 4 + trix);
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
std::vector<int> PlanetData::getTrianglesUnderCamera(const Vector3f& viewerPosition) {
|
||||
// Âûçûâàåì ðåêóðñèþ ñ òåêóùèì LOD
|
||||
return recursiveTriangleSearch(currentLod, viewerPosition, planetMeshLods);
|
||||
}
|
||||
|
||||
// --- Îñòàëüíûå ìåòîäû (check_spherical_side, generateSphere è ò.ä.) ---
|
||||
// (Êîïèðóéòå ðåàëèçàöèþ èç ñòàðîãî PlanetObject.cpp,
|
||||
// çàìåíÿÿ îáðàùåíèÿ ê Environment::shipPosition íà àðãóìåíòû, åñëè íóæíî,
|
||||
// è èñïîëüçóÿ this->planetMeshLods)
|
||||
|
||||
float PlanetData::check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon) {
|
||||
Vector3f N_plane = V1.cross(V2);
|
||||
float sign_P = P.dot(N_plane);
|
||||
float sign_V3 = V3.dot(N_plane);
|
||||
return sign_P * sign_V3;
|
||||
}
|
||||
|
||||
bool PlanetData::is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon) {
|
||||
if (check_spherical_side(V1, V2, P, V3, epsilon) < -epsilon) return false;
|
||||
if (check_spherical_side(V2, V3, P, V1, epsilon) < -epsilon) return false;
|
||||
if (check_spherical_side(V3, V1, P, V2, epsilon) < -epsilon) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<int> PlanetData::find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig) {
|
||||
const float EPSILON = 1e-6f;
|
||||
std::vector<int> result;
|
||||
const Vector3f a = a_orig.normalized();
|
||||
const Vector3f b = b_orig.normalized();
|
||||
const Vector3f c = c_orig.normalized();
|
||||
const Vector3f pxx_normalized = px_orig.normalized();
|
||||
const Vector3f m_ab = ((a + b) * 0.5f).normalized();
|
||||
const Vector3f m_bc = ((b + c) * 0.5f).normalized();
|
||||
const Vector3f m_ac = ((a + c) * 0.5f).normalized();
|
||||
|
||||
if (is_inside_spherical_triangle(pxx_normalized, a, m_ab, m_ac, EPSILON)) result.push_back(0);
|
||||
if (is_inside_spherical_triangle(pxx_normalized, m_ab, b, m_bc, EPSILON)) result.push_back(1);
|
||||
if (is_inside_spherical_triangle(pxx_normalized, m_ac, m_bc, c, EPSILON)) result.push_back(2);
|
||||
if (is_inside_spherical_triangle(pxx_normalized, m_ab, m_bc, m_ac, EPSILON)) result.push_back(3);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Triangle> PlanetData::subdivideTriangles(const std::vector<Triangle>& input, float noiseCoeff) {
|
||||
std::vector<Triangle> output;
|
||||
|
||||
for (const auto& t : input) {
|
||||
// Âåðøèíû è èõ ID
|
||||
const Vector3f& a = t.data[0];
|
||||
const Vector3f& b = t.data[1];
|
||||
const Vector3f& c = t.data[2];
|
||||
const VertexID& id_a = t.ids[0];
|
||||
const VertexID& id_b = t.ids[1];
|
||||
const VertexID& id_c = t.ids[2];
|
||||
|
||||
// 1. Âû÷èñëÿåì ñåðåäèíû (êîîðäèíàòû)
|
||||
Vector3f m_ab = ((a + b) * 0.5f).normalized();
|
||||
Vector3f m_bc = ((b + c) * 0.5f).normalized();
|
||||
Vector3f m_ac = ((a + c) * 0.5f).normalized();
|
||||
|
||||
Vector3f pm_ab = m_ab * perlin.getSurfaceHeight(m_ab, noiseCoeff);
|
||||
Vector3f pm_bc = m_bc * perlin.getSurfaceHeight(m_bc, noiseCoeff);
|
||||
Vector3f pm_ac = m_ac * perlin.getSurfaceHeight(m_ac, noiseCoeff);
|
||||
|
||||
// 2. Âû÷èñëÿåì ID íîâûõ âåðøèí
|
||||
VertexID id_mab = generateEdgeID(id_a, id_b);
|
||||
VertexID id_mbc = generateEdgeID(id_b, id_c);
|
||||
VertexID id_mac = generateEdgeID(id_a, id_c);
|
||||
|
||||
// 3. Ôîðìèðóåì 4 íîâûõ òðåóãîëüíèêà
|
||||
output.emplace_back(Triangle{ {a, pm_ab, pm_ac}, {id_a, id_mab, id_mac} }); // 0
|
||||
output.emplace_back(Triangle{ {pm_ab, b, pm_bc}, {id_mab, id_b, id_mbc} }); // 1
|
||||
output.emplace_back(Triangle{ {pm_ac, pm_bc, c}, {id_mac, id_mbc, id_c} }); // 2
|
||||
output.emplace_back(Triangle{ {pm_ab, pm_bc, pm_ac}, {id_mab, id_mbc, id_mac} }); // 3
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
LodLevel PlanetData::trianglesToVertices(const std::vector<Triangle>& geometry) {
|
||||
LodLevel result;
|
||||
result.triangles = geometry;
|
||||
|
||||
size_t vertexCount = geometry.size() * 3;
|
||||
result.vertexData.PositionData.reserve(vertexCount);
|
||||
result.vertexData.NormalData.reserve(vertexCount);
|
||||
result.vertexData.TexCoordData.reserve(vertexCount);
|
||||
result.vertexData.TangentData.reserve(vertexCount); // Äîáàâëÿåì ðåçåðâ
|
||||
result.vertexData.BinormalData.reserve(vertexCount);
|
||||
result.VertexIDs.reserve(vertexCount);
|
||||
|
||||
const std::array<Vector2f, 3> triangleUVs = {
|
||||
Vector2f(0.5f, 1.0f),
|
||||
Vector2f(0.0f, 0.0f),
|
||||
Vector2f(1.0f, 0.0f)
|
||||
};
|
||||
|
||||
for (const auto& t : geometry) {
|
||||
// --- Âû÷èñëÿåì ëîêàëüíûé áàçèñ òðåóãîëüíèêà (êàê â GetRotationForTriangle) ---
|
||||
Vector3f vA = t.data[0];
|
||||
Vector3f vB = t.data[1];
|
||||
Vector3f vC = t.data[2];
|
||||
|
||||
Vector3f x_axis = (vC - vB).normalized(); // Íàïðàâëåíèå U
|
||||
|
||||
Vector3f edge1 = vB - vA;
|
||||
Vector3f edge2 = vC - vA;
|
||||
Vector3f z_axis = edge1.cross(edge2).normalized(); // Íîðìàëü ïëîñêîñòè
|
||||
|
||||
// Ïðîâåðêà íàïðàâëåíèÿ íîðìàëè íàðóæó (îò öåíòðà ïëàíåòû)
|
||||
Vector3f centerToTri = (vA + vB + vC).normalized();
|
||||
if (z_axis.dot(centerToTri) < 0) {
|
||||
z_axis = z_axis * -1.0f;
|
||||
}
|
||||
|
||||
Vector3f y_axis = z_axis.cross(x_axis).normalized(); // Íàïðàâëåíèå V
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
result.vertexData.PositionData.push_back(t.data[i]);
|
||||
result.vertexData.NormalData.push_back(z_axis); // Ïëîñêàÿ íîðìàëü ãðàíè äëÿ Parallax
|
||||
result.vertexData.TexCoordData.push_back(triangleUVs[i]);
|
||||
|
||||
// Çàïèñûâàåì âû÷èñëåííûé áàçèñ â êàæäóþ âåðøèíó òðåóãîëüíèêà
|
||||
result.vertexData.TangentData.push_back(x_axis);
|
||||
result.vertexData.BinormalData.push_back(y_axis);
|
||||
|
||||
result.VertexIDs.push_back(t.ids[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
LodLevel PlanetData::generateSphere(int subdivisions, float noiseCoeff) {
|
||||
// 1. Èñõîäíûé îêòàýäð è ïðèñâîåíèå ID
|
||||
std::vector<Triangle> geometry = {
|
||||
// Âåðõíÿÿ ïîëóñôåðà (Y > 0)
|
||||
{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 0
|
||||
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}}, // 1
|
||||
{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 2
|
||||
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // 3
|
||||
// Íèæíÿÿ ïîëóñôåðà (Y < 0)
|
||||
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // 4
|
||||
{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 5
|
||||
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // 6
|
||||
{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}} // 7
|
||||
};
|
||||
|
||||
// Ïðèñâîåíèå ID èñõîäíûì âåðøèíàì
|
||||
for (auto& t : geometry) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Èñïîëüçóåì map äëÿ ïîëó÷åíèÿ ID ïî ÷èñòûì êîîðäèíàòàì (íîðì. == ÷èñòûå)
|
||||
t.ids[i] = initialVertexMap[t.data[i].normalized()];
|
||||
}
|
||||
}
|
||||
// 3. Ðàçáèâàåì N ðàç (â subdivideTriangles ãåíåðèðóþòñÿ ID íîâûõ âåðøèí)
|
||||
for (int i = 0; i < subdivisions; i++) {
|
||||
geometry = subdivideTriangles(geometry, noiseCoeff);
|
||||
}
|
||||
|
||||
// 4. Ãåíåðèðóåì PositionData, NormalData è VertexIDs
|
||||
LodLevel lodLevel = trianglesToVertices(geometry);
|
||||
|
||||
// 5. Ñîçäàíèå V2T-Map íà îñíîâå VertexIDs (ÒÎÏÎËÎÃÈ×ÅÑÊÈÉ ÊËÞ×)
|
||||
V2TMap v2tMap;
|
||||
const auto& finalVertexIDs = lodLevel.VertexIDs;
|
||||
size_t num_triangles = geometry.size();
|
||||
|
||||
for (size_t i = 0; i < num_triangles; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
VertexID v_id = finalVertexIDs[i * 3 + j];
|
||||
|
||||
// Åñëè êëþ÷ óæå åñòü, ïðîñòî äîáàâëÿåì èíäåêñ
|
||||
v2tMap[v_id].push_back((int)i);
|
||||
}
|
||||
}
|
||||
lodLevel.v2tMap = v2tMap;
|
||||
|
||||
// 6. Ïðèìåíåíèå ôèíàëüíîãî øóìà (åñëè âû õîòåëè, ÷òîáû øóì áûë òîëüêî çäåñü)
|
||||
// Çäåñü ìû äîëæíû áûëè áû ïðèìåíèòü øóì ê buffer.PositionData,
|
||||
// íî â âàøåì èñõîäíîì êîäå øóì ïðèìåíÿëñÿ ðàíåå.
|
||||
// Ïðåäïîëàãàåì, ÷òî øóì áóäåò ïðèìåíåí çäåñü (èíà÷å v2tMap íå áóäåò ñîîòâåòñòâîâàòü).
|
||||
// ÂÎÑÑÒÀÍÎÂÈÌ ØÀÃ 2, íî äëÿ ôèíàëüíîé ãåîìåòðèè:
|
||||
for (size_t i = 0; i < lodLevel.vertexData.PositionData.size(); i++) {
|
||||
Vector3f dir = lodLevel.vertexData.PositionData[i].normalized();
|
||||
lodLevel.vertexData.PositionData[i] = dir * perlin.getSurfaceHeight(dir, noiseCoeff);
|
||||
}
|
||||
|
||||
|
||||
// 7. Ãåíåðàöèÿ ColorData
|
||||
lodLevel.vertexData.ColorData.reserve(geometry.size() * 3);
|
||||
const Vector3f baseColor = { 0.498f, 0.416f, 0.0f };
|
||||
const float colorFrequency = 5.0f;
|
||||
const float colorAmplitude = 0.2f;
|
||||
const Vector3f offsetR = { 0.1f, 0.2f, 0.3f };
|
||||
const Vector3f offsetG = { 0.5f, 0.4f, 0.6f };
|
||||
const Vector3f offsetB = { 0.9f, 0.8f, 0.7f };
|
||||
|
||||
|
||||
for (size_t i = 0; i < geometry.size(); i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
// Èñïîëüçóåì íîðìàëèçîâàííûé âåêòîð èç PositionData (êîòîðûé ðàâåí NormalData)
|
||||
Vector3f dir = lodLevel.vertexData.NormalData[i * 3 + j];
|
||||
|
||||
// Âû÷èñëåíèå öâåòîâîãî øóìà
|
||||
float noiseR = colorPerlin.noise(
|
||||
(dir.v[0] + offsetR.v[0]) * colorFrequency,
|
||||
(dir.v[1] + offsetR.v[1]) * colorFrequency,
|
||||
(dir.v[2] + offsetR.v[2]) * colorFrequency
|
||||
);
|
||||
// ... (àíàëîãè÷íî äëÿ noiseG è noiseB)
|
||||
|
||||
// Çäåñü ìû èñïîëüçóåì çàãëóøêè, òàê êàê íåò ïîëíîãî îïðåäåëåíèÿ PerlinNoise
|
||||
float noiseG = 0.0f;
|
||||
float noiseB = 0.0f;
|
||||
|
||||
Vector3f colorOffset = {
|
||||
noiseR * colorAmplitude,
|
||||
noiseG * colorAmplitude,
|
||||
noiseB * colorAmplitude
|
||||
};
|
||||
|
||||
Vector3f finalColor = baseColor + colorOffset;
|
||||
|
||||
lodLevel.vertexData.ColorData.push_back(finalColor);
|
||||
}
|
||||
}
|
||||
|
||||
return lodLevel;
|
||||
}
|
||||
|
||||
|
||||
// Ïðèìåð findNeighbors:
|
||||
std::vector<int> PlanetData::findNeighbors(int index, int lod) {
|
||||
const V2TMap& v2tMap = planetMeshLods[lod].v2tMap;
|
||||
const auto& vertexIDs = planetMeshLods[lod].VertexIDs;
|
||||
if ((index * 3 + 2) >= vertexIDs.size()) return {};
|
||||
std::set<int> neighbors;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
VertexID v_id = vertexIDs[index * 3 + i];
|
||||
auto it = v2tMap.find(v_id);
|
||||
if (it != v2tMap.end()) {
|
||||
for (int tri_index : it->second) {
|
||||
if (tri_index != index) {
|
||||
neighbors.insert(tri_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::vector<int>(neighbors.begin(), neighbors.end());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "utils/Perlin.h"
|
||||
#include "render/Renderer.h"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
using VertexID = std::string;
|
||||
using V2TMap = std::map<VertexID, std::vector<int>>;
|
||||
|
||||
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2);
|
||||
|
||||
constexpr static int MAX_LOD_LEVELS = 6;
|
||||
//constexpr static int MAX_LOD_LEVELS = 3;
|
||||
|
||||
struct Triangle
|
||||
{
|
||||
std::array<Vector3f, 3> data;
|
||||
std::array<VertexID, 3> ids;
|
||||
|
||||
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
|
||||
: data{ p1, p2, p3 }
|
||||
{
|
||||
}
|
||||
|
||||
Triangle(std::array<Vector3f, 3> idata, std::array<VertexID, 3> iids)
|
||||
: data{ idata }
|
||||
, ids{ iids }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct LodLevel
|
||||
{
|
||||
std::vector<Triangle> triangles;
|
||||
VertexDataStruct vertexData;
|
||||
std::vector<VertexID> VertexIDs;
|
||||
V2TMap v2tMap;
|
||||
|
||||
void Scale(float s)
|
||||
{
|
||||
vertexData.Scale(s);
|
||||
for (auto& t : triangles) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t.data[i] = t.data[i] * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Move(Vector3f pos)
|
||||
{
|
||||
vertexData.Move(pos);
|
||||
for (auto& t : triangles) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t.data[i] = t.data[i] + pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PlanetData {
|
||||
public:
|
||||
static const float PLANET_RADIUS;
|
||||
static const Vector3f PLANET_CENTER_OFFSET;
|
||||
|
||||
private:
|
||||
PerlinNoise perlin;
|
||||
PerlinNoise colorPerlin;
|
||||
|
||||
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLods;
|
||||
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLodsNoDist;
|
||||
LodLevel planetAtmosphereLod;
|
||||
|
||||
int currentLod; // Ëîãè÷åñêèé òåêóùèé óðîâåíü äåòàëèçàöèè
|
||||
|
||||
std::map<Vector3f, VertexID> initialVertexMap;
|
||||
|
||||
// Âíóòðåííèå ìåòîäû ãåíåðàöèè
|
||||
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles, float noiseCoeff);
|
||||
LodLevel trianglesToVertices(const std::vector<Triangle>& triangles);
|
||||
LodLevel generateSphere(int subdivisions, float noiseCoeff);
|
||||
|
||||
// Âñïîìîãàòåëüíûå ìåòîäû ìàòåìàòèêè
|
||||
static float check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon = 1e-6f);
|
||||
static bool is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon);
|
||||
|
||||
public:
|
||||
PlanetData();
|
||||
|
||||
void init();
|
||||
|
||||
// Ìåòîäû äîñòóïà ê äàííûì (äëÿ ðåíäåðåðà)
|
||||
const LodLevel& getLodLevel(int level) const;
|
||||
const LodLevel& getLodLevelNoDist(int level) const;
|
||||
const LodLevel& getAtmosphereLod() const;
|
||||
int getCurrentLodIndex() const;
|
||||
int getMaxLodIndex() const;
|
||||
|
||||
// Ëîãèêà
|
||||
std::pair<float, float> calculateZRange(float distanceToSurface);
|
||||
float distanceToPlanetSurface(const Vector3f& viewerPosition);
|
||||
|
||||
// Âîçâðàùàåò èíäåêñû òðåóãîëüíèêîâ, âèäèìûõ êàìåðîé
|
||||
std::vector<int> getTrianglesUnderCamera(const Vector3f& viewerPosition);
|
||||
|
||||
std::vector<int> findNeighbors(int index, int lod);
|
||||
|
||||
static std::vector<int> find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig);
|
||||
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,570 @@
|
||||
#include "PlanetObject.h"
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include "Environment.h"
|
||||
#include "StoneObject.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
|
||||
Matrix3f GetRotationForTriangle(const Triangle& tri) {
|
||||
// Для треугольника №0:
|
||||
// tri.data[0] = {0, 20000, 0} (A)
|
||||
// tri.data[1] = {0, 0, 20000} (B)
|
||||
// tri.data[2] = {20000, 0, 0} (C)
|
||||
|
||||
Vector3f vA = tri.data[0];
|
||||
Vector3f vB = tri.data[1];
|
||||
Vector3f vC = tri.data[2];
|
||||
|
||||
// 1. Вычисляем ось X (горизонталь).
|
||||
// Нам нужна грань BC: от (0, 0, 20000) до (20000, 0, 0)
|
||||
Vector3f x_axis = (vC - vB).normalized();
|
||||
|
||||
|
||||
// 2. Вычисляем нормаль (ось Z).
|
||||
// Порядок cross product (AB x AC) определит "лицевую" сторону.
|
||||
Vector3f edge1 = vB - vA;
|
||||
Vector3f edge2 = vC - vA;
|
||||
Vector3f z_axis = edge1.cross(edge2).normalized();
|
||||
|
||||
// 3. Вычисляем ось Y (вертикаль).
|
||||
// В ортонормированном базисе Y всегда перпендикулярна Z и X.
|
||||
Vector3f y_axis = z_axis.cross(x_axis).normalized();
|
||||
|
||||
// 4. Формируем прямую матрицу поворота (Rotation/World Matrix).
|
||||
Matrix3f rot;
|
||||
|
||||
// Столбец 0: Ось X
|
||||
rot.m[0] = x_axis.v[0];
|
||||
rot.m[3] = x_axis.v[1];
|
||||
rot.m[6] = x_axis.v[2];
|
||||
|
||||
// Столбец 1: Ось Y
|
||||
rot.m[1] = y_axis.v[0];
|
||||
rot.m[4] = y_axis.v[1];
|
||||
rot.m[7] = y_axis.v[2];
|
||||
|
||||
// Столбец 2: Ось Z
|
||||
rot.m[2] = z_axis.v[0];
|
||||
rot.m[5] = z_axis.v[1];
|
||||
rot.m[8] = z_axis.v[2];
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
PlanetObject::PlanetObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::init() {
|
||||
// 1. Инициализируем данные (генерация мешей)
|
||||
planetData.init();
|
||||
|
||||
// 2. Забираем данные для VBO
|
||||
// Берем максимальный LOD для начальной отрисовки
|
||||
int lodIndex = planetData.getMaxLodIndex();
|
||||
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
|
||||
planetRenderStruct.RefreshVBO();
|
||||
|
||||
planetRenderStructCut.data = planetData.getLodLevelNoDist(lodIndex).vertexData;
|
||||
|
||||
planetRenderStructCut.data.PositionData.resize(3);
|
||||
|
||||
planetRenderStructCut.RefreshVBO();
|
||||
//sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand2.png", ""));
|
||||
sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand2.png", ""));
|
||||
stoneTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
|
||||
|
||||
// Атмосфера
|
||||
planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData;
|
||||
planetAtmosphereRenderStruct.RefreshVBO();
|
||||
|
||||
|
||||
planetStones = CreateStoneGroupData(778, planetData.getLodLevel(lodIndex));
|
||||
planetStones.inflate({ 0/*,1,2,3,4,5,6,7*/ });
|
||||
planetStonesToBakeRenderStruct.AssignFrom(planetStones.mesh);
|
||||
planetStonesToBakeRenderStruct.RefreshVBO();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::prepareDrawData() {
|
||||
if (!drawDataDirty) return;
|
||||
drawDataDirty = false;
|
||||
}
|
||||
|
||||
|
||||
void PlanetObject::update(float deltaTimeMs) {
|
||||
// 1. Получаем базовые треугольники под камерой
|
||||
auto lr = planetData.getTrianglesUnderCamera(Environment::shipPosition);
|
||||
int currentLod = planetData.getCurrentLodIndex();
|
||||
|
||||
// Временный вектор для сбора новых индексов
|
||||
std::vector<int> newIndices;
|
||||
std::set<int> used;
|
||||
|
||||
// Рекурсивно (или итеративно, как у тебя) собираем индексы видимых зон
|
||||
for (int i : lr) {
|
||||
if (used.insert(i).second) newIndices.push_back(i);
|
||||
|
||||
auto neighbors = planetData.findNeighbors(i, currentLod);
|
||||
for (int n : neighbors) {
|
||||
if (used.insert(n).second) newIndices.push_back(n);
|
||||
|
||||
auto neighbors2 = planetData.findNeighbors(n, currentLod);
|
||||
for (int n2 : neighbors2) {
|
||||
if (used.insert(n2).second) newIndices.push_back(n2);
|
||||
|
||||
auto neighbors3 = planetData.findNeighbors(n, currentLod);
|
||||
|
||||
for (int n3 : neighbors3) {
|
||||
if (used.insert(n3).second) newIndices.push_back(n3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Сортируем новый список, чтобы порядок не влиял на сравнение
|
||||
std::sort(newIndices.begin(), newIndices.end());
|
||||
|
||||
// 3. Сравниваем с тем, что было нарисовано в прошлый раз
|
||||
if (newIndices != triangleIndicesToDraw) {
|
||||
// Обновляем список индексов (используем move для эффективности)
|
||||
triangleIndicesToDraw = std::move(newIndices);
|
||||
|
||||
// --- ОБНОВЛЯЕМ ЖЕЛТУЮ ЗОНУ (только когда изменился состав треугольников) ---
|
||||
const auto& fullMesh = planetData.getLodLevel(currentLod).vertexData;
|
||||
planetRenderYellowStruct.data.PositionData.clear();
|
||||
planetRenderYellowStruct.data.TexCoordData.clear();
|
||||
|
||||
for (int i : triangleIndicesToDraw) {
|
||||
// Копируем геометрию для подсветки
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
planetRenderYellowStruct.data.PositionData.push_back(fullMesh.PositionData[i * 3 + j]);
|
||||
planetRenderYellowStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[i * 3 + j]);
|
||||
}
|
||||
}
|
||||
if (planetRenderYellowStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
planetRenderYellowStruct.RefreshVBO();
|
||||
}
|
||||
|
||||
// --- ОБНОВЛЯЕМ КАМНИ (через новую структуру StoneGroup) ---
|
||||
if (triangleIndicesToDraw.size() > 0)
|
||||
{
|
||||
planetStones.inflate(triangleIndicesToDraw);
|
||||
// Используем AssignFrom, он внутри сам вызывает RefreshVBO
|
||||
planetStonesRenderStruct.AssignFrom(planetStones.mesh);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PlanetObject::bakeStoneTexture(Renderer& renderer) {
|
||||
glViewport(0, 0, 512, 512);
|
||||
glClearColor(224 / 255.f, 201 / 255.f, 167 / 255.f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
static const std::string defaultShaderName2 = "planetBake";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
|
||||
|
||||
Triangle tr = planetData.getLodLevelNoDist(planetData.getCurrentLodIndex()).triangles[0];
|
||||
|
||||
// 1. Получаем матрицу вращения (оси в столбцах)
|
||||
Matrix3f mr = GetRotationForTriangle(tr);
|
||||
|
||||
// 2. Трансформируем вершины в локальное пространство экрана, чтобы найти габариты
|
||||
// Используем MultMatrixVector(Matrix, Vector).
|
||||
// Если ваша функция считает V * M, то передайте Inverse(mr).
|
||||
Vector3f rA = MultMatrixVector(mr, tr.data[0]);
|
||||
Vector3f rB = MultMatrixVector(mr, tr.data[1]);
|
||||
Vector3f rC = MultMatrixVector(mr, tr.data[2]);
|
||||
|
||||
// 3. Вычисляем реальные границы треугольника после поворота
|
||||
float minX = min(rA.v[0], min(rB.v[0], rC.v[0]));
|
||||
float maxX = max(rA.v[0], max(rB.v[0], rC.v[0]));
|
||||
float minY = min(rA.v[1], min(rB.v[1], rC.v[1]));
|
||||
float maxY = max(rA.v[1], max(rB.v[1], rC.v[1]));
|
||||
|
||||
|
||||
// Находим центр и размеры
|
||||
float width = maxX - minX;
|
||||
float height = maxY - minY;
|
||||
float centerX = (minX + maxX) * 0.5f;
|
||||
float centerY = (minY + maxY) * 0.5f;
|
||||
|
||||
width = width * 0.995;
|
||||
height = height * 0.995;
|
||||
|
||||
renderer.PushProjectionMatrix(
|
||||
centerX - width*0.5, centerX + width * 0.5,
|
||||
centerY - height * 0.5, centerY + height * 0.5,
|
||||
150, 200000);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
// Сдвигаем камеру по Z
|
||||
renderer.TranslateMatrix(Vector3f{ 0, 0, -45000 });
|
||||
|
||||
// Применяем вращение
|
||||
renderer.RotateMatrix(mr);
|
||||
|
||||
|
||||
// Извлекаем нормаль треугольника (это 3-й столбец нашей матрицы вращения)
|
||||
Vector3f planeNormal = { mr.m[2], mr.m[5], mr.m[8] };
|
||||
|
||||
renderer.RenderUniform3fv("uPlanePoint", &tr.data[0].v[0]);
|
||||
renderer.RenderUniform3fv("uPlaneNormal", &planeNormal.v[0]);
|
||||
renderer.RenderUniform1f("uMaxHeight", StoneParams::BASE_SCALE * 1.1f); // Соответствует BASE_SCALE + perturbation
|
||||
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||
renderer.PushMatrix();
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetRenderStructCut);
|
||||
renderer.PopMatrix();
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK); // Отсекаем задние грани
|
||||
if (planetStonesToBakeRenderStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(planetStonesToBakeRenderStruct);
|
||||
CheckGlError();
|
||||
}
|
||||
glDisable(GL_CULL_FACE); // Не забываем выключить, чтобы не сломать остальной рендер
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
|
||||
void PlanetObject::draw(Renderer& renderer) {
|
||||
|
||||
prepareDrawData();
|
||||
|
||||
{
|
||||
if (stoneMapFB == nullptr)
|
||||
{
|
||||
stoneMapFB = std::make_unique<FrameBuffer>(512, 512);
|
||||
}
|
||||
stoneMapFB->Bind();
|
||||
|
||||
bakeStoneTexture(renderer);
|
||||
|
||||
stoneMapFB->Unbind();
|
||||
|
||||
}
|
||||
|
||||
//bakeStoneTexture(renderer);
|
||||
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
//--------------------------
|
||||
|
||||
|
||||
drawPlanet(renderer);
|
||||
//drawYellowZone(renderer);
|
||||
drawStones(renderer);
|
||||
|
||||
drawAtmosphere(renderer);
|
||||
}
|
||||
|
||||
void PlanetObject::drawPlanet(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName = "planetLand";
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray("vTangent");
|
||||
renderer.EnableVertexAttribArray("vBinormal");
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
|
||||
renderer.RenderUniform1i("Texture", 0);
|
||||
renderer.RenderUniform1i("BakedTexture", 1);
|
||||
|
||||
|
||||
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0]; // Берем базовый треугольник
|
||||
Matrix3f mr = GetRotationForTriangle(tr); // Та же матрица, что и при запекании
|
||||
|
||||
// Позиция камеры (корабля) в мире
|
||||
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
|
||||
|
||||
//renderer.RenderUniform1f("uHeightScale", 0.03f);
|
||||
renderer.RenderUniform1f("uHeightScale", 0.0f);
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetRenderStruct);
|
||||
CheckGlError();
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray("vTangent");
|
||||
renderer.DisableVertexAttribArray("vBinormal");
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::drawStones(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName2 = "planetStone";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
if (planetStonesRenderStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(planetStonesRenderStruct);
|
||||
CheckGlError();
|
||||
}
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void PlanetObject::drawYellowZone(Renderer& renderer)
|
||||
{
|
||||
|
||||
static const std::string defaultShaderName = "planetLand";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
if (planetRenderYellowStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray("vTangent");
|
||||
renderer.EnableVertexAttribArray("vBinormal");
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
|
||||
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
|
||||
|
||||
Vector3f color2 = { 1.0, 1.0, 0.0 };
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||
|
||||
renderer.RenderUniform3fv("uColor", &color2.v[0]);
|
||||
renderer.DrawVertexRenderStruct(planetRenderYellowStruct);
|
||||
//glDisable(GL_BLEND);
|
||||
CheckGlError();
|
||||
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray("vTangent");
|
||||
renderer.DisableVertexAttribArray("vBinormal");
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void PlanetObject::drawAtmosphere(Renderer& renderer) {
|
||||
static const std::string defaultShaderName = "defaultAtmosphere";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
|
||||
Vector3f color = { 0.0, 0.5, 1.0 };
|
||||
|
||||
renderer.RenderUniform3fv("uColor", &color.v[0]);
|
||||
|
||||
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
}
|
||||
|
||||
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
|
||||
{
|
||||
return planetData.distanceToPlanetSurface(viewerPosition);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "utils/Perlin.h"
|
||||
#include "PlanetData.h"
|
||||
#include "StoneObject.h"
|
||||
#include "render/FrameBuffer.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class PlanetObject {
|
||||
public:
|
||||
// Агрегация: логика и данные теперь здесь
|
||||
PlanetData planetData;
|
||||
|
||||
// Данные только для рендеринга (OpenGL specific)
|
||||
VertexRenderStruct planetRenderStruct;
|
||||
VertexRenderStruct planetRenderStructCut;
|
||||
VertexRenderStruct planetRenderYellowStruct;
|
||||
VertexRenderStruct planetAtmosphereRenderStruct;
|
||||
VertexRenderStruct planetStonesRenderStruct;
|
||||
VertexRenderStruct planetStonesToBakeRenderStruct;
|
||||
StoneGroup planetStones;
|
||||
|
||||
std::vector<int> triangleIndicesToDraw;
|
||||
|
||||
std::shared_ptr<Texture> sandTexture;
|
||||
std::shared_ptr<Texture> stoneTexture;
|
||||
|
||||
bool drawDataDirty = true;
|
||||
void prepareDrawData();
|
||||
|
||||
std::unique_ptr<FrameBuffer> stoneMapFB;
|
||||
|
||||
public:
|
||||
PlanetObject();
|
||||
|
||||
void init();
|
||||
void update(float deltaTimeMs);
|
||||
void bakeStoneTexture(Renderer& renderer);
|
||||
void draw(Renderer& renderer);
|
||||
void drawStones(Renderer& renderer);
|
||||
void drawPlanet(Renderer& renderer);
|
||||
void drawYellowZone(Renderer& renderer);
|
||||
void drawAtmosphere(Renderer& renderer);
|
||||
|
||||
|
||||
float distanceToPlanetSurface(const Vector3f& viewerPosition);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "StoneObject.h"
|
||||
|
||||
#include "utils/Utils.h"
|
||||
#include <GL/gl.h>
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include "render/Renderer.h"
|
||||
#include "PlanetData.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
|
||||
// --- ÊÎÍÑÒÀÍÒÛ ÏÀÐÀÌÅÒÐÎÂ (êàê âû ïðîñèëè) ---
|
||||
const float StoneParams::BASE_SCALE = 17.0f; // Îáùèé ðàçìåð êàìíÿ
|
||||
//const float StoneParams::BASE_SCALE = 5000.0f; // Îáùèé ðàçìåð êàìíÿ
|
||||
//const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
//const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
//const float StoneParams::MIN_PERTURBATION = 0.0f; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
//const float StoneParams::MAX_PERTURBATION = 0.0f; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
const float StoneParams::MIN_AXIS_SCALE = 0.5f; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
const float StoneParams::MAX_AXIS_SCALE = 1.5f; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
const float StoneParams::MIN_PERTURBATION = 0.05f; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
const float StoneParams::MAX_PERTURBATION = 0.25f; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
const int StoneParams::STONES_PER_TRIANGLE = 100;
|
||||
|
||||
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ïîëó÷åíèÿ ñëó÷àéíîãî ÷èñëà â äèàïàçîíå [min, max]
|
||||
float getRandomFloat(std::mt19937& gen, float min, float max) {
|
||||
std::uniform_real_distribution<> distrib(min, max);
|
||||
return static_cast<float>(distrib(gen));
|
||||
}
|
||||
|
||||
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ãåíåðàöèè ñëó÷àéíîé òî÷êè íà òðåóãîëüíèêå
|
||||
// Èñïîëüçóåò áàðèöåíòðè÷åñêèå êîîðäèíàòû
|
||||
Vector3f GetRandomPointOnTriangle(const Triangle& t, std::mt19937& engine) {
|
||||
std::uniform_real_distribution<> distrib(0.0f, 1.0f);
|
||||
|
||||
float r1 = getRandomFloat(engine, 0.0f, 1.0f);
|
||||
float r2 = getRandomFloat(engine, 0.0f, 1.0f);
|
||||
|
||||
// Ïðåîáðàçîâàíèå r1, r2 äëÿ ïîëó÷åíèÿ ðàâíîìåðíîãî ðàñïðåäåëåíèÿ
|
||||
float a = 1.0f - std::sqrt(r1);
|
||||
float b = std::sqrt(r1) * r2;
|
||||
float c = 1.0f - a - b; // c = sqrt(r1) * (1 - r2)
|
||||
|
||||
// Áàðèöåíòðè÷åñêèå êîîðäèíàòû
|
||||
// P = a*p1 + b*p2 + c*p3
|
||||
Vector3f p1_term = t.data[0] * a;
|
||||
Vector3f p2_term = t.data[1] * b;
|
||||
Vector3f p3_term = t.data[2] * c;
|
||||
|
||||
return p1_term + p2_term + p3_term;
|
||||
}
|
||||
|
||||
|
||||
// Èêîñàýäð (íà îñíîâå çîëîòîãî ñå÷åíèÿ phi)
|
||||
// Êîîðäèíàòû ìîãóò áûòü âû÷èñëåíû çàðàíåå äëÿ êîíñòàíòíîãî èêîñàýäðà.
|
||||
// Çäåñü òîëüêî îáúÿâëåíèå, ÷òîáû ïîêàçàòü èäåþ.
|
||||
|
||||
VertexDataStruct CreateBaseConvexPolyhedron(uint64_t seed) {
|
||||
|
||||
// const size_t SUBDIVISION_LEVEL = 1; // Óðîâåíü ïîäðàçäåëåíèÿ (äëÿ áîëåå êðóãëîãî êàìíÿ, ïîêà îïóñòèì)
|
||||
|
||||
std::mt19937 engine(static_cast<unsigned int>(seed));
|
||||
|
||||
// Çîëîòîå ñå÷åíèå
|
||||
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
|
||||
|
||||
// 12 âåðøèí èêîñàýäðà
|
||||
std::vector<Vector3f> initialVertices = {
|
||||
{ -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 },
|
||||
{ 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t },
|
||||
{ t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 }
|
||||
};
|
||||
|
||||
// 20 òðåóãîëüíûõ ãðàíåé (èíäåêñû âåðøèí)
|
||||
std::vector<std::array<int, 3>> faces = {
|
||||
// 5 òðåóãîëüíèêîâ âîêðóã âåðøèíû 0
|
||||
{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
|
||||
// 5 ñìåæíûõ ïîëîñ
|
||||
{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
|
||||
// 5 òðåóãîëüíèêîâ âîêðóã âåðøèíû 3
|
||||
{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
|
||||
// 5 ñìåæíûõ ïîëîñ
|
||||
{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
|
||||
};
|
||||
|
||||
// 1. Íîðìàëèçàöèÿ è Âîçìóùåíèå (Perturbation)
|
||||
for (Vector3f& v : initialVertices) {
|
||||
v = v.normalized() * StoneParams::BASE_SCALE; // Íîðìàëèçàöèÿ ê ñôåðå ðàäèóñà BASE_SCALE
|
||||
|
||||
// Ðàäèàëüíîå âîçìóùåíèå:
|
||||
float perturbation = getRandomFloat(engine, StoneParams::MIN_PERTURBATION, StoneParams::MAX_PERTURBATION);
|
||||
v = v * (1.0f + perturbation);
|
||||
}
|
||||
|
||||
// 2. Òðàíñôîðìàöèÿ (Ìàñøòàáèðîâàíèå è Ïîâîðîò)
|
||||
|
||||
// Ñëó÷àéíûå ìàñøòàáû ïî îñÿì
|
||||
Vector3f scaleFactors = {
|
||||
getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE),
|
||||
getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE),
|
||||
getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE)
|
||||
};
|
||||
|
||||
// Ïðèìåíÿåì ìàñøòàáèðîâàíèå
|
||||
for (Vector3f& v : initialVertices) {
|
||||
v.v[0] *= scaleFactors.v[0];
|
||||
v.v[1] *= scaleFactors.v[1];
|
||||
v.v[2] *= scaleFactors.v[2];
|
||||
}
|
||||
|
||||
// Ñëó÷àéíûé ïîâîðîò (íàïðèìåð, âîêðóã òðåõ îñåé)
|
||||
Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
|
||||
Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
|
||||
Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
|
||||
Vector4f qFinal = slerp(qx, qy, 0.5f); // Ïðîñòîé ïðèìåð êîìáèíèðîâàíèÿ
|
||||
qFinal = slerp(qFinal, qz, 0.5f).normalized();
|
||||
Matrix3f rotationMatrix = QuatToMatrix(qFinal);
|
||||
|
||||
for (Vector3f& v : initialVertices) {
|
||||
v = MultMatrixVector(rotationMatrix, v);
|
||||
}
|
||||
|
||||
// 3. Ñãëàæåííûå Íîðìàëè è Ôîðìèðîâàíèå Mesh
|
||||
VertexDataStruct result;
|
||||
// Êàðòà äëÿ íàêîïëåíèÿ íîðìàëåé ïî óíèêàëüíûì ïîçèöèÿì âåðøèí
|
||||
// (Òðåáóåò îïðåäåëåííîãî îïåðàòîðà < äëÿ Vector3f â ZLMath.h, êîòîðûé ó âàñ åñòü)
|
||||
std::map<Vector3f, Vector3f> smoothNormalsMap;
|
||||
|
||||
// Ïðåäâàðèòåëüíîå çàïîëíåíèå êàðòû íîðìàëÿìè
|
||||
for (const auto& face : faces) {
|
||||
Vector3f p1 = initialVertices[face[0]];
|
||||
Vector3f p2 = initialVertices[face[1]];
|
||||
Vector3f p3 = initialVertices[face[2]];
|
||||
|
||||
// Íîðìàëü ãðàíè: (p2 - p1) x (p3 - p1)
|
||||
Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized();
|
||||
|
||||
smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal;
|
||||
smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal;
|
||||
smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal;
|
||||
}
|
||||
|
||||
// Íîðìàëèçàöèÿ íàêîïëåííûõ íîðìàëåé
|
||||
for (auto& pair : smoothNormalsMap) {
|
||||
pair.second = pair.second.normalized();
|
||||
}
|
||||
|
||||
|
||||
// 4. Ôèíàëüíîå çàïîëíåíèå VertexDataStruct è Òåêñòóðíûå Êîîðäèíàòû
|
||||
for (const auto& face : faces) {
|
||||
Vector3f p1 = initialVertices[face[0]];
|
||||
Vector3f p2 = initialVertices[face[1]];
|
||||
Vector3f p3 = initialVertices[face[2]];
|
||||
|
||||
// Ïîçèöèè
|
||||
result.PositionData.push_back(p1);
|
||||
result.PositionData.push_back(p2);
|
||||
result.PositionData.push_back(p3);
|
||||
|
||||
// Ñãëàæåííûå Íîðìàëè (èç êàðòû)
|
||||
result.NormalData.push_back(smoothNormalsMap[p1]);
|
||||
result.NormalData.push_back(smoothNormalsMap[p2]);
|
||||
result.NormalData.push_back(smoothNormalsMap[p3]);
|
||||
|
||||
// Òåêñòóðíûå Êîîðäèíàòû (Ïëàíàðíàÿ ïðîåêöèÿ íà ïëîñêîñòü ãðàíè)
|
||||
// p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle))
|
||||
// Ãäå L_xy - äëèíà âåêòîðà, angle - óãîë ìåæäó p2-p1 è p3-p1
|
||||
|
||||
Vector3f uAxis = (p2 - p1).normalized();
|
||||
Vector3f vRaw = p3 - p1;
|
||||
|
||||
// Ïðîåêöèÿ vRaw íà uAxis
|
||||
float uProjLen = vRaw.dot(uAxis);
|
||||
|
||||
// Âåêòîð V ïåðïåíäèêóëÿðíûé U: vRaw - uProj
|
||||
Vector3f vAxisRaw = vRaw - (uAxis * uProjLen);
|
||||
|
||||
// Äëèíà îñè V
|
||||
float vLen = vAxisRaw.length();
|
||||
|
||||
// Íîðìàëèçîâàííàÿ îñü V
|
||||
Vector3f vAxis = vAxisRaw.normalized();
|
||||
|
||||
// Êîîðäèíàòû (u, v) äëÿ p1, p2, p3 îòíîñèòåëüíî p1
|
||||
Vector2f uv1 = { 0.0f, 0.0f };
|
||||
Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 âäîëü îñè U
|
||||
Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u-êîìïîíåíòà = uProjLen, v-êîìïîíåíòà = vLen
|
||||
|
||||
// Íàõîäèì ìàêñèìàëüíûé ðàçìåð, ÷òîáû ìàñøòàáèðîâàòü â [0, 1]
|
||||
float maxUV = max(uv2.v[0], max(uv3.v[0], uv3.v[1]));
|
||||
|
||||
if (maxUV > 0.000001f) {
|
||||
// Ìàñøòàáèðóåì:
|
||||
result.TexCoordData.push_back(uv1);
|
||||
result.TexCoordData.push_back(uv2 * (1.f / maxUV));
|
||||
result.TexCoordData.push_back(uv3 * (1.f / maxUV));
|
||||
}
|
||||
else {
|
||||
// Ïðåäîòâðàùåíèå äåëåíèÿ íà íîëü äëÿ âûðîæäåííûõ ãðàíåé
|
||||
result.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
result.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
result.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel) {
|
||||
StoneGroup group;
|
||||
|
||||
// Ðåçåðâèðóåì ìåñòî ïîä âñå òðåóãîëüíèêè òåêóùåãî LOD
|
||||
group.allInstances.resize(planetLodLevel.triangles.size());
|
||||
|
||||
for (size_t tIdx = 0; tIdx < planetLodLevel.triangles.size(); ++tIdx) {
|
||||
const Triangle& tri = planetLodLevel.triangles[tIdx];
|
||||
std::mt19937 engine(static_cast<unsigned int>(globalSeed));
|
||||
|
||||
for (int i = 0; i < StoneParams::STONES_PER_TRIANGLE; ++i) {
|
||||
StoneInstance instance;
|
||||
instance.seed = globalSeed;// + tIdx * 1000 + i; // Óíèêàëüíûé ñèä äëÿ êàæäîãî êàìíÿ
|
||||
|
||||
instance.position = GetRandomPointOnTriangle(tri, engine);
|
||||
|
||||
//instance.position = Vector3f(5000.0f, 5000.0f, 5000.0f);
|
||||
// Ãåíåðèðóåì ñëó÷àéíûå ïàðàìåòðû îäèí ðàç
|
||||
instance.scale = {
|
||||
getRandomFloat(engine, 0.5f, 1.5f),
|
||||
getRandomFloat(engine, 0.5f, 1.5f),
|
||||
getRandomFloat(engine, 0.5f, 1.5f)
|
||||
};
|
||||
|
||||
Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
|
||||
Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
|
||||
Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
|
||||
instance.rotation = slerp(slerp(qx, qy, 0.5f), qz, 0.5f).normalized();
|
||||
|
||||
group.allInstances[tIdx].push_back(instance);
|
||||
}
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
void StoneGroup::inflate(const std::vector<int>& triangleIndices) {
|
||||
// 1. Î÷èùàåì òåêóùèé ìåø ïåðåä çàïîëíåíèåì
|
||||
mesh.PositionData.clear();
|
||||
mesh.NormalData.clear();
|
||||
mesh.TexCoordData.clear();
|
||||
|
||||
static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);
|
||||
|
||||
// 2. Íàïîëíÿåì ìåø òîëüêî äëÿ âèäèìûõ òðåóãîëüíèêîâ
|
||||
for (int tIdx : triangleIndices) {
|
||||
if (tIdx >= allInstances.size()) continue;
|
||||
|
||||
for (const auto& inst : allInstances[tIdx]) {
|
||||
Matrix3f rotMat = QuatToMatrix(inst.rotation);
|
||||
|
||||
for (size_t j = 0; j < baseStone.PositionData.size(); ++j) {
|
||||
Vector3f p = baseStone.PositionData[j];
|
||||
Vector3f n = baseStone.NormalData[j];
|
||||
|
||||
// Ìàñøòàá -> Ïîâîðîò -> Ñìåùåíèå
|
||||
p.v[0] *= inst.scale.v[0];
|
||||
p.v[1] *= inst.scale.v[1];
|
||||
p.v[2] *= inst.scale.v[2];
|
||||
|
||||
mesh.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position);
|
||||
mesh.NormalData.push_back(MultMatrixVector(rotMat, n));
|
||||
mesh.TexCoordData.push_back(baseStone.TexCoordData[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "PlanetData.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct StoneParams
|
||||
{
|
||||
static const float BASE_SCALE; // Îáùèé ðàçìåð êàìíÿ
|
||||
static const float MIN_AXIS_SCALE; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
static const float MAX_AXIS_SCALE; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
static const float MIN_PERTURBATION; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
static const float MAX_PERTURBATION; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
static const int STONES_PER_TRIANGLE;
|
||||
|
||||
};
|
||||
|
||||
struct StoneInstance {
|
||||
uint64_t seed;
|
||||
Vector3f position;
|
||||
Vector3f scale;
|
||||
Vector4f rotation;
|
||||
};
|
||||
|
||||
struct StoneGroup {
|
||||
// mesh.PositionData è ïðî÷èå áóäóò çàïîëíÿòüñÿ â inflate()
|
||||
VertexDataStruct mesh;
|
||||
|
||||
// Âíåøíèé âåêòîð — èíäåêñ òðåóãîëüíèêà ïëàíåòû,
|
||||
// âíóòðåííèé — ñïèñîê êàìíåé íà ýòîì òðåóãîëüíèêå
|
||||
std::vector<std::vector<StoneInstance>> allInstances;
|
||||
|
||||
// Î÷èùàåò ñòàðóþ ãåîìåòðèþ è ãåíåðèðóåò íîâóþ äëÿ óêàçàííûõ èíäåêñîâ
|
||||
void inflate(const std::vector<int>& triangleIndices);
|
||||
};
|
||||
|
||||
// Òåïåðü âîçâðàùàåò çàãîòîâêó ñî âñåìè ïàðàìåòðàìè, íî áåç òÿæåëîãî ìåøà
|
||||
StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel);
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "FrameBuffer.h"
|
||||
#include <iostream>
|
||||
#include "Environment.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
FrameBuffer::FrameBuffer(int w, int h) : width(w), height(h) {
|
||||
// 1. Ñîçäàåì FBO
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
// 2. Ñîçäàåì òåêñòóðó, êóäà áóäåì "ôîòîãðàôèðîâàòü"
|
||||
glGenTextures(1, &textureID);
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
|
||||
// Óñòàíàâëèâàåì ïàðàìåòðû òåêñòóðû
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// 3. Ïðèâÿçûâàåì òåêñòóðó ê FBO
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
|
||||
|
||||
// Ïðîâåðêà ãîòîâíîñòè
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
std::cerr << "Error: Framebuffer is not complete!" << std::endl;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer() {
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
glDeleteTextures(1, &textureID);
|
||||
}
|
||||
|
||||
void FrameBuffer::Bind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glViewport(0, 0, width, height); // Âàæíî: óñòàíàâëèâàåì âüþïîðò ïîä ðàçìåð òåêñòóðû
|
||||
}
|
||||
|
||||
void FrameBuffer::Unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
// Çäåñü æåëàòåëüíî âîçâðàùàòü âüþïîðò ê ðàçìåðàì ýêðàíà,
|
||||
// íàïðèìåð, ÷åðåç Environment::width/height
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "OpenGlExtensions.h"
|
||||
#include <memory>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class FrameBuffer {
|
||||
private:
|
||||
GLuint fbo = 0;
|
||||
GLuint textureID = 0;
|
||||
int width, height;
|
||||
|
||||
public:
|
||||
FrameBuffer(int w, int h);
|
||||
~FrameBuffer();
|
||||
|
||||
// Çàïðåùàåì êîïèðîâàíèå, êàê â VBOHolder
|
||||
FrameBuffer(const FrameBuffer&) = delete;
|
||||
FrameBuffer& operator=(const FrameBuffer&) = delete;
|
||||
|
||||
void Bind(); // Íà÷àòü ðåíäåð â ýòîò áóôåð
|
||||
void Unbind(); // Âåðíóòüñÿ ê îáû÷íîìó ðåíäåðó â ýêðàí
|
||||
|
||||
GLuint getTextureID() const { return textureID; }
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,335 @@
|
||||
#include "OpenGlExtensions.h"
|
||||
|
||||
#include "utils/Utils.h"
|
||||
#include <iostream>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
|
||||
//====================================================
|
||||
//===================== GLSL Shaders =================
|
||||
//====================================================
|
||||
|
||||
//Requires GL_VERSION_2_0
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram = NULL;
|
||||
PFNGLDELETEPROGRAMPROC glDeleteProgram = NULL;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram = NULL;
|
||||
PFNGLVALIDATEPROGRAMPROC glValidateProgram = NULL;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram = NULL;
|
||||
PFNGLGETPROGRAMIVPROC glGetProgramiv = NULL;
|
||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = NULL;
|
||||
PFNGLCREATESHADERPROC glCreateShader = NULL;
|
||||
PFNGLDELETESHADERPROC glDeleteShader = NULL;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource = NULL;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader = NULL;
|
||||
PFNGLATTACHSHADERPROC glAttachShader = NULL;
|
||||
PFNGLDETACHSHADERPROC glDetachShader = NULL;
|
||||
PFNGLGETSHADERIVPROC glGetShaderiv = NULL;
|
||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = NULL;
|
||||
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = NULL;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = NULL;
|
||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = NULL;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL;
|
||||
PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv = NULL;
|
||||
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = NULL;
|
||||
PFNGLUNIFORM1IPROC glUniform1i = NULL;
|
||||
PFNGLUNIFORM1FVPROC glUniform1fv = NULL;
|
||||
PFNGLUNIFORM3FVPROC glUniform2fv = NULL;
|
||||
PFNGLUNIFORM3FVPROC glUniform3fv = NULL;
|
||||
PFNGLUNIFORM4FVPROC glUniform4fv = NULL;
|
||||
PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f = NULL;
|
||||
PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f = NULL;
|
||||
PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f = NULL;
|
||||
PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = NULL;
|
||||
PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv = NULL;
|
||||
PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv = NULL;
|
||||
PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv = NULL;
|
||||
PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib = NULL;
|
||||
PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = NULL;
|
||||
|
||||
|
||||
//=======================================
|
||||
//=========== Multitexture ==============
|
||||
//=======================================
|
||||
|
||||
//Requires GL version 1.3
|
||||
PFNGLACTIVETEXTUREPROC glActiveTexture = NULL;
|
||||
|
||||
//=======================================
|
||||
//========== Vertex buffer ==============
|
||||
//=======================================
|
||||
|
||||
//Requires GL_VERSION_1_5
|
||||
PFNGLGENBUFFERSPROC glGenBuffers = NULL;
|
||||
PFNGLDELETEBUFFERSPROC glDeleteBuffers = NULL;
|
||||
PFNGLBINDBUFFERPROC glBindBuffer = NULL;
|
||||
PFNGLBUFFERDATAPROC glBufferData = NULL;
|
||||
PFNGLBUFFERSUBDATAPROC glBufferSubData = NULL;
|
||||
PFNGLMAPBUFFERPROC glMapBuffer = NULL;
|
||||
PFNGLUNMAPBUFFERPROC glUnmapBuffer = NULL;
|
||||
|
||||
//=========================================
|
||||
//============ Frame buffer ===============
|
||||
//=========================================
|
||||
|
||||
//Requires GL_ARB_framebuffer_object
|
||||
PFNGLISRENDERBUFFERPROC glIsRenderbuffer = NULL;
|
||||
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = NULL;
|
||||
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = NULL;
|
||||
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = NULL;
|
||||
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = NULL;
|
||||
PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv = NULL;
|
||||
PFNGLISFRAMEBUFFERPROC glIsFramebuffer = NULL;
|
||||
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
|
||||
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = NULL;
|
||||
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
|
||||
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = NULL;
|
||||
PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = NULL;
|
||||
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = NULL;
|
||||
PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = NULL;
|
||||
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = NULL;
|
||||
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv = NULL;
|
||||
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
|
||||
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = NULL;
|
||||
PFNGLGENERATEMIPMAPPROC glGenerateMipmap = NULL;
|
||||
PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = NULL;
|
||||
|
||||
|
||||
|
||||
//===========================================
|
||||
//============ Uniform buffer ===============
|
||||
//===========================================
|
||||
|
||||
//Requires GL_ARB_uniform_buffer_object
|
||||
PFNGLGETUNIFORMINDICESPROC glGetUniformIndices = NULL;
|
||||
PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv = NULL;
|
||||
PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName = NULL;
|
||||
PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex = NULL;
|
||||
PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv = NULL;
|
||||
PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName = NULL;
|
||||
PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding = NULL;
|
||||
PFNGLBINDBUFFERBASEPROC glBindBufferBase = NULL;
|
||||
|
||||
|
||||
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = NULL;
|
||||
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = NULL;
|
||||
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray = NULL;
|
||||
|
||||
#endif
|
||||
|
||||
namespace ZL {
|
||||
|
||||
bool BindOpenGlFunctions()
|
||||
{
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
//char* extensionList = (char*)glGetString(GL_EXTENSIONS);
|
||||
char* glVersion = (char*)glGetString(GL_VERSION);
|
||||
bool ok = true;
|
||||
|
||||
//Requires OpenGL 2.0 or above
|
||||
if (glVersion[0] >= '2')
|
||||
{
|
||||
|
||||
glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");
|
||||
|
||||
glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
|
||||
glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffers");
|
||||
glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
|
||||
glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
|
||||
glBufferSubData = (PFNGLBUFFERSUBDATAPROC)wglGetProcAddress("glBufferSubData");
|
||||
glMapBuffer = (PFNGLMAPBUFFERPROC)wglGetProcAddress("glMapBuffer");
|
||||
glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)wglGetProcAddress("glUnmapBuffer");
|
||||
|
||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
|
||||
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram");
|
||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
|
||||
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)wglGetProcAddress("glValidateProgram");
|
||||
glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
|
||||
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
|
||||
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
|
||||
glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
|
||||
|
||||
|
||||
glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
|
||||
glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
|
||||
glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
|
||||
glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
|
||||
glDetachShader = (PFNGLDETACHSHADERPROC)wglGetProcAddress("glDetachShader");
|
||||
glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
|
||||
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
|
||||
glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)wglGetProcAddress("glGetAttribLocation");
|
||||
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
|
||||
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
|
||||
|
||||
glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glDisableVertexAttribArray");
|
||||
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");
|
||||
glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)wglGetProcAddress("glUniformMatrix3fv");
|
||||
glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");
|
||||
glUniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i");
|
||||
glUniform1fv = (PFNGLUNIFORM1FVPROC)wglGetProcAddress("glUniform1fv");
|
||||
glUniform2fv = (PFNGLUNIFORM2FVPROC)wglGetProcAddress("glUniform2fv");
|
||||
glUniform3fv = (PFNGLUNIFORM3FVPROC)wglGetProcAddress("glUniform3fv");
|
||||
glUniform4fv = (PFNGLUNIFORM4FVPROC)wglGetProcAddress("glUniform4fv");
|
||||
|
||||
glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)wglGetProcAddress("glVertexAttrib1f");
|
||||
glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)wglGetProcAddress("glVertexAttrib2f");
|
||||
glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)wglGetProcAddress("glVertexAttrib3f");
|
||||
glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)wglGetProcAddress("glVertexAttrib4f");
|
||||
glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)wglGetProcAddress("glVertexAttrib2fv");
|
||||
glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)wglGetProcAddress("glVertexAttrib3fv");
|
||||
glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)wglGetProcAddress("glVertexAttrib4fv");
|
||||
glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)wglGetProcAddress("glGetActiveAttrib");
|
||||
glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)wglGetProcAddress("glGetActiveUniform");
|
||||
|
||||
|
||||
if (glActiveTexture == NULL ||
|
||||
glGenBuffers == NULL ||
|
||||
glDeleteBuffers == NULL ||
|
||||
glBindBuffer == NULL ||
|
||||
glBufferData == NULL ||
|
||||
glBufferSubData == NULL ||
|
||||
glMapBuffer == NULL ||
|
||||
glCreateProgram == NULL ||
|
||||
glDeleteProgram == NULL ||
|
||||
glLinkProgram == NULL ||
|
||||
glValidateProgram == NULL ||
|
||||
glUseProgram == NULL ||
|
||||
glGetProgramiv == NULL ||
|
||||
glGetProgramInfoLog == NULL ||
|
||||
glCreateShader == NULL ||
|
||||
glDeleteShader == NULL ||
|
||||
glShaderSource == NULL ||
|
||||
glCompileShader == NULL ||
|
||||
glAttachShader == NULL ||
|
||||
glDetachShader == NULL ||
|
||||
glGetShaderiv == NULL ||
|
||||
glGetShaderInfoLog == NULL ||
|
||||
glGetAttribLocation == NULL ||
|
||||
glVertexAttribPointer == NULL ||
|
||||
glEnableVertexAttribArray == NULL ||
|
||||
glDisableVertexAttribArray == NULL ||
|
||||
glGetUniformLocation == NULL ||
|
||||
glUniformMatrix3fv == NULL ||
|
||||
glUniformMatrix4fv == NULL ||
|
||||
glUniform1i == NULL ||
|
||||
glUniform1fv == NULL ||
|
||||
glUniform2fv == NULL ||
|
||||
glUniform3fv == NULL ||
|
||||
glUniform4fv == NULL ||
|
||||
glEnableVertexAttribArray == NULL ||
|
||||
glVertexAttrib1f == NULL ||
|
||||
glVertexAttrib2f == NULL ||
|
||||
glVertexAttrib3f == NULL ||
|
||||
glVertexAttrib4f == NULL ||
|
||||
glVertexAttrib2fv == NULL ||
|
||||
glVertexAttrib3fv == NULL ||
|
||||
glVertexAttrib4fv == NULL ||
|
||||
glGetActiveAttrib == NULL ||
|
||||
glGetActiveUniform == NULL)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
|
||||
glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)wglGetProcAddress("glIsRenderbuffer");
|
||||
glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbuffer");
|
||||
glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffers");
|
||||
glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffers");
|
||||
glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorage");
|
||||
glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)wglGetProcAddress("glGetRenderbufferParameteriv");
|
||||
glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)wglGetProcAddress("glIsFramebuffer");
|
||||
glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer");
|
||||
glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers");
|
||||
glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers");
|
||||
glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus");
|
||||
glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)wglGetProcAddress("glFramebufferTexture1D");
|
||||
glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D");
|
||||
glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)wglGetProcAddress("glFramebufferTexture3D");
|
||||
glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer");
|
||||
glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)wglGetProcAddress("glGetFramebufferAttachmentParameteriv");
|
||||
glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)wglGetProcAddress("glBlitFramebuffer");
|
||||
glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)wglGetProcAddress("glRenderbufferStorageMultisample");
|
||||
glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap");
|
||||
glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)wglGetProcAddress("glFramebufferTextureLayer");
|
||||
|
||||
if (glIsRenderbuffer == NULL ||
|
||||
glBindRenderbuffer == NULL ||
|
||||
glDeleteRenderbuffers == NULL ||
|
||||
glGenRenderbuffers == NULL ||
|
||||
glRenderbufferStorage == NULL ||
|
||||
glGetRenderbufferParameteriv == NULL ||
|
||||
glIsFramebuffer == NULL ||
|
||||
glBindFramebuffer == NULL ||
|
||||
glDeleteFramebuffers == NULL ||
|
||||
glGenFramebuffers == NULL ||
|
||||
glCheckFramebufferStatus == NULL ||
|
||||
glFramebufferTexture1D == NULL ||
|
||||
glFramebufferTexture2D == NULL ||
|
||||
glFramebufferTexture3D == NULL ||
|
||||
glFramebufferRenderbuffer == NULL ||
|
||||
glGetFramebufferAttachmentParameteriv == NULL ||
|
||||
glBlitFramebuffer == NULL ||
|
||||
glRenderbufferStorageMultisample == NULL ||
|
||||
glGenerateMipmap == NULL ||
|
||||
glFramebufferTextureLayer == NULL)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
|
||||
glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)wglGetProcAddress("glGetUniformIndices");
|
||||
glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)wglGetProcAddress("glGetActiveUniformsiv");
|
||||
glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)wglGetProcAddress("glGetActiveUniformName");
|
||||
glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)wglGetProcAddress("glGetUniformBlockIndex");
|
||||
glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)wglGetProcAddress("glGetActiveUniformBlockiv");
|
||||
glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)wglGetProcAddress("glGetActiveUniformBlockName");
|
||||
glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)wglGetProcAddress("glUniformBlockBinding");
|
||||
glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)wglGetProcAddress("glBindBufferBase");
|
||||
|
||||
if (glGetUniformIndices == NULL ||
|
||||
glGetActiveUniformsiv == NULL ||
|
||||
glGetActiveUniformName == NULL ||
|
||||
glGetUniformBlockIndex == NULL ||
|
||||
glGetActiveUniformBlockiv == NULL ||
|
||||
glGetActiveUniformBlockName == NULL ||
|
||||
glUniformBlockBinding == NULL ||
|
||||
glBindBufferBase == NULL)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
|
||||
glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
|
||||
glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
|
||||
glDeleteVertexArray = (PFNGLDELETEVERTEXARRAYSPROC)wglGetProcAddress("glBindVertexArray");
|
||||
|
||||
if (glGenVertexArrays == NULL ||
|
||||
glBindVertexArray == NULL ||
|
||||
glDeleteVertexArray == NULL)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
|
||||
return ok;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckGlError()
|
||||
{
|
||||
size_t error = glGetError();
|
||||
if (error != GL_NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("Gl error");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "SDL.h"
|
||||
#ifdef EMSCRIPTEN
|
||||
//#define GL_GLEXT_PROTOTYPES 1
|
||||
//#define EGL_EGLEXT_PROTOTYPES 1
|
||||
//#include <SDL2/SDL_opengl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include "emscripten.h"
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
|
||||
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
|
||||
|
||||
//#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include "gl/gl.h"
|
||||
#include "gl/glu.h"
|
||||
#include "gl/glext.h"
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
//Requires GL_VERSION_2_0
|
||||
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
|
||||
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
|
||||
extern PFNGLLINKPROGRAMPROC glLinkProgram;
|
||||
extern PFNGLVALIDATEPROGRAMPROC glValidateProgram;
|
||||
extern PFNGLUSEPROGRAMPROC glUseProgram;
|
||||
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
||||
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
||||
extern PFNGLCREATESHADERPROC glCreateShader;
|
||||
extern PFNGLDELETESHADERPROC glDeleteShader;
|
||||
extern PFNGLSHADERSOURCEPROC glShaderSource;
|
||||
extern PFNGLCOMPILESHADERPROC glCompileShader;
|
||||
extern PFNGLATTACHSHADERPROC glAttachShader;
|
||||
extern PFNGLDETACHSHADERPROC glDetachShader;
|
||||
extern PFNGLGETSHADERIVPROC glGetShaderiv;
|
||||
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
||||
extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
|
||||
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
|
||||
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
|
||||
extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
|
||||
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
|
||||
extern PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv;
|
||||
extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
|
||||
extern PFNGLUNIFORM1IPROC glUniform1i;
|
||||
extern PFNGLUNIFORM1FVPROC glUniform1fv;
|
||||
extern PFNGLUNIFORM3FVPROC glUniform2fv;
|
||||
extern PFNGLUNIFORM3FVPROC glUniform3fv;
|
||||
extern PFNGLUNIFORM4FVPROC glUniform4fv;
|
||||
extern PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f;
|
||||
extern PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f;
|
||||
extern PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f;
|
||||
extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f;
|
||||
extern PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv;
|
||||
extern PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv;
|
||||
extern PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv;
|
||||
extern PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib;
|
||||
extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
|
||||
|
||||
|
||||
//=======================================
|
||||
//=========== Multitexture ==============
|
||||
//=======================================
|
||||
|
||||
//Requires GL version 1.3
|
||||
extern PFNGLACTIVETEXTUREPROC glActiveTexture;
|
||||
|
||||
//=======================================
|
||||
//========== Vertex buffer ==============
|
||||
//=======================================
|
||||
|
||||
//Requires GL_VERSION_1_5
|
||||
extern PFNGLGENBUFFERSPROC glGenBuffers;
|
||||
extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
|
||||
extern PFNGLBINDBUFFERPROC glBindBuffer;
|
||||
extern PFNGLBUFFERDATAPROC glBufferData;
|
||||
extern PFNGLBUFFERSUBDATAPROC glBufferSubData;
|
||||
extern PFNGLMAPBUFFERPROC glMapBuffer;
|
||||
extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
|
||||
|
||||
//=========================================
|
||||
//============ Frame buffer ===============
|
||||
//=========================================
|
||||
|
||||
//Requires GL_ARB_framebuffer_object
|
||||
extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer;
|
||||
extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
|
||||
extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
|
||||
extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
|
||||
extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
|
||||
extern PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv;
|
||||
extern PFNGLISFRAMEBUFFERPROC glIsFramebuffer;
|
||||
extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
|
||||
extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
|
||||
extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
|
||||
extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
|
||||
extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D;
|
||||
extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
|
||||
extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D;
|
||||
extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
|
||||
extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv;
|
||||
extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
|
||||
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
|
||||
extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
|
||||
extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer;
|
||||
|
||||
|
||||
|
||||
//===========================================
|
||||
//============ Uniform buffer ===============
|
||||
//===========================================
|
||||
|
||||
//Requires GL_ARB_uniform_buffer_object
|
||||
extern PFNGLGETUNIFORMINDICESPROC glGetUniformIndices;
|
||||
extern PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv;
|
||||
extern PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName;
|
||||
extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
|
||||
extern PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv;
|
||||
extern PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName;
|
||||
extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
|
||||
extern PFNGLBINDBUFFERBASEPROC glBindBufferBase;
|
||||
|
||||
|
||||
extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
|
||||
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
|
||||
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray;
|
||||
#else
|
||||
|
||||
#endif
|
||||
namespace ZL {
|
||||
|
||||
|
||||
|
||||
bool BindOpenGlFunctions();
|
||||
|
||||
void CheckGlError();
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
#include "render/Renderer.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
VBOHolder::VBOHolder()
|
||||
{
|
||||
glGenBuffers(1, &Buffer);
|
||||
}
|
||||
|
||||
VBOHolder::~VBOHolder()
|
||||
{
|
||||
glDeleteBuffers(1, &Buffer);
|
||||
}
|
||||
|
||||
GLuint VBOHolder::getBuffer()
|
||||
{
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
VAOHolder::VAOHolder()
|
||||
{
|
||||
#ifndef EMSCRIPTEN
|
||||
glGenVertexArrays(1, &vao);
|
||||
#endif
|
||||
}
|
||||
|
||||
VAOHolder::~VAOHolder()
|
||||
{
|
||||
#ifndef EMSCRIPTEN
|
||||
|
||||
#ifdef __linux__
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
#else
|
||||
//Windows
|
||||
glDeleteVertexArray(1, &vao);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
GLuint VAOHolder::getBuffer()
|
||||
{
|
||||
return vao;
|
||||
}
|
||||
|
||||
|
||||
VertexDataStruct CreateRect2D(Vector2f center, Vector2f halfWidthHeight, float zLevel)
|
||||
{
|
||||
Vector2f posFrom = center - halfWidthHeight;
|
||||
|
||||
Vector2f posTo = center + halfWidthHeight;
|
||||
|
||||
Vector3f pos1 = { posFrom.v[0], posFrom.v[1], zLevel };
|
||||
Vector3f pos2 = { posFrom.v[0], posTo.v[1], zLevel };
|
||||
Vector3f pos3 = { posTo.v[0], posTo.v[1], zLevel };
|
||||
Vector3f pos4 = { posTo.v[0], posFrom.v[1], zLevel };
|
||||
|
||||
|
||||
Vector2f texCoordPos1 = { 0.0f, 0.0f };
|
||||
Vector2f texCoordPos2 = { 0.0f, 1.0f };
|
||||
Vector2f texCoordPos3 = { 1.0f, 1.0f };
|
||||
Vector2f texCoordPos4 = { 1.0f, 0.0f };
|
||||
|
||||
VertexDataStruct result;
|
||||
|
||||
result.PositionData.push_back(pos1);
|
||||
result.PositionData.push_back(pos2);
|
||||
result.PositionData.push_back(pos3);
|
||||
result.PositionData.push_back(pos3);
|
||||
result.PositionData.push_back(pos4);
|
||||
result.PositionData.push_back(pos1);
|
||||
|
||||
result.TexCoordData.push_back(texCoordPos1);
|
||||
result.TexCoordData.push_back(texCoordPos2);
|
||||
result.TexCoordData.push_back(texCoordPos3);
|
||||
result.TexCoordData.push_back(texCoordPos3);
|
||||
result.TexCoordData.push_back(texCoordPos4);
|
||||
result.TexCoordData.push_back(texCoordPos1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VertexDataStruct CreateRectHorizontalSections2D(Vector2f center, Vector2f halfWidthHeight, float zLevel, size_t sectionCount)
|
||||
{
|
||||
Vector2f posFrom = center - halfWidthHeight;
|
||||
|
||||
Vector2f posTo = center + halfWidthHeight;
|
||||
|
||||
float sectionWidth = halfWidthHeight.v[0] * 2.f;
|
||||
|
||||
VertexDataStruct result;
|
||||
|
||||
for (size_t i = 0; i < sectionCount; i++)
|
||||
{
|
||||
Vector3f pos1 = { posFrom.v[0]+sectionWidth*i, posFrom.v[1], zLevel };
|
||||
Vector3f pos2 = { posFrom.v[0] + sectionWidth * i, posTo.v[1], zLevel };
|
||||
Vector3f pos3 = { posTo.v[0] + sectionWidth * i, posTo.v[1], zLevel };
|
||||
Vector3f pos4 = { posTo.v[0] + sectionWidth * i, posFrom.v[1], zLevel };
|
||||
|
||||
result.PositionData.push_back(pos1);
|
||||
result.PositionData.push_back(pos2);
|
||||
result.PositionData.push_back(pos3);
|
||||
result.PositionData.push_back(pos3);
|
||||
result.PositionData.push_back(pos4);
|
||||
result.PositionData.push_back(pos1);
|
||||
|
||||
Vector2f texCoordPos1 = { 0.0f, 0.0f };
|
||||
Vector2f texCoordPos2 = { 0.0f, 1.0f };
|
||||
Vector2f texCoordPos3 = { 1.0f, 1.0f };
|
||||
Vector2f texCoordPos4 = { 1.0f, 0.0f };
|
||||
|
||||
result.TexCoordData.push_back(texCoordPos1);
|
||||
result.TexCoordData.push_back(texCoordPos2);
|
||||
result.TexCoordData.push_back(texCoordPos3);
|
||||
result.TexCoordData.push_back(texCoordPos3);
|
||||
result.TexCoordData.push_back(texCoordPos4);
|
||||
result.TexCoordData.push_back(texCoordPos1);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
VertexDataStruct CreateCube3D(float scale)
|
||||
{
|
||||
|
||||
std::array<std::array<Vector3f, 4>, 6> cubeSides;
|
||||
|
||||
std::array<Vector3f, 6> cubeColors;
|
||||
|
||||
|
||||
cubeSides[0][0] = { -1, -1, -1 };
|
||||
cubeSides[0][1] = { -1, 1, -1 };
|
||||
cubeSides[0][2] = { 1, 1, -1 };
|
||||
cubeSides[0][3] = { 1, -1, -1 };
|
||||
|
||||
cubeSides[1][0] = { -1, -1, 1 };
|
||||
cubeSides[1][1] = { -1, 1, 1 };
|
||||
cubeSides[1][2] = { 1, 1, 1 };
|
||||
cubeSides[1][3] = { 1, -1, 1 };
|
||||
|
||||
//------------
|
||||
|
||||
cubeSides[2][0] = { -1, -1, -1 };
|
||||
cubeSides[2][1] = { -1, -1, 1 };
|
||||
cubeSides[2][2] = { 1, -1, 1 };
|
||||
cubeSides[2][3] = { 1, -1, -1 };
|
||||
|
||||
cubeSides[3][0] = { -1, 1, -1 };
|
||||
cubeSides[3][1] = { -1, 1, 1 };
|
||||
cubeSides[3][2] = { 1, 1, 1 };
|
||||
cubeSides[3][3] = { 1, 1, -1 };
|
||||
|
||||
//------------
|
||||
cubeSides[4][0] = { -1, -1, -1 };
|
||||
cubeSides[4][1] = { -1, -1, 1 };
|
||||
cubeSides[4][2] = { -1, 1, 1 };
|
||||
cubeSides[4][3] = { -1, 1, -1 };
|
||||
|
||||
cubeSides[5][0] = { 1, -1, -1 };
|
||||
cubeSides[5][1] = { 1, -1, 1 };
|
||||
cubeSides[5][2] = { 1, 1, 1 };
|
||||
cubeSides[5][3] = { 1, 1, -1 };
|
||||
|
||||
//-----------
|
||||
|
||||
cubeColors[0] = Vector3f{ 1, 0, 0 };
|
||||
cubeColors[1] = Vector3f{ 0, 1, 0 };
|
||||
cubeColors[2] = Vector3f{ 0, 0, 1 };
|
||||
cubeColors[3] = Vector3f{ 1, 1, 0 };
|
||||
cubeColors[4] = Vector3f{ 0, 1, 1 };
|
||||
cubeColors[5] = Vector3f{ 1, 0, 1 };
|
||||
|
||||
//-----------
|
||||
|
||||
VertexDataStruct result;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
result.PositionData.push_back(cubeSides[i][0] * scale);
|
||||
result.PositionData.push_back(cubeSides[i][1] * scale);
|
||||
result.PositionData.push_back(cubeSides[i][2] * scale);
|
||||
result.PositionData.push_back(cubeSides[i][2] * scale);
|
||||
result.PositionData.push_back(cubeSides[i][3] * scale);
|
||||
result.PositionData.push_back(cubeSides[i][0] * scale);
|
||||
|
||||
result.ColorData.push_back(cubeColors[i]);
|
||||
result.ColorData.push_back(cubeColors[i]);
|
||||
result.ColorData.push_back(cubeColors[i]);
|
||||
result.ColorData.push_back(cubeColors[i]);
|
||||
result.ColorData.push_back(cubeColors[i]);
|
||||
result.ColorData.push_back(cubeColors[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VertexDataStruct CreateCubemap(float scale)
|
||||
{
|
||||
VertexDataStruct cubemapVertexDataStruct;
|
||||
|
||||
// +x
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, scale });
|
||||
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, scale });
|
||||
|
||||
// -x
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, scale });
|
||||
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, scale });
|
||||
|
||||
|
||||
// +y
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, scale });
|
||||
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, scale });
|
||||
|
||||
|
||||
// -y
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, scale });
|
||||
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, scale });
|
||||
|
||||
|
||||
// +z
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, scale });
|
||||
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, scale });
|
||||
|
||||
// -z
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, -scale });
|
||||
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, -scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ scale, scale, -scale });
|
||||
cubemapVertexDataStruct.PositionData.push_back({ -scale, scale, -scale });
|
||||
|
||||
return cubemapVertexDataStruct;
|
||||
|
||||
}
|
||||
|
||||
void VertexRenderStruct::RefreshVBO()
|
||||
{
|
||||
//Check if main thread, check if data is not empty...
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
if (!vao)
|
||||
{
|
||||
vao = std::make_shared<VAOHolder>();
|
||||
}
|
||||
|
||||
glBindVertexArray(vao->getBuffer());
|
||||
#endif
|
||||
if (!positionVBO)
|
||||
{
|
||||
positionVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, positionVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.PositionData.size() * 12, &data.PositionData[0], GL_STATIC_DRAW);
|
||||
|
||||
if (data.TexCoordData.size() > 0)
|
||||
{
|
||||
if (!texCoordVBO)
|
||||
{
|
||||
texCoordVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, texCoordVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.NormalData.size() > 0)
|
||||
{
|
||||
if (!normalVBO)
|
||||
{
|
||||
normalVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, normalVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.NormalData.size() * 12, &data.NormalData[0], GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.TangentData.size() > 0)
|
||||
{
|
||||
if (!tangentVBO)
|
||||
{
|
||||
tangentVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tangentVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.TangentData.size() * 12, &data.TangentData[0], GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.BinormalData.size() > 0)
|
||||
{
|
||||
if (!binormalVBO)
|
||||
{
|
||||
binormalVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, binormalVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.BinormalData.size() * 12, &data.BinormalData[0], GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
if (data.ColorData.size() > 0)
|
||||
{
|
||||
if (!colorVBO)
|
||||
{
|
||||
colorVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, colorVBO->getBuffer());
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, data.ColorData.size() * 12, &data.ColorData[0], GL_STATIC_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDataStruct::Scale(float scale)
|
||||
{
|
||||
for (int i = 0; i < PositionData.size(); i++)
|
||||
{
|
||||
PositionData[i] = PositionData[i] * scale;
|
||||
}
|
||||
}
|
||||
void VertexDataStruct::Move(Vector3f diff)
|
||||
{
|
||||
for (int i = 0; i < PositionData.size(); i++)
|
||||
{
|
||||
PositionData[i] = PositionData[i] + diff;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDataStruct::SwapZandY()
|
||||
{
|
||||
for (int i = 0; i < PositionData.size(); i++)
|
||||
{
|
||||
auto value = PositionData[i].v[1];
|
||||
PositionData[i].v[1] = PositionData[i].v[2];
|
||||
PositionData[i].v[2] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VertexDataStruct::RotateByMatrix(Matrix3f m)
|
||||
{
|
||||
|
||||
for (int i = 0; i < PositionData.size(); i++)
|
||||
{
|
||||
PositionData[i] = MultVectorMatrix(PositionData[i], m);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NormalData.size(); i++)
|
||||
{
|
||||
NormalData[i] = MultVectorMatrix(NormalData[i], m);
|
||||
}
|
||||
|
||||
for (int i = 0; i < TangentData.size(); i++)
|
||||
{
|
||||
TangentData[i] = MultVectorMatrix(TangentData[i], m);
|
||||
}
|
||||
|
||||
for (int i = 0; i < BinormalData.size(); i++)
|
||||
{
|
||||
BinormalData[i] = MultVectorMatrix(BinormalData[i], m);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexRenderStruct::AssignFrom(const VertexDataStruct& v)
|
||||
{
|
||||
data = v;
|
||||
RefreshVBO();
|
||||
}
|
||||
|
||||
void Renderer::InitOpenGL()
|
||||
{
|
||||
ModelviewMatrixStack.push(Matrix4f::Identity());
|
||||
ProjectionMatrixStack.push(Matrix4f::Identity());
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
void Renderer::PushProjectionMatrix(float width, float height, float zNear, float zFar)
|
||||
{
|
||||
Matrix4f m = MakeOrthoMatrix(width, height, zNear, zFar);
|
||||
ProjectionMatrixStack.push(m);
|
||||
SetMatrix();
|
||||
|
||||
if (ProjectionMatrixStack.size() > CONST_MATRIX_STACK_SIZE)
|
||||
{
|
||||
throw std::runtime_error("Projection matrix stack overflow!!!!");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::PushProjectionMatrix(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar)
|
||||
{
|
||||
Matrix4f m = MakeOrthoMatrix(xmin, xmax, ymin, ymax, zNear, zFar);
|
||||
ProjectionMatrixStack.push(m);
|
||||
SetMatrix();
|
||||
|
||||
if (ProjectionMatrixStack.size() > CONST_MATRIX_STACK_SIZE)
|
||||
{
|
||||
throw std::runtime_error("Projection matrix stack overflow!!!!");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::PushPerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear, float zFar)
|
||||
{
|
||||
Matrix4f m = MakePerspectiveMatrix(fovY, aspectRatio, zNear, zFar);
|
||||
ProjectionMatrixStack.push(m);
|
||||
SetMatrix();
|
||||
|
||||
if (ProjectionMatrixStack.size() > CONST_MATRIX_STACK_SIZE)
|
||||
{
|
||||
throw std::runtime_error("Projection matrix stack overflow!!!!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Renderer::PopProjectionMatrix()
|
||||
{
|
||||
if (ProjectionMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Projection matrix stack underflow!!!!");
|
||||
}
|
||||
ProjectionMatrixStack.pop();
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
Matrix4f Renderer::GetProjectionModelViewMatrix()
|
||||
{
|
||||
return ProjectionModelViewMatrix;
|
||||
}
|
||||
|
||||
Matrix4f Renderer::GetCurrentModelViewMatrix()
|
||||
{
|
||||
return ModelviewMatrixStack.top();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::SetMatrix()
|
||||
{
|
||||
if (ProjectionMatrixStack.size() <= 0)
|
||||
{
|
||||
throw std::runtime_error("Projection matrix stack out!");
|
||||
}
|
||||
|
||||
if (ModelviewMatrixStack.size() <= 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack out!");
|
||||
}
|
||||
|
||||
Matrix4f& modelViewMatrix = ModelviewMatrixStack.top();
|
||||
ProjectionModelViewMatrix = ProjectionMatrixStack.top() * modelViewMatrix;
|
||||
|
||||
static const std::string ProjectionModelViewMatrixName = "ProjectionModelViewMatrix";
|
||||
|
||||
RenderUniformMatrix4fv(ProjectionModelViewMatrixName, false, &ProjectionModelViewMatrix.m[0]);
|
||||
|
||||
static const std::string ModelViewMatrixName = "ModelViewMatrix";
|
||||
|
||||
RenderUniformMatrix4fv(ModelViewMatrixName, false, &modelViewMatrix.m[0]);
|
||||
|
||||
}
|
||||
|
||||
void Renderer::PushMatrix()
|
||||
{
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.push(ModelviewMatrixStack.top());
|
||||
|
||||
if (ModelviewMatrixStack.size() > CONST_MATRIX_STACK_SIZE)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack overflow!!!!");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::LoadIdentity()
|
||||
{
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.pop();
|
||||
ModelviewMatrixStack.push(Matrix4f::Identity());
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
void Renderer::TranslateMatrix(const Vector3f& p)
|
||||
{
|
||||
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
m.m[12] = p.v[0];
|
||||
m.m[13] = p.v[1];
|
||||
m.m[14] = p.v[2];
|
||||
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.pop();
|
||||
ModelviewMatrixStack.push(m);
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
void Renderer::ScaleMatrix(float scale)
|
||||
{
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
m.m[0] = scale;
|
||||
m.m[5] = scale;
|
||||
m.m[10] = scale;
|
||||
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.pop();
|
||||
ModelviewMatrixStack.push(m);
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
void Renderer::ScaleMatrix(const Vector3f& scale)
|
||||
{
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
m.m[0] = scale.v[0];
|
||||
m.m[5] = scale.v[1];
|
||||
m.m[10] = scale.v[2];
|
||||
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.pop();
|
||||
ModelviewMatrixStack.push(m);
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
void Renderer::RotateMatrix(const Vector4f& q)
|
||||
{
|
||||
|
||||
Matrix3f m3 = QuatToMatrix(q);
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
m.m[0] = m3.m[0];
|
||||
m.m[1] = m3.m[1];
|
||||
m.m[2] = m3.m[2];
|
||||
|
||||
m.m[4] = m3.m[3];
|
||||
m.m[5] = m3.m[4];
|
||||
m.m[6] = m3.m[5];
|
||||
|
||||
m.m[8] = m3.m[6];
|
||||
m.m[9] = m3.m[7];
|
||||
m.m[10] = m3.m[8];
|
||||
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.pop();
|
||||
ModelviewMatrixStack.push(m);
|
||||
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
void Renderer::RotateMatrix(const Matrix3f& m3)
|
||||
{
|
||||
Matrix4f m = Matrix4f::Identity();
|
||||
m.m[0] = m3.m[0];
|
||||
m.m[1] = m3.m[1];
|
||||
m.m[2] = m3.m[2];
|
||||
|
||||
m.m[4] = m3.m[3];
|
||||
m.m[5] = m3.m[4];
|
||||
m.m[6] = m3.m[5];
|
||||
|
||||
m.m[8] = m3.m[6];
|
||||
m.m[9] = m3.m[7];
|
||||
m.m[10] = m3.m[8];
|
||||
|
||||
m = ModelviewMatrixStack.top() * m;
|
||||
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
|
||||
ModelviewMatrixStack.pop();
|
||||
ModelviewMatrixStack.push(m);
|
||||
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
void Renderer::PushSpecialMatrix(const Matrix4f& m)
|
||||
{
|
||||
if (ModelviewMatrixStack.size() > 64)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack overflow!!!!");
|
||||
}
|
||||
ModelviewMatrixStack.push(m);
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::PopMatrix()
|
||||
{
|
||||
if (ModelviewMatrixStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Modelview matrix stack underflow!!!!");
|
||||
}
|
||||
ModelviewMatrixStack.pop();
|
||||
|
||||
SetMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::EnableVertexAttribArray(const std::string& attribName)
|
||||
{
|
||||
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
if (shader->attribList.find(attribName) != shader->attribList.end())
|
||||
glEnableVertexAttribArray(shader->attribList[attribName]);
|
||||
}
|
||||
|
||||
void Renderer::DisableVertexAttribArray(const std::string& attribName)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
if (shader->attribList.find(attribName) != shader->attribList.end())
|
||||
glDisableVertexAttribArray(shader->attribList[attribName]);
|
||||
}
|
||||
|
||||
void Renderer::RenderUniformMatrix3fv(const std::string& uniformName, bool transpose, const float* value)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
auto uniform = shader->uniformList.find(uniformName);
|
||||
|
||||
if (uniform != shader->uniformList.end())
|
||||
{
|
||||
glUniformMatrix3fv(uniform->second, 1, transpose, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Renderer::RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
auto uniform = shader->uniformList.find(uniformName);
|
||||
|
||||
if (uniform != shader->uniformList.end())
|
||||
{
|
||||
glUniformMatrix4fv(uniform->second, 1, transpose, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::RenderUniform3fv(const std::string& uniformName, const float* value)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
auto uniform = shader->uniformList.find(uniformName);
|
||||
|
||||
if (uniform != shader->uniformList.end())
|
||||
{
|
||||
glUniform3fv(uniform->second, 1, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::RenderUniform1i(const std::string& uniformName, const int value)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
auto uniform = shader->uniformList.find(uniformName);
|
||||
|
||||
if (uniform != shader->uniformList.end())
|
||||
{
|
||||
glUniform1i(uniform->second, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Renderer::RenderUniform1f(const std::string& uniformName, float value)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
auto uniform = shader->uniformList.find(uniformName);
|
||||
|
||||
if (uniform != shader->uniformList.end())
|
||||
{
|
||||
glUniform1fv(uniform->second, 1, &value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer::VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer)
|
||||
{
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
if (shader->attribList.find(attribName) != shader->attribList.end())
|
||||
glVertexAttribPointer(shader->attribList[attribName], 2, GL_FLOAT, GL_FALSE, stride, pointer);
|
||||
|
||||
}
|
||||
|
||||
void Renderer::VertexAttribPointer3fv(const std::string& attribName, int stride, const char* pointer)
|
||||
{
|
||||
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
if (shader->attribList.find(attribName) != shader->attribList.end())
|
||||
glVertexAttribPointer(shader->attribList[attribName], 3, GL_FLOAT, GL_FALSE, stride, pointer);
|
||||
}
|
||||
|
||||
void Renderer::DrawVertexRenderStruct(const VertexRenderStruct& VertexRenderStruct)
|
||||
{
|
||||
static const std::string vNormal("vNormal");
|
||||
static const std::string vTangent("vTangent");
|
||||
static const std::string vBinormal("vBinormal");
|
||||
static const std::string vColor("vColor");
|
||||
static const std::string vTexCoord("vTexCoord");
|
||||
static const std::string vPosition("vPosition");
|
||||
|
||||
//glBindVertexArray(VertexRenderStruct.vao->getBuffer());
|
||||
|
||||
//Check if main thread, check if data is not empty...
|
||||
if (VertexRenderStruct.data.NormalData.size() > 0)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.normalVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vNormal, 0, NULL);
|
||||
}
|
||||
if (VertexRenderStruct.data.TangentData.size() > 0)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.tangentVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vTangent, 0, NULL);
|
||||
}
|
||||
if (VertexRenderStruct.data.BinormalData.size() > 0)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.binormalVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vBinormal, 0, NULL);
|
||||
}
|
||||
if (VertexRenderStruct.data.ColorData.size() > 0)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.colorVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vColor, 0, NULL);
|
||||
}
|
||||
if (VertexRenderStruct.data.TexCoordData.size() > 0)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer());
|
||||
VertexAttribPointer2fv(vTexCoord, 0, NULL);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vPosition, 0, NULL);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(VertexRenderStruct.data.PositionData.size()));
|
||||
|
||||
}
|
||||
|
||||
void worldToScreenCoordinates(Vector3f objectPos,
|
||||
Matrix4f projectionModelView,
|
||||
int screenWidth, int screenHeight,
|
||||
int& screenX, int& screenY) {
|
||||
|
||||
Vector4f inx = { objectPos.v[0], objectPos.v[1], objectPos.v[2], 1.0f };
|
||||
Vector4f clipCoords = MultMatrixVector(projectionModelView, inx);
|
||||
|
||||
float ndcX = clipCoords.v[0] / clipCoords.v[3];
|
||||
float ndcY = clipCoords.v[1] / clipCoords.v[3];
|
||||
|
||||
screenX = (int)((ndcX + 1.0f) * 0.5f * screenWidth);
|
||||
screenY = (int)((1.0f + ndcY) * 0.5f * screenHeight);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
|
||||
#include "OpenGlExtensions.h"
|
||||
#include "utils/ZLMath.h"
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include "ShaderManager.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
constexpr size_t CONST_MATRIX_STACK_SIZE = 64;
|
||||
|
||||
class VBOHolder {
|
||||
GLuint Buffer;
|
||||
|
||||
public:
|
||||
VBOHolder();
|
||||
|
||||
VBOHolder(const VBOHolder& v) = delete;
|
||||
|
||||
VBOHolder& operator=(const VBOHolder& v) = delete;
|
||||
|
||||
~VBOHolder();
|
||||
|
||||
GLuint getBuffer();
|
||||
};
|
||||
|
||||
class VAOHolder {
|
||||
GLuint vao;
|
||||
|
||||
public:
|
||||
VAOHolder();
|
||||
|
||||
VAOHolder(const VAOHolder& v) = delete;
|
||||
|
||||
VAOHolder& operator=(const VAOHolder& v) = delete;
|
||||
|
||||
~VAOHolder();
|
||||
|
||||
GLuint getBuffer();
|
||||
};
|
||||
|
||||
struct VertexDataStruct
|
||||
{
|
||||
std::vector<Vector3f> PositionData;
|
||||
std::vector<Vector2f> TexCoordData;
|
||||
std::vector<Vector3f> NormalData;
|
||||
std::vector<Vector3f> TangentData;
|
||||
std::vector<Vector3f> BinormalData;
|
||||
std::vector<Vector3f> ColorData;
|
||||
|
||||
void RotateByMatrix(Matrix3f m);
|
||||
|
||||
void Scale(float scale);
|
||||
void Move(Vector3f diff);
|
||||
void SwapZandY();
|
||||
};
|
||||
|
||||
struct VertexRenderStruct
|
||||
{
|
||||
VertexDataStruct data;
|
||||
|
||||
std::shared_ptr<VAOHolder> vao;
|
||||
std::shared_ptr<VBOHolder> positionVBO;
|
||||
std::shared_ptr<VBOHolder> texCoordVBO;
|
||||
std::shared_ptr<VBOHolder> normalVBO;
|
||||
std::shared_ptr<VBOHolder> tangentVBO;
|
||||
std::shared_ptr<VBOHolder> binormalVBO;
|
||||
std::shared_ptr<VBOHolder> colorVBO;
|
||||
void RefreshVBO();
|
||||
|
||||
void AssignFrom(const VertexDataStruct& v);
|
||||
};
|
||||
|
||||
VertexDataStruct CreateRect2D(Vector2f center, Vector2f halfWidthHeight, float zLevel);
|
||||
VertexDataStruct CreateRectHorizontalSections2D(Vector2f center, Vector2f halfWidthHeight, float zLevel, size_t sectionCount);
|
||||
VertexDataStruct CreateCube3D(float scale);
|
||||
VertexDataStruct CreateCubemap(float scale = 1000.f);
|
||||
|
||||
|
||||
class Renderer
|
||||
{
|
||||
protected:
|
||||
std::stack<Matrix4f> ProjectionMatrixStack;
|
||||
std::stack<Matrix4f> ModelviewMatrixStack;
|
||||
|
||||
Matrix4f ProjectionModelViewMatrix;
|
||||
|
||||
public:
|
||||
|
||||
ShaderManager shaderManager;
|
||||
|
||||
void InitOpenGL();
|
||||
|
||||
void PushProjectionMatrix(float width, float height, float zNear = 0.f, float zFar = 1.f);
|
||||
void PushProjectionMatrix(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar);
|
||||
void PushPerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear, float zFar);
|
||||
void PopProjectionMatrix();
|
||||
|
||||
void PushMatrix();
|
||||
void LoadIdentity();
|
||||
void TranslateMatrix(const Vector3f& p);
|
||||
void ScaleMatrix(float scale);
|
||||
void ScaleMatrix(const Vector3f& scale);
|
||||
void RotateMatrix(const Vector4f& q);
|
||||
void RotateMatrix(const Matrix3f& m3);
|
||||
void PushSpecialMatrix(const Matrix4f& m);
|
||||
void PopMatrix();
|
||||
|
||||
|
||||
Matrix4f GetProjectionModelViewMatrix();
|
||||
Matrix4f GetCurrentModelViewMatrix();
|
||||
|
||||
void SetMatrix();
|
||||
|
||||
|
||||
|
||||
void EnableVertexAttribArray(const std::string& attribName);
|
||||
|
||||
void DisableVertexAttribArray(const std::string& attribName);
|
||||
|
||||
|
||||
void RenderUniformMatrix3fv(const std::string& uniformName, bool transpose, const float* value);
|
||||
void RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value);
|
||||
void RenderUniform1i(const std::string& uniformName, const int value);
|
||||
void RenderUniform3fv(const std::string& uniformName, const float* value);
|
||||
void RenderUniform1f(const std::string& uniformName, float value);
|
||||
|
||||
void VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer);
|
||||
|
||||
void VertexAttribPointer3fv(const std::string& attribName, int stride, const char* pointer);
|
||||
|
||||
void DrawVertexRenderStruct(const VertexRenderStruct& VertexRenderStruct);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
void worldToScreenCoordinates(Vector3f objectPos,
|
||||
Matrix4f projectionModelView,
|
||||
int screenWidth, int screenHeight,
|
||||
int& screenX, int& screenY);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,215 @@
|
||||
#include "ShaderManager.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace ZL {
|
||||
|
||||
ShaderResource::ShaderResource(const std::string& vertexCode, const std::string& fragmentCode)
|
||||
{
|
||||
|
||||
const int CONST_INFOLOG_LENGTH = 256;
|
||||
|
||||
char infoLog[CONST_INFOLOG_LENGTH];
|
||||
int infoLogLength;
|
||||
char infoLog2[CONST_INFOLOG_LENGTH];
|
||||
int infoLogLength2;
|
||||
|
||||
int vertexShaderCompiled;
|
||||
int fragmentShaderCompiled;
|
||||
int programLinked;
|
||||
|
||||
GLuint vertexShader;
|
||||
GLuint fragmentShader;
|
||||
|
||||
int vertexCodeLength = static_cast<int>(strlen(vertexCode.c_str()));
|
||||
int fragmentCodeLength = static_cast<int>(strlen(fragmentCode.c_str()));
|
||||
|
||||
const char* vc = &vertexCode[0];
|
||||
const char* fc = &fragmentCode[0];
|
||||
|
||||
vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertexShader, 1, &(vc), &vertexCodeLength);
|
||||
|
||||
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragmentShader, 1, &(fc), &fragmentCodeLength);
|
||||
|
||||
glCompileShader(vertexShader);
|
||||
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexShaderCompiled);
|
||||
glGetShaderInfoLog(vertexShader, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
||||
|
||||
glCompileShader(fragmentShader);
|
||||
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShaderCompiled);
|
||||
glGetShaderInfoLog(fragmentShader, CONST_INFOLOG_LENGTH, &infoLogLength2, infoLog2);
|
||||
|
||||
|
||||
if (!vertexShaderCompiled)
|
||||
{
|
||||
throw std::runtime_error("Failed to compile vertex shader code!");
|
||||
}
|
||||
|
||||
if (!fragmentShaderCompiled)
|
||||
{
|
||||
throw std::runtime_error("Failed to compile fragment shader code!");
|
||||
}
|
||||
|
||||
shaderProgram = glCreateProgram();
|
||||
|
||||
glAttachShader(shaderProgram, vertexShader);
|
||||
glAttachShader(shaderProgram, fragmentShader);
|
||||
|
||||
glLinkProgram(shaderProgram);
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
|
||||
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programLinked);
|
||||
glGetProgramInfoLog(shaderProgram, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
||||
|
||||
if (!programLinked)
|
||||
{
|
||||
shaderProgram = 0;
|
||||
throw std::runtime_error("Failed to link shader program!");
|
||||
}
|
||||
|
||||
|
||||
int dummySize; //Dummy
|
||||
int dummyLen; //Dummy
|
||||
GLenum dummyEnum;
|
||||
|
||||
|
||||
//================= Parsing all uniforms ================
|
||||
|
||||
int activeUniforms;
|
||||
|
||||
const int CONST_UNIFORM_NAME_LENGTH = 256;
|
||||
char uniformName[CONST_UNIFORM_NAME_LENGTH];
|
||||
|
||||
glGetProgramiv(shaderProgram, GL_ACTIVE_UNIFORMS, &activeUniforms);
|
||||
|
||||
for (int i = 0; i < activeUniforms; i++)
|
||||
{
|
||||
glGetActiveUniform(shaderProgram, i, CONST_UNIFORM_NAME_LENGTH, &dummyLen, &dummySize, &dummyEnum, uniformName);
|
||||
|
||||
uniformList[uniformName] = glGetUniformLocation(shaderProgram, uniformName);
|
||||
}
|
||||
|
||||
//================= Parsing all attributes ================
|
||||
int activeAttribs;
|
||||
|
||||
const int CONST_ATTRIB_NAME_LENGTH = 256;
|
||||
char attribName[CONST_ATTRIB_NAME_LENGTH];
|
||||
|
||||
glGetProgramiv(shaderProgram, GL_ACTIVE_ATTRIBUTES, &activeAttribs);
|
||||
|
||||
for (int i = 0; i < activeAttribs; i++)
|
||||
{
|
||||
glGetActiveAttrib(shaderProgram, i, CONST_ATTRIB_NAME_LENGTH, &dummyLen, &dummySize, &dummyEnum, attribName);
|
||||
attribList[attribName] = glGetAttribLocation(shaderProgram, attribName);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderResource::~ShaderResource()
|
||||
{
|
||||
if (shaderProgram != 0)
|
||||
{
|
||||
glDeleteProgram(shaderProgram);
|
||||
|
||||
shaderProgram = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint ShaderResource::getShaderProgram()
|
||||
{
|
||||
return shaderProgram;
|
||||
}
|
||||
|
||||
|
||||
void ShaderManager::AddShaderFromFiles(const std::string& shaderName, const std::string& vertexShaderFileName, const std::string& fragmentShaderFileName, const std::string& ZIPFileName)
|
||||
{
|
||||
|
||||
std::string vertexShader;
|
||||
std::string fragmentShader;
|
||||
|
||||
if (!ZIPFileName.empty()){
|
||||
|
||||
std::vector<char> vertexShaderData;
|
||||
std::vector<char> fragmentShaderData;
|
||||
|
||||
vertexShaderData = readFileFromZIP(vertexShaderFileName, ZIPFileName);
|
||||
fragmentShaderData = readFileFromZIP(fragmentShaderFileName, ZIPFileName);
|
||||
|
||||
vertexShader = std::string(vertexShaderData.begin(), vertexShaderData.end());
|
||||
fragmentShader = std::string(fragmentShaderData.begin(), fragmentShaderData.end());
|
||||
|
||||
}else{
|
||||
vertexShader = readTextFile(vertexShaderFileName);
|
||||
fragmentShader = readTextFile(fragmentShaderFileName);
|
||||
}
|
||||
///std::cout << "Shader: "<< vertexShader << std::endl;
|
||||
shaderResourceMap[shaderName] = std::make_shared<ShaderResource>(vertexShader, fragmentShader);
|
||||
}
|
||||
|
||||
void ShaderManager::PushShader(const std::string& shaderName)
|
||||
{
|
||||
if (shaderStack.size() >= CONST_MAX_SHADER_STACK_SIZE)
|
||||
{
|
||||
throw std::runtime_error("Shader stack overflow!");
|
||||
}
|
||||
|
||||
if (shaderResourceMap.find(shaderName) == shaderResourceMap.end())
|
||||
{
|
||||
throw std::runtime_error("Shader does not exist!");
|
||||
}
|
||||
|
||||
shaderStack.push(shaderName);
|
||||
|
||||
glUseProgram(shaderResourceMap[shaderName]->getShaderProgram());
|
||||
}
|
||||
|
||||
|
||||
void ShaderManager::PopShader()
|
||||
{
|
||||
if (shaderStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Shader stack underflow!");
|
||||
}
|
||||
|
||||
shaderStack.pop();
|
||||
|
||||
if (shaderStack.size() == 0)
|
||||
{
|
||||
glUseProgram(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUseProgram(shaderResourceMap[shaderStack.top()]->getShaderProgram());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ShaderResource> ShaderManager::GetCurrentShader()
|
||||
{
|
||||
if (shaderStack.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Shader stack underflow!");
|
||||
}
|
||||
|
||||
|
||||
return shaderResourceMap[shaderStack.top()];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ShaderSetter::ShaderSetter(ShaderManager& inShaderManager, const std::string& shaderName)
|
||||
: shaderManager(shaderManager)
|
||||
{
|
||||
shaderManager.PushShader(shaderName);
|
||||
}
|
||||
|
||||
ShaderSetter::~ShaderSetter()
|
||||
{
|
||||
shaderManager.PopShader();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "OpenGlExtensions.h"
|
||||
#include "utils/Utils.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
|
||||
constexpr size_t CONST_MAX_SHADER_STACK_SIZE = 16;
|
||||
|
||||
class ShaderResource
|
||||
{
|
||||
protected:
|
||||
GLuint shaderProgram;
|
||||
|
||||
std::unordered_map<std::string, GLuint> uniformList;
|
||||
|
||||
//std::unordered_map<std::string, std::pair<bool, size_t>> UniformList;
|
||||
std::map<std::string, GLuint> attribList;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
GLuint getShaderProgram();
|
||||
|
||||
ShaderResource(const std::string& vertexCode, const std::string& fragmentCode);
|
||||
~ShaderResource();
|
||||
|
||||
public:
|
||||
friend class ShaderManager;
|
||||
friend class Renderer;
|
||||
};
|
||||
|
||||
class ShaderManager {
|
||||
protected:
|
||||
std::unordered_map<std::string, std::shared_ptr<ShaderResource>> shaderResourceMap;
|
||||
|
||||
std::stack<std::string> shaderStack;
|
||||
|
||||
public:
|
||||
void AddShaderFromFiles(const std::string& shaderName, const std::string& vertexShaderFileName, const std::string& fragmentShaderFileName, const std::string& ZIPFileName = "");
|
||||
|
||||
void PushShader(const std::string& shaderName);
|
||||
void PopShader();
|
||||
|
||||
std::shared_ptr<ShaderResource> GetCurrentShader();
|
||||
};
|
||||
|
||||
|
||||
class ShaderSetter
|
||||
{
|
||||
protected:
|
||||
|
||||
ShaderManager& shaderManager;
|
||||
|
||||
public:
|
||||
ShaderSetter(ShaderManager& inShaderManager, const std::string& shaderName);
|
||||
|
||||
~ShaderSetter();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
#include "render/TextureManager.h"
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#ifdef PNG_ENABLED
|
||||
#include "png.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
Texture::Texture(const TextureDataStruct& texData)
|
||||
{
|
||||
|
||||
width = texData.width;
|
||||
height = texData.height;
|
||||
|
||||
glGenTextures(1, &texID);
|
||||
|
||||
if (texID == 0)
|
||||
{
|
||||
throw std::runtime_error("glGenTextures did not work");
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texID);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
//This should be only for Windows
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
if (texData.bitSize == TextureDataStruct::BS_24BIT)
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, static_cast<GLsizei>(texData.width), static_cast<GLsizei>(texData.height), 0, GL_RGB, GL_UNSIGNED_BYTE, &texData.data[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(texData.width), static_cast<GLsizei>(texData.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData.data[0]);
|
||||
}
|
||||
|
||||
CheckGlError();
|
||||
|
||||
}
|
||||
|
||||
Texture::Texture(const std::array<TextureDataStruct, 6>& texDataArray)
|
||||
{
|
||||
// Ïðîâåðêà, ÷òî âñå ãðàíè èìåþò îäèíàêîâûå ðàçìåðû
|
||||
width = texDataArray[0].width;
|
||||
height = texDataArray[0].height;
|
||||
|
||||
for (size_t i = 1; i < 6; ++i) {
|
||||
if (texDataArray[i].width != width || texDataArray[i].height != height) {
|
||||
throw std::runtime_error("Cubemap faces must have the same dimensions");
|
||||
}
|
||||
}
|
||||
|
||||
glGenTextures(1, &texID);
|
||||
|
||||
if (texID == 0)
|
||||
{
|
||||
throw std::runtime_error("glGenTextures did not work for cubemap");
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, texID);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
// Íàñòðîéêà ïàðàìåòðîâ äëÿ Cubemap
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// Èñïîëüçóåì GL_LINEAR äëÿ MIN_FILTER, òàê êàê ìèïìàïû çäåñü íå ãåíåðèðóþòñÿ
|
||||
// Åñëè áû èñïîëüçîâàëèñü ìèïìàïû (e.g., GL_LINEAR_MIPMAP_LINEAR), íóæíî áûëî áû âûçâàòü glGenerateMipmap.
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
// Îáÿçàòåëüíûå ïàðàìåòðû îáåðòêè äëÿ Cubemap
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// GL_TEXTURE_WRAP_R íå ïîääåðæèâàåòñÿ â WebGL 1.0/OpenGL ES 2.0 è âûçûâàåò îøèáêó.
|
||||
// Îãðàíè÷èâàåì åãî âûçîâ òîëüêî äëÿ íàñòîëüíûõ ïëàòôîðì.
|
||||
#ifndef EMSCRIPTEN
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
#endif
|
||||
|
||||
CheckGlError(); // Ïðîâåðêà ïîñëå óñòàíîâêè ïàðàìåòðîâ
|
||||
|
||||
// Çàãðóçêà äàííûõ äëÿ êàæäîé èç 6 ãðàíåé
|
||||
// GL_TEXTURE_CUBE_MAP_POSITIVE_X + i äàåò ãðàíè: +X (0), -X (1), +Y (2), -Y (3), +Z (4), -Z (5)
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
|
||||
// Â WebGL 1.0/OpenGL ES 2.0 âíóòðåííèé ôîðìàò (internalFormat)
|
||||
// äîëæåí ñòðîãî ñîîòâåòñòâîâàòü ôîðìàòó äàííûõ (format).
|
||||
if (texDataArray[i].bitSize == TextureDataStruct::BS_24BIT)
|
||||
{
|
||||
internalFormat = GL_RGB; // internalFormat
|
||||
format = GL_RGB; // format
|
||||
}
|
||||
else // BS_32BIT
|
||||
{
|
||||
internalFormat = GL_RGBA; // internalFormat
|
||||
format = GL_RGBA; // format
|
||||
}
|
||||
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, // Öåëåâàÿ ãðàíü
|
||||
0, // Óðîâåíü MIP-òåêñòóðû
|
||||
internalFormat, // Âíóòðåííèé ôîðìàò (äîëæåí ñîâïàäàòü ñ ôîðìàòîì)
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
0, // Ãðàíèöà (âñåãäà 0)
|
||||
format, // Ôîðìàò èñõîäíûõ äàííûõ
|
||||
GL_UNSIGNED_BYTE, // Òèï äàííûõ
|
||||
texDataArray[i].data.data() // Óêàçàòåëü íà äàííûå
|
||||
);
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
// Ñíèìàåì ïðèâÿçêó äëÿ ÷èñòîòû
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
glDeleteTextures(1, &texID);
|
||||
texID = 0;
|
||||
}
|
||||
|
||||
GLuint Texture::getTexID()
|
||||
{
|
||||
return texID;
|
||||
}
|
||||
|
||||
size_t Texture::getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
size_t Texture::getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TextureDataStruct CreateTextureDataFromBmp24(const std::string& fullFileName, const std::string& ZIPFileName)
|
||||
{
|
||||
|
||||
TextureDataStruct texData;
|
||||
std::vector<char> fileArr;
|
||||
|
||||
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
||||
|
||||
size_t fileSize = fileArr.size();
|
||||
|
||||
if (fileSize < 22)
|
||||
{
|
||||
throw std::runtime_error("File is too short or not correct!");
|
||||
}
|
||||
|
||||
//This refers to BITMAPV5HEADER
|
||||
texData.width = *reinterpret_cast<uint32_t*>(&fileArr[18]);
|
||||
texData.height = *reinterpret_cast<uint32_t*>(&fileArr[22]);
|
||||
|
||||
texData.bitSize = TextureDataStruct::BS_24BIT;
|
||||
|
||||
size_t dataSize = texData.width * texData.height * 3;
|
||||
|
||||
texData.data.resize(dataSize);
|
||||
|
||||
size_t pos = *reinterpret_cast<uint32_t*>(&fileArr[10]);
|
||||
size_t x = 0;
|
||||
|
||||
for (size_t i = 0; i < texData.width; i++)
|
||||
for (size_t j = 0; j < texData.height; j++)
|
||||
{
|
||||
|
||||
if (pos + 3 > fileSize)
|
||||
{
|
||||
throw std::runtime_error("File is too short!");
|
||||
}
|
||||
|
||||
|
||||
x = (i * texData.height + j) + (i * texData.height + j) + (i * texData.height + j);
|
||||
|
||||
texData.data[x + 2] = fileArr[pos++];
|
||||
texData.data[x + 1] = fileArr[pos++];
|
||||
texData.data[x + 0] = fileArr[pos++];
|
||||
|
||||
}
|
||||
|
||||
return texData;
|
||||
}
|
||||
|
||||
TextureDataStruct CreateTextureDataFromBmp32(const std::string& fullFileName, const std::string& ZIPFileName)
|
||||
{
|
||||
|
||||
TextureDataStruct texData;
|
||||
std::vector<char> fileArr;
|
||||
|
||||
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
||||
|
||||
size_t fileSize = fileArr.size();
|
||||
|
||||
if (fileSize < 22)
|
||||
{
|
||||
throw std::runtime_error("File is too short or not correct!");
|
||||
}
|
||||
|
||||
//This refers to BITMAPV5HEADER
|
||||
texData.width = *reinterpret_cast<uint32_t*>(&fileArr[18]);
|
||||
texData.height = *reinterpret_cast<uint32_t*>(&fileArr[22]);
|
||||
|
||||
texData.bitSize = TextureDataStruct::BS_32BIT;
|
||||
|
||||
size_t dataSize = texData.width * texData.height * 4;
|
||||
|
||||
texData.data.resize(dataSize);
|
||||
|
||||
size_t pos = *reinterpret_cast<uint32_t*>(&fileArr[10]);
|
||||
size_t x = 0;
|
||||
|
||||
for (size_t i = 0; i < texData.width; i++)
|
||||
for (size_t j = 0; j < texData.height; j++)
|
||||
{
|
||||
|
||||
if (pos + 4 > fileSize)
|
||||
{
|
||||
throw std::runtime_error("File is too short!");
|
||||
}
|
||||
|
||||
|
||||
x = (i * texData.height + j) + (i * texData.height + j) + (i * texData.height + j) + (i * texData.height + j);
|
||||
|
||||
texData.data[x + 2] = fileArr[pos++];
|
||||
texData.data[x + 1] = fileArr[pos++];
|
||||
texData.data[x + 0] = fileArr[pos++];
|
||||
texData.data[x + 3] = fileArr[pos++];
|
||||
|
||||
}
|
||||
|
||||
return texData;
|
||||
}
|
||||
|
||||
#ifdef PNG_ENABLED
|
||||
|
||||
// Ñòðóêòóðà äëÿ õðàíåíèÿ äàííûõ î ôàéëå/ìàññèâå è òåêóùåé ïîçèöèè ÷òåíèÿ
|
||||
struct png_data_t {
|
||||
const char* data;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
// Ïîëüçîâàòåëüñêàÿ ôóíêöèÿ ÷òåíèÿ äëÿ libpng
|
||||
// 'png_ptr' - óêàçàòåëü íà ñòðóêòóðó png
|
||||
// 'out_ptr' - êóäà çàïèñûâàòü ïðî÷èòàííûå äàííûå
|
||||
// 'bytes_to_read' - ñêîëüêî áàéò íóæíî ïðî÷èòàòü
|
||||
void user_read_data(png_structp png_ptr, png_bytep out_ptr, png_size_t bytes_to_read) {
|
||||
// Ïîëó÷àåì óêàçàòåëü íà íàøó ñòðóêòóðó png_data_t, êîòîðóþ ìû óñòàíîâèëè ñ ïîìîùüþ png_set_read_fn
|
||||
png_data_t* data = (png_data_t*)png_get_io_ptr(png_ptr);
|
||||
|
||||
if (data->offset + bytes_to_read > data->size) {
|
||||
// Ïîïûòêà ïðî÷èòàòü áîëüøå, ÷åì åñòü â ìàññèâå.
|
||||
// Âìåñòî âûçîâà ñòàíäàðòíîé îøèáêè, ìû ìîæåì ïðîñòî ïðî÷èòàòü îñòàòîê èëè âûçâàòü îøèáêó.
|
||||
//  ýòîì ñëó÷àå ìû âûçîâåì îøèáêó libpng.
|
||||
png_error(png_ptr, "PNG Read Error: Attempted to read past end of data buffer.");
|
||||
bytes_to_read = data->size - data->offset; // Óñòàíàâëèâàåì, ÷òîáû ïðî÷èòàòü îñòàâøååñÿ
|
||||
}
|
||||
|
||||
// Êîïèðóåì äàííûå èç íàøåãî ìàññèâà â áóôåð libpng
|
||||
std::memcpy(out_ptr, data->data + data->offset, bytes_to_read);
|
||||
|
||||
// Îáíîâëÿåì ñìåùåíèå
|
||||
data->offset += bytes_to_read;
|
||||
}
|
||||
|
||||
// Ïîëüçîâàòåëüñêàÿ ôóíêöèÿ ïðåäóïðåæäåíèé (ïî æåëàíèþ, ìîæíî èñïîëüçîâàòü nullptr)
|
||||
void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
|
||||
// Çäåñü ìîæíî ðåàëèçîâàòü ëîãèðîâàíèå ïðåäóïðåæäåíèé
|
||||
//throw std::runtime_error();
|
||||
std::cout << "PNG Warning: " << warning_msg << std::endl;
|
||||
}
|
||||
|
||||
// Ïîëüçîâàòåëüñêàÿ ôóíêöèÿ îøèáîê (îáÿçàòåëüíà äëÿ setjmp)
|
||||
void user_error_fn(png_structp png_ptr, png_const_charp error_msg) {
|
||||
// Çäåñü ìîæíî ðåàëèçîâàòü ëîãèðîâàíèå îøèáîê
|
||||
std::cout << "PNG Error: " << error_msg << std::endl;
|
||||
// Îáÿçàòåëüíî âûçûâàåì longjmp äëÿ âûõîäà èç ïðîöåññà ÷òåíèÿ/çàïèñè PNG
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
TextureDataStruct CreateTextureDataFromPng(const std::vector<char>& fileArr)
|
||||
{
|
||||
TextureDataStruct texData;
|
||||
|
||||
// Ñòðóêòóðà äëÿ óïðàâëåíèÿ ÷òåíèåì èç ìàññèâà
|
||||
png_data_t png_data = { fileArr.data(), fileArr.size(), 0 };
|
||||
|
||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (!png) {
|
||||
throw std::runtime_error("Could not create PNG read structure");
|
||||
}
|
||||
|
||||
png_infop info = png_create_info_struct(png);
|
||||
if (!info) {
|
||||
png_destroy_read_struct(&png, nullptr, nullptr);
|
||||
throw std::runtime_error("Could not create PNG info structure");
|
||||
}
|
||||
|
||||
// === Óñòàíîâêà ïîëüçîâàòåëüñêèõ ôóíêöèé ÷òåíèÿ è îáðàáîòêè îøèáîê ===
|
||||
// 1. Óñòàíîâêà îáðàáîò÷èêà îøèáîê è longjmp
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_read_struct(&png, &info, nullptr);
|
||||
throw std::runtime_error("Error during PNG read (longjmp was executed)");
|
||||
}
|
||||
|
||||
// 2. Óñòàíîâêà ïîëüçîâàòåëüñêèõ ôóíêöèé äëÿ îáðàáîòêè îøèáîê è ïðåäóïðåæäåíèé
|
||||
// Âìåñòî nullptr â error_ptr è warning_ptr ìîæíî ïåðåäàòü óêàçàòåëü íà ñâîþ ñòðóêòóðó äàííûõ, åñëè íåîáõîäèìî
|
||||
png_set_error_fn(png, nullptr, user_error_fn, user_warning_fn);
|
||||
|
||||
// 3. Óñòàíîâêà ïîëüçîâàòåëüñêîé ôóíêöèè ÷òåíèÿ è ïåðåäà÷à åé íàøåé ñòðóêòóðû png_data
|
||||
png_set_read_fn(png, &png_data, user_read_data);
|
||||
// ===================================================================
|
||||
|
||||
png_read_info(png, info);
|
||||
|
||||
texData.width = png_get_image_width(png, info);
|
||||
texData.height = png_get_image_height(png, info);
|
||||
png_byte color_type = png_get_color_type(png, info);
|
||||
png_byte bit_depth = png_get_bit_depth(png, info);
|
||||
|
||||
// === Áëîê ïðåîáðàçîâàíèé (îñòàâëåí áåç èçìåíåíèé) ===
|
||||
if (bit_depth == 16)
|
||||
png_set_strip_16(png);
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
png_set_palette_to_rgb(png);
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||
png_set_expand_gray_1_2_4_to_8(png);
|
||||
|
||||
if (png_get_valid(png, info, PNG_INFO_tRNS))
|
||||
png_set_tRNS_to_alpha(png);
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_RGB ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png);
|
||||
|
||||
png_read_update_info(png, info);
|
||||
// ====================================================
|
||||
|
||||
// === ×òåíèå ïèêñåëåé (îñòàâëåí áåç èçìåíåíèé) ===
|
||||
png_bytep* row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * texData.height);
|
||||
for (int y = 0; y < texData.height; y++) {
|
||||
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png, info));
|
||||
}
|
||||
|
||||
png_read_image(png, row_pointers);
|
||||
|
||||
bool has_alpha = (color_type & PNG_COLOR_MASK_ALPHA) || (png_get_valid(png, info, PNG_INFO_tRNS));
|
||||
|
||||
size_t dataSize;
|
||||
|
||||
if (has_alpha)
|
||||
{
|
||||
texData.bitSize = TextureDataStruct::BS_32BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
texData.bitSize = TextureDataStruct::BS_24BIT;
|
||||
}
|
||||
|
||||
int channels = has_alpha ? 4 : 3;
|
||||
|
||||
dataSize = texData.width * texData.height * channels;
|
||||
texData.data.resize(dataSize);
|
||||
|
||||
|
||||
for (int y = texData.height - 1; y >= 0; y--) {
|
||||
png_bytep row = row_pointers[texData.height - 1 - y];
|
||||
for (int x = 0; x < texData.width; x++) {
|
||||
png_bytep px = &(row[x * 4]);
|
||||
texData.data[(y * texData.width + x) * channels + 0] = px[0]; // R
|
||||
texData.data[(y * texData.width + x) * channels + 1] = px[1]; // G
|
||||
texData.data[(y * texData.width + x) * channels + 2] = px[2]; // B
|
||||
if (has_alpha) {
|
||||
texData.data[(y * texData.width + x) * channels + 3] = px[3]; // A
|
||||
}
|
||||
}
|
||||
free(row_pointers[texData.height - 1 - y]);
|
||||
}
|
||||
free(row_pointers);
|
||||
// ==================================================
|
||||
|
||||
png_destroy_read_struct(&png, &info, nullptr);
|
||||
|
||||
return texData;
|
||||
}
|
||||
|
||||
|
||||
TextureDataStruct CreateTextureDataFromPng(const std::string& fullFileName, const std::string& ZIPFileName)
|
||||
{
|
||||
std::vector<char> fileArr;
|
||||
|
||||
fileArr = !ZIPFileName.empty() ? readFileFromZIP(fullFileName, ZIPFileName) : readFile(fullFileName);
|
||||
|
||||
if (fileArr.empty()) {
|
||||
throw std::runtime_error("Could not read file data into memory");
|
||||
}
|
||||
|
||||
// Âûçûâàåì íîâóþ ôóíêöèþ, êîòîðàÿ ðàáîòàåò ñ ìàññèâîì áàéò
|
||||
return CreateTextureDataFromPng(fileArr);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "OpenGlExtensions.h"
|
||||
#include "utils/Utils.h"
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#define PNG_ENABLED
|
||||
#endif
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
struct TextureDataStruct
|
||||
{
|
||||
size_t width;
|
||||
size_t height;
|
||||
std::vector<char> data;
|
||||
enum BitSize {
|
||||
BS_24BIT,
|
||||
BS_32BIT
|
||||
};
|
||||
|
||||
BitSize bitSize;
|
||||
};
|
||||
|
||||
class Texture
|
||||
{
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
GLuint texID = 0;
|
||||
|
||||
public:
|
||||
|
||||
Texture(const TextureDataStruct& texData);
|
||||
|
||||
//Cubemap texture:
|
||||
Texture(const std::array<TextureDataStruct, 6>& texDataArray);
|
||||
|
||||
~Texture();
|
||||
|
||||
GLuint getTexID();
|
||||
|
||||
size_t getWidth();
|
||||
size_t getHeight();
|
||||
|
||||
|
||||
};
|
||||
|
||||
TextureDataStruct CreateTextureDataFromBmp24(const std::string& fullFileName, const std::string& ZIPFileName="");
|
||||
TextureDataStruct CreateTextureDataFromBmp32(const std::string& fullFileName, const std::string& ZIPFileName="");
|
||||
#ifdef PNG_ENABLED
|
||||
TextureDataStruct CreateTextureDataFromPng(const std::string& fullFileName, const std::string& ZIPFileName = "");
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "Perlin.h"
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
PerlinNoise::PerlinNoise() {
|
||||
p.resize(256);
|
||||
std::iota(p.begin(), p.end(), 0);
|
||||
// Ïåðåìåøèâàåì äëÿ ñëó÷àéíîñòè (ìîæíî çàäàòü seed)
|
||||
std::default_random_engine engine(77777);
|
||||
std::shuffle(p.begin(), p.end(), engine);
|
||||
p.insert(p.end(), p.begin(), p.end()); // Äóáëèðóåì äëÿ ïåðåïîëíåíèÿ
|
||||
}
|
||||
|
||||
PerlinNoise::PerlinNoise(uint64_t seed) {
|
||||
p.resize(256);
|
||||
std::iota(p.begin(), p.end(), 0);
|
||||
// Ïåðåìåøèâàåì äëÿ ñëó÷àéíîñòè (èñïîëüçóåì ïåðåäàííûé seed)
|
||||
std::default_random_engine engine(static_cast<unsigned int>(seed));
|
||||
std::shuffle(p.begin(), p.end(), engine);
|
||||
p.insert(p.end(), p.begin(), p.end()); // Äóáëèðóåì äëÿ ïåðåïîëíåíèÿ
|
||||
}
|
||||
|
||||
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||
|
||||
float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); }
|
||||
|
||||
float PerlinNoise::grad(int hash, float x, float y, float z) {
|
||||
int h = hash & 15;
|
||||
float u = h < 8 ? x : y;
|
||||
float v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
|
||||
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||
}
|
||||
|
||||
float PerlinNoise::noise(float x, float y, float z) {
|
||||
int X = (int)floor(x) & 255;
|
||||
int Y = (int)floor(y) & 255;
|
||||
int Z = (int)floor(z) & 255;
|
||||
|
||||
x -= floor(x);
|
||||
y -= floor(y);
|
||||
z -= floor(z);
|
||||
|
||||
float u = fade(x);
|
||||
float v = fade(y);
|
||||
float w = fade(z);
|
||||
|
||||
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z;
|
||||
int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
|
||||
|
||||
return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)),
|
||||
lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))),
|
||||
lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)),
|
||||
lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1))));
|
||||
}
|
||||
|
||||
float PerlinNoise::getSurfaceHeight(Vector3f pos, float noiseCoeff) {
|
||||
// ×àñòîòà øóìà (÷åì áîëüøå, òåì áîëüøå "õîëìîâ")
|
||||
float frequency = 7.0f;
|
||||
|
||||
// Ïîëó÷àåì çíà÷åíèå øóìà (îáû÷íî îò -1 äî 1)
|
||||
float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency);
|
||||
|
||||
// Ìàñøòàáèðóåì: õîòèì îòêëîíåíèå îò 1.0 äî 1.1 (ïðèìåðíî)
|
||||
float height = 1.0f + (noiseValue * noiseCoeff);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "utils/ZLMath.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class PerlinNoise {
|
||||
std::vector<int> p;
|
||||
public:
|
||||
PerlinNoise();
|
||||
PerlinNoise(uint64_t seed);
|
||||
|
||||
float fade(float t);
|
||||
float lerp(float t, float a, float b);
|
||||
float grad(int hash, float x, float y, float z);
|
||||
|
||||
float noise(float x, float y, float z);
|
||||
|
||||
float getSurfaceHeight(Vector3f pos, float noiseCoeff);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "utils/Utils.h"
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <zip.h>
|
||||
#endif
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
std::string readTextFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream f(filename);
|
||||
|
||||
std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::vector<char> readFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
|
||||
file.unsetf(std::ios::skipws);
|
||||
|
||||
std::streampos fileSize;
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> vec;
|
||||
vec.reserve(fileSize);
|
||||
|
||||
vec.insert(vec.begin(),
|
||||
std::istream_iterator<char>(file),
|
||||
std::istream_iterator<char>());
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
|
||||
#ifdef EMSCRIPTEN
|
||||
const std::string zipPath = zipfilename;
|
||||
int zipErr;
|
||||
zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &zipErr);
|
||||
if (!archive) {
|
||||
throw std::runtime_error("Ошибка открытия ZIP: " + zipPath);
|
||||
}
|
||||
|
||||
std::string cleanFilename = filename;
|
||||
if (cleanFilename.rfind("./", 0) == 0) {
|
||||
cleanFilename = cleanFilename.substr(2);
|
||||
}
|
||||
|
||||
std::cout << "Ищем в ZIP: " << cleanFilename << std::endl;
|
||||
|
||||
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
||||
if (!zipFile) {
|
||||
zip_close(archive);
|
||||
throw std::runtime_error("Файл не найден в ZIP: " + cleanFilename);
|
||||
}
|
||||
|
||||
zip_stat_t fileStat;
|
||||
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0) {
|
||||
zip_fclose(zipFile);
|
||||
zip_close(archive);
|
||||
throw std::runtime_error("Ошибка чтения ZIP-статистики.");
|
||||
}
|
||||
|
||||
std::vector<char> fileData;
|
||||
fileData.resize(fileStat.size);
|
||||
|
||||
zip_fread(zipFile, fileData.data(), fileData.size());
|
||||
|
||||
zip_fclose(zipFile);
|
||||
zip_close(archive);
|
||||
|
||||
return fileData;
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
bool findString(const char* in, char* list)
|
||||
{
|
||||
size_t thisLength = strlen(in);
|
||||
while (*list != 0)
|
||||
{
|
||||
size_t length = strcspn(list, " ");
|
||||
|
||||
if (thisLength == length)
|
||||
if (!strncmp(in, list, length))
|
||||
return true;
|
||||
|
||||
list += length;
|
||||
list += 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
namespace ZL
|
||||
{
|
||||
std::string readTextFile(const std::string& filename);
|
||||
|
||||
std::vector<char> readFile(const std::string& filename);
|
||||
|
||||
std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename);
|
||||
|
||||
bool findString(const char* in, char* list);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,799 @@
|
||||
#include "utils/ZLMath.h"
|
||||
|
||||
#include <exception>
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
Vector2f operator+(const Vector2f& x, const Vector2f& y)
|
||||
{
|
||||
Vector2f result;
|
||||
|
||||
result.v[0] = x.v[0] + y.v[0];
|
||||
result.v[1] = x.v[1] + y.v[1];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector2f operator-(const Vector2f& x, const Vector2f& y)
|
||||
{
|
||||
Vector2f result;
|
||||
|
||||
result.v[0] = x.v[0] - y.v[0];
|
||||
result.v[1] = x.v[1] - y.v[1];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3f operator+(const Vector3f& x, const Vector3f& y)
|
||||
{
|
||||
Vector3f result;
|
||||
|
||||
result.v[0] = x.v[0] + y.v[0];
|
||||
result.v[1] = x.v[1] + y.v[1];
|
||||
result.v[2] = x.v[2] + y.v[2];
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3f operator-(const Vector3f& x, const Vector3f& y)
|
||||
{
|
||||
Vector3f result;
|
||||
|
||||
result.v[0] = x.v[0] - y.v[0];
|
||||
result.v[1] = x.v[1] - y.v[1];
|
||||
result.v[2] = x.v[2] - y.v[2];
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3f operator-(const Vector3f& x)
|
||||
{
|
||||
Vector3f result;
|
||||
|
||||
result.v[0] = -x.v[0];
|
||||
result.v[1] = -x.v[1];
|
||||
result.v[2] = -x.v[2];
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4f operator+(const Vector4f& x, const Vector4f& y)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = x.v[0] + y.v[0];
|
||||
result.v[1] = x.v[1] + y.v[1];
|
||||
result.v[2] = x.v[2] + y.v[2];
|
||||
result.v[3] = x.v[3] + y.v[3];
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4f operator-(const Vector4f& x, const Vector4f& y)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = x.v[0] - y.v[0];
|
||||
result.v[1] = x.v[1] - y.v[1];
|
||||
result.v[2] = x.v[2] - y.v[2];
|
||||
result.v[3] = x.v[3] - y.v[3];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Matrix3f Matrix3f::Identity()
|
||||
{
|
||||
Matrix3f r;
|
||||
|
||||
r.m[0] = 1.f;
|
||||
r.m[1] = 0.f;
|
||||
r.m[2] = 0.f;
|
||||
|
||||
r.m[3] = 0.f;
|
||||
r.m[4] = 1.f;
|
||||
r.m[5] = 0.f;
|
||||
|
||||
r.m[6] = 0.f;
|
||||
r.m[7] = 0.f;
|
||||
r.m[8] = 1.f;
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
Matrix4f Matrix4f::Identity()
|
||||
{
|
||||
Matrix4f r;
|
||||
|
||||
r.m[0] = 1.f;
|
||||
r.m[1] = 0.f;
|
||||
r.m[2] = 0.f;
|
||||
r.m[3] = 0.f;
|
||||
|
||||
r.m[4] = 0.f;
|
||||
r.m[5] = 1.f;
|
||||
r.m[6] = 0.f;
|
||||
r.m[7] = 0.f;
|
||||
|
||||
r.m[8] = 0.f;
|
||||
r.m[9] = 0.f;
|
||||
r.m[10] = 1.f;
|
||||
r.m[11] = 0.f;
|
||||
|
||||
r.m[12] = 0.f;
|
||||
r.m[13] = 0.f;
|
||||
r.m[14] = 0.f;
|
||||
r.m[15] = 1.f;
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
Matrix4f operator*(const Matrix4f& m1, const Matrix4f& m2)
|
||||
{
|
||||
Matrix4f r;
|
||||
|
||||
r.m[0] = m1.m[0] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[8] * m2.m[2] + m1.m[12] * m2.m[3];
|
||||
r.m[1] = m1.m[1] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[9] * m2.m[2] + m1.m[13] * m2.m[3];
|
||||
r.m[2] = m1.m[2] * m2.m[0] + m1.m[6] * m2.m[1] + m1.m[10] * m2.m[2] + m1.m[14] * m2.m[3];
|
||||
r.m[3] = m1.m[3] * m2.m[0] + m1.m[7] * m2.m[1] + m1.m[11] * m2.m[2] + m1.m[15] * m2.m[3];
|
||||
|
||||
r.m[4] = m1.m[0] * m2.m[4] + m1.m[4] * m2.m[5] + m1.m[8] * m2.m[6] + m1.m[12] * m2.m[7];
|
||||
r.m[5] = m1.m[1] * m2.m[4] + m1.m[5] * m2.m[5] + m1.m[9] * m2.m[6] + m1.m[13] * m2.m[7];
|
||||
r.m[6] = m1.m[2] * m2.m[4] + m1.m[6] * m2.m[5] + m1.m[10] * m2.m[6] + m1.m[14] * m2.m[7];
|
||||
r.m[7] = m1.m[3] * m2.m[4] + m1.m[7] * m2.m[5] + m1.m[11] * m2.m[6] + m1.m[15] * m2.m[7];
|
||||
|
||||
|
||||
r.m[8] = m1.m[0] * m2.m[8] + m1.m[4] * m2.m[9] + m1.m[8] * m2.m[10] + m1.m[12] * m2.m[11];
|
||||
r.m[9] = m1.m[1] * m2.m[8] + m1.m[5] * m2.m[9] + m1.m[9] * m2.m[10] + m1.m[13] * m2.m[11];
|
||||
r.m[10] = m1.m[2] * m2.m[8] + m1.m[6] * m2.m[9] + m1.m[10] * m2.m[10] + m1.m[14] * m2.m[11];
|
||||
r.m[11] = m1.m[3] * m2.m[8] + m1.m[7] * m2.m[9] + m1.m[11] * m2.m[10] + m1.m[15] * m2.m[11];
|
||||
|
||||
r.m[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8] * m2.m[14] + m1.m[12] * m2.m[15];
|
||||
r.m[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9] * m2.m[14] + m1.m[13] * m2.m[15];
|
||||
r.m[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
|
||||
r.m[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix4f MakeOrthoMatrix(float width, float height, float zNear, float zFar)
|
||||
{
|
||||
float depthRange = zFar - zNear;
|
||||
|
||||
if (depthRange <= 0)
|
||||
{
|
||||
throw std::runtime_error("zFar must be greater than zNear");
|
||||
}
|
||||
|
||||
Matrix4f r;
|
||||
|
||||
r.m[0] = 2.f / width;
|
||||
r.m[1] = 0;
|
||||
r.m[2] = 0;
|
||||
r.m[3] = 0;
|
||||
|
||||
r.m[4] = 0;
|
||||
r.m[5] = 2.f / height;
|
||||
r.m[6] = 0;
|
||||
r.m[7] = 0;
|
||||
|
||||
r.m[8] = 0;
|
||||
r.m[9] = 0;
|
||||
r.m[10] = -1 / depthRange;
|
||||
r.m[11] = 0;
|
||||
|
||||
r.m[12] = -1;
|
||||
r.m[13] = -1;
|
||||
r.m[14] = zNear / depthRange;
|
||||
r.m[15] = 1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix4f MakeOrthoMatrix(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar)
|
||||
{
|
||||
float width = xmax - xmin;
|
||||
float height = ymax - ymin;
|
||||
float depthRange = zFar - zNear;
|
||||
|
||||
if (width <= 0 || height <= 0 || depthRange <= 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid dimensions for orthogonal matrix");
|
||||
}
|
||||
|
||||
Matrix4f r;
|
||||
|
||||
// Масштабирование
|
||||
r.m[0] = 2.f / width;
|
||||
r.m[5] = 2.f / height;
|
||||
r.m[10] = -1.f / depthRange;
|
||||
|
||||
// Обнуление неиспользуемых компонентов
|
||||
r.m[1] = r.m[2] = r.m[3] = 0;
|
||||
r.m[4] = r.m[6] = r.m[7] = 0;
|
||||
r.m[8] = r.m[9] = r.m[11] = 0;
|
||||
|
||||
// Трансляция (смещение)
|
||||
r.m[12] = -(xmax + xmin) / width;
|
||||
r.m[13] = -(ymax + ymin) / height;
|
||||
r.m[14] = zNear / depthRange;
|
||||
r.m[15] = 1.f;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix4f MakePerspectiveMatrix(float fovY, float aspectRatio, float zNear, float zFar)
|
||||
{
|
||||
float tanHalfFovy = tan(fovY / 2.f);
|
||||
Matrix4f r;
|
||||
|
||||
if (zNear >= zFar || aspectRatio == 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid perspective parameters");
|
||||
}
|
||||
|
||||
r.m[0] = 1.f / (aspectRatio * tanHalfFovy);
|
||||
r.m[1] = 0;
|
||||
r.m[2] = 0;
|
||||
r.m[3] = 0;
|
||||
|
||||
r.m[4] = 0;
|
||||
r.m[5] = 1.f / (tanHalfFovy);
|
||||
r.m[6] = 0;
|
||||
r.m[7] = 0;
|
||||
|
||||
r.m[8] = 0;
|
||||
r.m[9] = 0;
|
||||
r.m[10] = -(zFar + zNear) / (zFar - zNear);
|
||||
r.m[11] = -1;
|
||||
|
||||
r.m[12] = 0;
|
||||
r.m[13] = 0;
|
||||
r.m[14] = -(2.f * zFar * zNear) / (zFar - zNear);
|
||||
r.m[15] = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f QuatToMatrix(const Vector4f& q)
|
||||
{
|
||||
Matrix3f m;
|
||||
|
||||
float wx, wy, wz, xx, yy, yz, xy, xz, zz, s, x2, y2, z2;
|
||||
|
||||
s = 2.0f / (q.v[0] * q.v[0] + q.v[1] * q.v[1] + q.v[2] * q.v[2] + q.v[3] * q.v[3]);
|
||||
|
||||
|
||||
x2 = q.v[0] * s;
|
||||
y2 = q.v[1] * s;
|
||||
z2 = q.v[2] * s;
|
||||
|
||||
wx = q.v[3] * x2; wy = q.v[3] * y2; wz = q.v[3] * z2;
|
||||
xx = q.v[0] * x2; xy = q.v[1] * x2; xz = q.v[2] * x2;
|
||||
yy = q.v[1] * y2; yz = q.v[2] * y2;
|
||||
zz = q.v[2] * z2;
|
||||
|
||||
m.m[0] = 1.0f - (yy + zz);
|
||||
m.m[1] = xy + wz;
|
||||
m.m[2] = xz - wy;
|
||||
|
||||
m.m[3] = xy - wz;
|
||||
m.m[4] = 1.0f - (xx + zz);
|
||||
m.m[5] = yz + wx;
|
||||
|
||||
m.m[6] = xz + wy;
|
||||
m.m[7] = yz - wx;
|
||||
m.m[8] = 1.0f - (xx + yy);
|
||||
|
||||
return m;
|
||||
|
||||
}
|
||||
|
||||
Vector4f MatrixToQuat(const Matrix3f& m)
|
||||
{
|
||||
Vector4f r;
|
||||
float trace = m.m[0] + m.m[4] + m.m[8];
|
||||
|
||||
if (trace > 0)
|
||||
{
|
||||
float s = 0.5f / sqrtf(trace + 1.0f);
|
||||
r.v[3] = 0.25f / s;
|
||||
r.v[0] = (m.m[5] - m.m[7]) * s;
|
||||
r.v[1] = (m.m[6] - m.m[2]) * s;
|
||||
r.v[2] = (m.m[1] - m.m[3]) * s;
|
||||
}
|
||||
else if (m.m[0] > m.m[4] && m.m[0] > m.m[8])
|
||||
{
|
||||
float s = 2.0f * sqrtf(1.0f + m.m[0] - m.m[4] - m.m[8]);
|
||||
r.v[3] = (m.m[5] - m.m[7]) / s;
|
||||
r.v[0] = 0.25f * s;
|
||||
r.v[1] = (m.m[1] + m.m[3]) / s;
|
||||
r.v[2] = (m.m[6] + m.m[2]) / s;
|
||||
}
|
||||
else if (m.m[4] > m.m[8])
|
||||
{
|
||||
float s = 2.0f * sqrtf(1.0f + m.m[4] - m.m[0] - m.m[8]);
|
||||
r.v[3] = (m.m[6] - m.m[2]) / s;
|
||||
r.v[0] = (m.m[1] + m.m[3]) / s;
|
||||
r.v[1] = 0.25f * s;
|
||||
r.v[2] = (m.m[5] + m.m[7]) / s;
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = 2.0f * sqrtf(1.0f + m.m[8] - m.m[0] - m.m[4]);
|
||||
r.v[3] = (m.m[1] - m.m[3]) / s;
|
||||
r.v[0] = (m.m[6] + m.m[2]) / s;
|
||||
r.v[1] = (m.m[5] + m.m[7]) / s;
|
||||
r.v[2] = 0.25f * s;
|
||||
}
|
||||
|
||||
return r.normalized();
|
||||
}
|
||||
|
||||
Vector4f QuatFromRotateAroundX(float angle)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = sinf(angle * 0.5f);
|
||||
result.v[1] = 0.f;
|
||||
result.v[2] = 0.f;
|
||||
result.v[3] = cosf(angle * 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4f QuatFromRotateAroundY(float angle)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = 0.f;
|
||||
result.v[1] = sinf(angle * 0.5f);
|
||||
result.v[2] = 0.f;
|
||||
result.v[3] = cosf(angle * 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4f QuatFromRotateAroundZ(float angle)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = 0.f;
|
||||
result.v[1] = 0.f;
|
||||
result.v[2] = sinf(angle * 0.5f);
|
||||
result.v[3] = cosf(angle * 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Matrix3f TransposeMatrix(const Matrix3f& m)
|
||||
{
|
||||
Matrix3f r;
|
||||
r.m[0] = m.m[0];
|
||||
r.m[1] = m.m[3];
|
||||
r.m[2] = m.m[6];
|
||||
r.m[3] = m.m[1];
|
||||
r.m[4] = m.m[4];
|
||||
r.m[5] = m.m[7];
|
||||
r.m[6] = m.m[2];
|
||||
r.m[7] = m.m[5];
|
||||
r.m[8] = m.m[8];
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f InverseMatrix(const Matrix3f& m)
|
||||
{
|
||||
float d;
|
||||
Matrix3f r;
|
||||
|
||||
d = m.m[0] * (m.m[4] * m.m[8] - m.m[5] * m.m[7]);
|
||||
d -= m.m[1] * (m.m[3] * m.m[8] - m.m[6] * m.m[5]);
|
||||
d += m.m[2] * (m.m[3] * m.m[7] - m.m[6] * m.m[4]);
|
||||
|
||||
if (fabs(d) < 0.01f)
|
||||
{
|
||||
throw std::runtime_error("Error: matrix cannot be inversed!");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
r.m[0] = (m.m[4] * m.m[8] - m.m[5] * m.m[7]) / d;
|
||||
r.m[1] = -(m.m[1] * m.m[8] - m.m[2] * m.m[7]) / d;
|
||||
r.m[2] = (m.m[1] * m.m[5] - m.m[2] * m.m[4]) / d;
|
||||
|
||||
r.m[3] = -(m.m[3] * m.m[8] - m.m[5] * m.m[6]) / d;
|
||||
r.m[4] = (m.m[0] * m.m[8] - m.m[2] * m.m[6]) / d;
|
||||
r.m[5] = -(m.m[0] * m.m[5] - m.m[2] * m.m[3]) / d;
|
||||
|
||||
r.m[6] = (m.m[3] * m.m[7] - m.m[6] * m.m[4]) / d;
|
||||
r.m[7] = -(m.m[0] * m.m[7] - m.m[6] * m.m[1]) / d;
|
||||
r.m[8] = (m.m[0] * m.m[4] - m.m[1] * m.m[3]) / d;
|
||||
|
||||
};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Matrix4f InverseMatrix(const Matrix4f& mat)
|
||||
{
|
||||
Matrix4f inv;
|
||||
float det;
|
||||
|
||||
inv.m[0] = mat.m[5] * mat.m[10] * mat.m[15] -
|
||||
mat.m[5] * mat.m[11] * mat.m[14] -
|
||||
mat.m[9] * mat.m[6] * mat.m[15] +
|
||||
mat.m[9] * mat.m[7] * mat.m[14] +
|
||||
mat.m[13] * mat.m[6] * mat.m[11] -
|
||||
mat.m[13] * mat.m[7] * mat.m[10];
|
||||
|
||||
inv.m[4] = -mat.m[4] * mat.m[10] * mat.m[15] +
|
||||
mat.m[4] * mat.m[11] * mat.m[14] +
|
||||
mat.m[8] * mat.m[6] * mat.m[15] -
|
||||
mat.m[8] * mat.m[7] * mat.m[14] -
|
||||
mat.m[12] * mat.m[6] * mat.m[11] +
|
||||
mat.m[12] * mat.m[7] * mat.m[10];
|
||||
|
||||
inv.m[8] = mat.m[4] * mat.m[9] * mat.m[15] -
|
||||
mat.m[4] * mat.m[11] * mat.m[13] -
|
||||
mat.m[8] * mat.m[5] * mat.m[15] +
|
||||
mat.m[8] * mat.m[7] * mat.m[13] +
|
||||
mat.m[12] * mat.m[5] * mat.m[11] -
|
||||
mat.m[12] * mat.m[7] * mat.m[9];
|
||||
|
||||
inv.m[12] = -mat.m[4] * mat.m[9] * mat.m[14] +
|
||||
mat.m[4] * mat.m[10] * mat.m[13] +
|
||||
mat.m[8] * mat.m[5] * mat.m[14] -
|
||||
mat.m[8] * mat.m[6] * mat.m[13] -
|
||||
mat.m[12] * mat.m[5] * mat.m[10] +
|
||||
mat.m[12] * mat.m[6] * mat.m[9];
|
||||
|
||||
inv.m[1] = -mat.m[1] * mat.m[10] * mat.m[15] +
|
||||
mat.m[1] * mat.m[11] * mat.m[14] +
|
||||
mat.m[9] * mat.m[2] * mat.m[15] -
|
||||
mat.m[9] * mat.m[3] * mat.m[14] -
|
||||
mat.m[13] * mat.m[2] * mat.m[11] +
|
||||
mat.m[13] * mat.m[3] * mat.m[10];
|
||||
|
||||
inv.m[5] = mat.m[0] * mat.m[10] * mat.m[15] -
|
||||
mat.m[0] * mat.m[11] * mat.m[14] -
|
||||
mat.m[8] * mat.m[2] * mat.m[15] +
|
||||
mat.m[8] * mat.m[3] * mat.m[14] +
|
||||
mat.m[12] * mat.m[2] * mat.m[11] -
|
||||
mat.m[12] * mat.m[3] * mat.m[10];
|
||||
|
||||
inv.m[9] = -mat.m[0] * mat.m[9] * mat.m[15] +
|
||||
mat.m[0] * mat.m[11] * mat.m[13] +
|
||||
mat.m[8] * mat.m[1] * mat.m[15] -
|
||||
mat.m[8] * mat.m[3] * mat.m[13] -
|
||||
mat.m[12] * mat.m[1] * mat.m[11] +
|
||||
mat.m[12] * mat.m[3] * mat.m[9];
|
||||
|
||||
inv.m[13] = mat.m[0] * mat.m[9] * mat.m[14] -
|
||||
mat.m[0] * mat.m[10] * mat.m[13] -
|
||||
mat.m[8] * mat.m[1] * mat.m[14] +
|
||||
mat.m[8] * mat.m[2] * mat.m[13] +
|
||||
mat.m[12] * mat.m[1] * mat.m[10] -
|
||||
mat.m[12] * mat.m[2] * mat.m[9];
|
||||
|
||||
inv.m[2] = mat.m[1] * mat.m[6] * mat.m[15] -
|
||||
mat.m[1] * mat.m[7] * mat.m[14] -
|
||||
mat.m[5] * mat.m[2] * mat.m[15] +
|
||||
mat.m[5] * mat.m[3] * mat.m[14] +
|
||||
mat.m[13] * mat.m[2] * mat.m[7] -
|
||||
mat.m[13] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[6] = -mat.m[0] * mat.m[6] * mat.m[15] +
|
||||
mat.m[0] * mat.m[7] * mat.m[14] +
|
||||
mat.m[4] * mat.m[2] * mat.m[15] -
|
||||
mat.m[4] * mat.m[3] * mat.m[14] -
|
||||
mat.m[12] * mat.m[2] * mat.m[7] +
|
||||
mat.m[12] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[10] = mat.m[0] * mat.m[5] * mat.m[15] -
|
||||
mat.m[0] * mat.m[7] * mat.m[13] -
|
||||
mat.m[4] * mat.m[1] * mat.m[15] +
|
||||
mat.m[4] * mat.m[3] * mat.m[13] +
|
||||
mat.m[12] * mat.m[1] * mat.m[7] -
|
||||
mat.m[12] * mat.m[3] * mat.m[5];
|
||||
|
||||
inv.m[14] = -mat.m[0] * mat.m[5] * mat.m[14] +
|
||||
mat.m[0] * mat.m[6] * mat.m[13] +
|
||||
mat.m[4] * mat.m[1] * mat.m[14] -
|
||||
mat.m[4] * mat.m[2] * mat.m[13] -
|
||||
mat.m[12] * mat.m[1] * mat.m[6] +
|
||||
mat.m[12] * mat.m[2] * mat.m[5];
|
||||
|
||||
inv.m[3] = -mat.m[1] * mat.m[6] * mat.m[11] +
|
||||
mat.m[1] * mat.m[7] * mat.m[10] +
|
||||
mat.m[5] * mat.m[2] * mat.m[11] -
|
||||
mat.m[5] * mat.m[3] * mat.m[10] -
|
||||
mat.m[9] * mat.m[2] * mat.m[7] +
|
||||
mat.m[9] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[7] = mat.m[0] * mat.m[6] * mat.m[11] -
|
||||
mat.m[0] * mat.m[7] * mat.m[10] -
|
||||
mat.m[4] * mat.m[2] * mat.m[11] +
|
||||
mat.m[4] * mat.m[3] * mat.m[10] +
|
||||
mat.m[8] * mat.m[2] * mat.m[7] -
|
||||
mat.m[8] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[11] = -mat.m[0] * mat.m[5] * mat.m[11] +
|
||||
mat.m[0] * mat.m[7] * mat.m[9] +
|
||||
mat.m[4] * mat.m[1] * mat.m[11] -
|
||||
mat.m[4] * mat.m[3] * mat.m[9] -
|
||||
mat.m[8] * mat.m[1] * mat.m[7] +
|
||||
mat.m[8] * mat.m[3] * mat.m[5];
|
||||
|
||||
inv.m[15] = mat.m[0] * mat.m[5] * mat.m[10] -
|
||||
mat.m[0] * mat.m[6] * mat.m[9] -
|
||||
mat.m[4] * mat.m[1] * mat.m[10] +
|
||||
mat.m[4] * mat.m[2] * mat.m[9] +
|
||||
mat.m[8] * mat.m[1] * mat.m[6] -
|
||||
mat.m[8] * mat.m[2] * mat.m[5];
|
||||
|
||||
// ��������� �����������
|
||||
det = mat.m[0] * inv.m[0] + mat.m[1] * inv.m[4] + mat.m[2] * inv.m[8] + mat.m[3] * inv.m[12];
|
||||
|
||||
if (std::fabs(det) < 0.01f)
|
||||
{
|
||||
throw std::runtime_error("Error: matrix cannot be inversed!");
|
||||
}
|
||||
|
||||
// ����� ��� �������� �� �����������
|
||||
det = 1.0f / det;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
inv.m[i] *= det;
|
||||
}
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
Matrix3f CreateZRotationMatrix(float angle)
|
||||
{
|
||||
Matrix3f result = Matrix3f::Identity();
|
||||
|
||||
result.m[0] = cosf(angle);
|
||||
result.m[1] = -sinf(angle);
|
||||
result.m[3] = sinf(angle);
|
||||
result.m[4] = cosf(angle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Matrix4f MultMatrixMatrix(const Matrix4f& m1, const Matrix4f& m2)
|
||||
{
|
||||
Matrix4f rx;
|
||||
|
||||
rx.m[0] = m1.m[0] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[8] * m2.m[2] + m1.m[12] * m2.m[3];
|
||||
rx.m[1] = m1.m[1] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[9] * m2.m[2] + m1.m[13] * m2.m[3];
|
||||
rx.m[2] = m1.m[2] * m2.m[0] + m1.m[6] * m2.m[1] + m1.m[10] * m2.m[2] + m1.m[14] * m2.m[3];
|
||||
rx.m[3] = m1.m[3] * m2.m[0] + m1.m[7] * m2.m[1] + m1.m[11] * m2.m[2] + m1.m[15] * m2.m[3];
|
||||
|
||||
rx.m[4] = m1.m[0] * m2.m[4] + m1.m[4] * m2.m[5] + m1.m[8] * m2.m[6] + m1.m[12] * m2.m[7];
|
||||
rx.m[5] = m1.m[1] * m2.m[4] + m1.m[5] * m2.m[5] + m1.m[9] * m2.m[6] + m1.m[13] * m2.m[7];
|
||||
rx.m[6] = m1.m[2] * m2.m[4] + m1.m[6] * m2.m[5] + m1.m[10] * m2.m[6] + m1.m[14] * m2.m[7];
|
||||
rx.m[7] = m1.m[3] * m2.m[4] + m1.m[7] * m2.m[5] + m1.m[11] * m2.m[6] + m1.m[15] * m2.m[7];
|
||||
|
||||
|
||||
rx.m[8] = m1.m[0] * m2.m[8] + m1.m[4] * m2.m[9] + m1.m[8] * m2.m[10] + m1.m[12] * m2.m[11];
|
||||
rx.m[9] = m1.m[1] * m2.m[8] + m1.m[5] * m2.m[9] + m1.m[9] * m2.m[10] + m1.m[13] * m2.m[11];
|
||||
rx.m[10] = m1.m[2] * m2.m[8] + m1.m[6] * m2.m[9] + m1.m[10] * m2.m[10] + m1.m[14] * m2.m[11];
|
||||
rx.m[11] = m1.m[3] * m2.m[8] + m1.m[7] * m2.m[9] + m1.m[11] * m2.m[10] + m1.m[15] * m2.m[11];
|
||||
|
||||
rx.m[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8] * m2.m[14] + m1.m[12] * m2.m[15];
|
||||
rx.m[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9] * m2.m[14] + m1.m[13] * m2.m[15];
|
||||
rx.m[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
|
||||
rx.m[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
|
||||
|
||||
return rx;
|
||||
}
|
||||
|
||||
Matrix3f MultMatrixMatrix(const Matrix3f& m1, const Matrix3f& m2)
|
||||
{
|
||||
Matrix3f r;
|
||||
|
||||
r.m[0] = m1.m[0] * m2.m[0] + m1.m[3] * m2.m[1] + m1.m[6] * m2.m[2];
|
||||
r.m[1] = m1.m[1] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[7] * m2.m[2];
|
||||
r.m[2] = m1.m[2] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[8] * m2.m[2];
|
||||
|
||||
r.m[3] = m1.m[0] * m2.m[3] + m1.m[3] * m2.m[4] + m1.m[6] * m2.m[5];
|
||||
r.m[4] = m1.m[1] * m2.m[3] + m1.m[4] * m2.m[4] + m1.m[7] * m2.m[5];
|
||||
r.m[5] = m1.m[2] * m2.m[3] + m1.m[5] * m2.m[4] + m1.m[8] * m2.m[5];
|
||||
|
||||
r.m[6] = m1.m[0] * m2.m[6] + m1.m[3] * m2.m[7] + m1.m[6] * m2.m[8] ;
|
||||
r.m[7] = m1.m[1] * m2.m[6] + m1.m[4] * m2.m[7] + m1.m[7] * m2.m[8];
|
||||
r.m[8] = m1.m[2] * m2.m[6] + m1.m[5] * m2.m[7] + m1.m[8] * m2.m[8];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f MakeTranslationMatrix(const Vector3f& p)
|
||||
{
|
||||
Matrix3f r = Matrix3f::Identity();
|
||||
|
||||
r.m[12] = p.v[0];
|
||||
r.m[13] = p.v[1];
|
||||
r.m[14] = p.v[2];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f MakeScaleMatrix(float scale)
|
||||
{
|
||||
Matrix3f r = Matrix3f::Identity();
|
||||
|
||||
r.m[0] = scale;
|
||||
r.m[5] = scale;
|
||||
r.m[10] = scale;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f MakeRotationMatrix(const Vector3f& p)
|
||||
{
|
||||
Matrix3f r = Matrix3f::Identity();
|
||||
|
||||
r.m[12] = p.v[0];
|
||||
r.m[13] = p.v[1];
|
||||
r.m[14] = p.v[2];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector2f operator*(Vector2f v, float scale)
|
||||
{
|
||||
Vector2f r = v;
|
||||
|
||||
r.v[0] = v.v[0] * scale;
|
||||
r.v[1] = v.v[1] * scale;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector3f operator*(Vector3f v, float scale)
|
||||
{
|
||||
Vector3f r = v;
|
||||
|
||||
r.v[0] = v.v[0] * scale;
|
||||
r.v[1] = v.v[1] * scale;
|
||||
r.v[2] = v.v[2] * scale;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector4f operator*(Vector4f v, float scale)
|
||||
{
|
||||
Vector4f r = v;
|
||||
|
||||
r.v[0] = v.v[0] * scale;
|
||||
r.v[1] = v.v[1] * scale;
|
||||
r.v[2] = v.v[2] * scale;
|
||||
r.v[3] = v.v[3] * scale;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector3f MultVectorMatrix(Vector3f v, Matrix3f mt)
|
||||
{
|
||||
Vector3f r;
|
||||
|
||||
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[1] + v.v[2] * mt.m[2];
|
||||
r.v[1] = v.v[0] * mt.m[3] + v.v[1] * mt.m[4] + v.v[2] * mt.m[5];
|
||||
r.v[2] = v.v[0] * mt.m[6] + v.v[1] * mt.m[7] + v.v[2] * mt.m[8];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector4f MultVectorMatrix(Vector4f v, Matrix4f mt)
|
||||
{
|
||||
Vector4f r;
|
||||
|
||||
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[1] + v.v[2] * mt.m[2] + v.v[3] * mt.m[3];
|
||||
r.v[1] = v.v[0] * mt.m[4] + v.v[1] * mt.m[5] + v.v[2] * mt.m[6] + v.v[3] * mt.m[7];
|
||||
r.v[2] = v.v[0] * mt.m[8] + v.v[1] * mt.m[9] + v.v[2] * mt.m[10] + v.v[3] * mt.m[11];
|
||||
r.v[3] = v.v[0] * mt.m[12] + v.v[1] * mt.m[13] + v.v[2] * mt.m[14] + v.v[3] * mt.m[15];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector4f MultMatrixVector(Matrix4f mt, Vector4f v)
|
||||
{
|
||||
Vector4f r;
|
||||
|
||||
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[4] + v.v[2] * mt.m[8] + v.v[3] * mt.m[12];
|
||||
r.v[1] = v.v[0] * mt.m[1] + v.v[1] * mt.m[5] + v.v[2] * mt.m[9] + v.v[3] * mt.m[13];
|
||||
r.v[2] = v.v[0] * mt.m[2] + v.v[1] * mt.m[6] + v.v[2] * mt.m[10] + v.v[3] * mt.m[14];
|
||||
r.v[3] = v.v[0] * mt.m[3] + v.v[1] * mt.m[7] + v.v[2] * mt.m[11] + v.v[3] * mt.m[15];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector3f MultMatrixVector(Matrix3f mt, Vector3f v)
|
||||
{
|
||||
Vector3f r;
|
||||
|
||||
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[3] + v.v[2] * mt.m[6];
|
||||
r.v[1] = v.v[0] * mt.m[1] + v.v[1] * mt.m[4] + v.v[2] * mt.m[7];
|
||||
r.v[2] = v.v[0] * mt.m[2] + v.v[1] * mt.m[5] + v.v[2] * mt.m[8];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector4f slerp(const Vector4f& q1, const Vector4f& q2, float t)
|
||||
{
|
||||
const float epsilon = 1e-6f;
|
||||
|
||||
// Нормализация входных кватернионов
|
||||
Vector4f q1_norm = q1.normalized();
|
||||
Vector4f q2_norm = q2.normalized();
|
||||
|
||||
float cosTheta = q1_norm.dot(q2_norm);
|
||||
|
||||
// Если q1 и q2 близки к противоположным направлениям, корректируем q2
|
||||
Vector4f q2_adjusted = q2_norm;
|
||||
if (cosTheta < 0.0f) {
|
||||
q2_adjusted.v[0] = -q2_adjusted.v[0];
|
||||
q2_adjusted.v[1] = -q2_adjusted.v[1];
|
||||
q2_adjusted.v[2] = -q2_adjusted.v[2];
|
||||
q2_adjusted.v[3] = -q2_adjusted.v[3];
|
||||
cosTheta = -cosTheta;
|
||||
}
|
||||
|
||||
// Если кватернионы близки, используем линейную интерполяцию
|
||||
if (cosTheta > 1.0f - epsilon) {
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = q1_norm.v[0] + t * (q2_adjusted.v[0] - q1_norm.v[0]);
|
||||
result.v[1] = q1_norm.v[1] + t * (q2_adjusted.v[1] - q1_norm.v[1]);
|
||||
result.v[2] = q1_norm.v[2] + t * (q2_adjusted.v[2] - q1_norm.v[2]);
|
||||
result.v[3] = q1_norm.v[3] + t * (q2_adjusted.v[3] - q1_norm.v[3]);
|
||||
|
||||
return result.normalized();
|
||||
}
|
||||
|
||||
// Иначе используем сферическую интерполяцию
|
||||
float theta = std::acos(cosTheta);
|
||||
float sinTheta = std::sin(theta);
|
||||
|
||||
float coeff1 = std::sin((1.0f - t) * theta) / sinTheta;
|
||||
float coeff2 = std::sin(t * theta) / sinTheta;
|
||||
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = coeff1 * q1_norm.v[0] + coeff2 * q2_adjusted.v[0];
|
||||
result.v[1] = coeff1 * q1_norm.v[1] + coeff2 * q2_adjusted.v[1];
|
||||
result.v[2] = coeff1 * q1_norm.v[2] + coeff2 * q2_adjusted.v[2];
|
||||
result.v[3] = coeff1 * q1_norm.v[3] + coeff2 * q2_adjusted.v[3];
|
||||
|
||||
return result.normalized();
|
||||
}
|
||||
|
||||
Matrix4f MakeMatrix4x4(const Matrix3f& m, const Vector3f pos)
|
||||
{
|
||||
Matrix4f r;
|
||||
|
||||
r.m[0] = m.m[0];
|
||||
r.m[1] = m.m[1];
|
||||
r.m[2] = m.m[2];
|
||||
r.m[3] = 0;
|
||||
|
||||
r.m[4] = m.m[3];
|
||||
r.m[5] = m.m[4];
|
||||
r.m[6] = m.m[5];
|
||||
r.m[7] = 0;
|
||||
|
||||
r.m[8] = m.m[6];
|
||||
r.m[9] = m.m[7];
|
||||
r.m[10] = m.m[8];
|
||||
r.m[11] = 0;
|
||||
|
||||
r.m[12] = pos.v[0];
|
||||
r.m[13] = pos.v[1];
|
||||
r.m[14] = pos.v[2];
|
||||
r.m[15] = 1.0;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct Vector4f
|
||||
{
|
||||
std::array<float, 4> v = { 0.f, 0.f, 0.f, 0.f };
|
||||
|
||||
Vector4f normalized() const {
|
||||
double norm = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
|
||||
if (norm <= 0.000001f) {
|
||||
return { 0,0, 0, 0};
|
||||
}
|
||||
Vector4f r;
|
||||
|
||||
r.v[0] = v[0] / norm;
|
||||
r.v[1] = v[1] / norm;
|
||||
r.v[2] = v[2] / norm;
|
||||
r.v[3] = v[3] / norm;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
double dot(const Vector4f& other) const {
|
||||
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2] + v[3] * other.v[3];
|
||||
}
|
||||
};
|
||||
|
||||
struct Vector3f
|
||||
{
|
||||
std::array<float, 3> v = { 0.f, 0.f, 0.f };
|
||||
|
||||
Vector3f()
|
||||
{
|
||||
}
|
||||
|
||||
Vector3f(float x, float y, float z)
|
||||
: v{x,y,z}
|
||||
{
|
||||
}
|
||||
|
||||
Vector3f normalized() const {
|
||||
double norm = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
if (norm <= 0.000001f) {
|
||||
return { 0,0,0 };
|
||||
}
|
||||
Vector3f r;
|
||||
|
||||
r.v[0] = v[0] / norm;
|
||||
r.v[1] = v[1] / norm;
|
||||
r.v[2] = v[2] / norm;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
float squaredNorm() const {
|
||||
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
||||
}
|
||||
|
||||
float length() const
|
||||
{
|
||||
return sqrt(squaredNorm());
|
||||
}
|
||||
|
||||
float dot(const Vector3f& other) const {
|
||||
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2];
|
||||
}
|
||||
|
||||
Vector3f cross(const Vector3f& other) const {
|
||||
return Vector3f(
|
||||
v[1] * other.v[2] - v[2] * other.v[1],
|
||||
v[2] * other.v[0] - v[0] * other.v[2],
|
||||
v[0] * other.v[1] - v[1] * other.v[0]
|
||||
);
|
||||
}
|
||||
|
||||
bool operator<(const Vector3f& other) const {
|
||||
if (v[0] != other.v[0]) return v[0] < other.v[0];
|
||||
if (v[1] != other.v[1]) return v[1] < other.v[1];
|
||||
return v[2] < other.v[2];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Vector2f
|
||||
{
|
||||
std::array<float, 2> v = {0.f, 0.f};
|
||||
|
||||
Vector2f()
|
||||
{
|
||||
}
|
||||
|
||||
Vector2f(float x, float y)
|
||||
: v{ x,y }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
Vector2f operator+(const Vector2f& x, const Vector2f& y);
|
||||
|
||||
Vector2f operator-(const Vector2f& x, const Vector2f& y);
|
||||
|
||||
Vector3f operator+(const Vector3f& x, const Vector3f& y);
|
||||
|
||||
Vector3f operator-(const Vector3f& x, const Vector3f& y);
|
||||
Vector4f operator+(const Vector4f& x, const Vector4f& y);
|
||||
|
||||
Vector4f operator-(const Vector4f& x, const Vector4f& y);
|
||||
|
||||
Vector3f operator-(const Vector3f& x);
|
||||
|
||||
|
||||
struct Matrix3f
|
||||
{
|
||||
std::array<float, 9> m = { 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, };
|
||||
|
||||
static Matrix3f Identity();
|
||||
};
|
||||
|
||||
struct Matrix4f
|
||||
{
|
||||
std::array<float, 16> m = { 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f };
|
||||
|
||||
static Matrix4f Identity();
|
||||
|
||||
float& operator()(int row, int col) {
|
||||
//return m[row * 4 + col]; //OpenGL specific
|
||||
return m[col * 4 + row];
|
||||
}
|
||||
|
||||
const float& operator()(int row, int col) const {
|
||||
//return m[row * 4 + col];
|
||||
return m[col * 4 + row];
|
||||
}
|
||||
};
|
||||
|
||||
Matrix4f operator*(const Matrix4f& m1, const Matrix4f& m2);
|
||||
|
||||
Matrix4f MakeOrthoMatrix(float width, float height, float zNear, float zFar);
|
||||
Matrix4f MakeOrthoMatrix(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar);
|
||||
|
||||
Matrix4f MakePerspectiveMatrix(float fovY, float aspectRatio, float zNear, float zFar);
|
||||
|
||||
Matrix3f QuatToMatrix(const Vector4f& q);
|
||||
|
||||
Vector4f MatrixToQuat(const Matrix3f& m);
|
||||
|
||||
Vector4f QuatFromRotateAroundX(float angle);
|
||||
Vector4f QuatFromRotateAroundY(float angle);
|
||||
Vector4f QuatFromRotateAroundZ(float angle);
|
||||
|
||||
Vector2f operator*(Vector2f v, float scale);
|
||||
Vector3f operator*(Vector3f v, float scale);
|
||||
Vector4f operator*(Vector4f v, float scale);
|
||||
|
||||
Vector3f MultVectorMatrix(Vector3f v, Matrix3f mt);
|
||||
Vector4f MultVectorMatrix(Vector4f v, Matrix4f mt);
|
||||
Vector4f MultMatrixVector(Matrix4f mt, Vector4f v);
|
||||
Vector3f MultMatrixVector(Matrix3f mt, Vector3f v);
|
||||
|
||||
Vector4f slerp(const Vector4f& q1, const Vector4f& q2, float t);
|
||||
Matrix3f InverseMatrix(const Matrix3f& m);
|
||||
Matrix4f InverseMatrix(const Matrix4f& m);
|
||||
Matrix3f MultMatrixMatrix(const Matrix3f& m1, const Matrix3f& m2);
|
||||
Matrix4f MultMatrixMatrix(const Matrix4f& m1, const Matrix4f& m2);
|
||||
Matrix4f MakeMatrix4x4(const Matrix3f& m, const Vector3f pos);
|
||||
|
||||
};
|
||||
Reference in new issue
Block a user