Compare commits
61 Commits
planet-dee
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96d879076a | ||
|
|
8f3528f9a1 | ||
|
|
67c0696061 | ||
|
|
1a7e424085 | ||
|
|
6aff22ec53 | ||
|
|
579f4886d1 | ||
|
|
64385ba15c | ||
|
|
3c55b59c8d | ||
|
|
e0a60dcd6f | ||
|
|
f967807296 | ||
|
|
fbc420e894 | ||
|
|
ff95163bcd | ||
|
|
e0453db063 | ||
| 0f415b6ed6 | |||
|
|
1348efb5fe | ||
|
|
ea26d6fb23 | ||
|
|
52a52ef960 | ||
|
|
90ad3392cb | ||
|
|
dc5c76f888 | ||
|
|
483757f8ca | ||
|
|
bdce619e68 | ||
|
|
dca38c986d | ||
|
|
13af968a04 | ||
|
|
4cc6971542 | ||
|
|
8d58e41459 | ||
|
|
dffbc6029d | ||
|
|
988e0fefa8 | ||
|
|
f0ad32476e | ||
|
|
deedec7833 | ||
|
|
cda1c0de20 | ||
|
|
ec30cbe810 | ||
|
|
d610f1dea1 | ||
|
|
c347545d0e | ||
|
|
a08ee412a6 | ||
|
|
528e7ae5a6 | ||
|
|
8089bcc0f3 | ||
|
|
d1cab1f3b3 | ||
|
|
9fc0adf210 | ||
|
|
197423b572 | ||
|
|
112684b40a | ||
|
|
648acf8938 | ||
|
|
b055faf882 | ||
|
|
723b973c7f | ||
|
|
6961152d34 | ||
|
|
34fee1005a | ||
|
|
8168734fbb | ||
|
|
144978bfa2 | ||
|
|
9bfe2bef89 | ||
|
|
c553780b6e | ||
|
|
570c96a628 | ||
|
|
c9178cff3a | ||
|
|
2b40c13c9d | ||
|
|
592e008914 | ||
|
|
0a5a3b653d | ||
|
|
ff3946a019 | ||
|
|
d57112be35 | ||
|
|
bee2fff538 | ||
|
|
068c319890 | ||
|
|
53ea1f768c | ||
|
|
b369c52270 | ||
|
|
01d362615f |
6
.gitignore
vendored
6
.gitignore
vendored
@ -400,4 +400,8 @@ jumpingbird.*
|
|||||||
jumpingbird.data
|
jumpingbird.data
|
||||||
build
|
build
|
||||||
build-emcmake
|
build-emcmake
|
||||||
thirdparty1
|
thirdparty
|
||||||
|
|
||||||
|
proj-web/build
|
||||||
|
proj-windows/build
|
||||||
|
public
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,719 +0,0 @@
|
|||||||
#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)) {
|
|
||||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#include "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) <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
// Then multiply by boneMatrixWorld <20> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
544
CMakeLists.txt
544
CMakeLists.txt
@ -1,544 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
|
||||||
|
|
||||||
project(space-game001 LANGUAGES CXX)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
set(BUILD_CONFIGS Debug Release)
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# Папка для всех сторонних либ
|
|
||||||
# ==============================
|
|
||||||
set(THIRDPARTY_DIR "${CMAKE_SOURCE_DIR}/thirdparty1")
|
|
||||||
file(MAKE_DIRECTORY "${THIRDPARTY_DIR}")
|
|
||||||
|
|
||||||
macro(log msg)
|
|
||||||
message(STATUS "${msg}")
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# 1) ZLIB (zlib131.zip → zlib-1.3.1) - без изменений
|
|
||||||
# ===========================================
|
|
||||||
set(ZLIB_ARCHIVE "${THIRDPARTY_DIR}/zlib131.zip")
|
|
||||||
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.1")
|
|
||||||
set(ZLIB_BUILD_DIR "${ZLIB_SRC_DIR}/build")
|
|
||||||
set(ZLIB_INSTALL_DIR "${ZLIB_SRC_DIR}/install")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${ZLIB_ARCHIVE}")
|
|
||||||
log("Downloading zlib131.zip ...")
|
|
||||||
file(DOWNLOAD
|
|
||||||
"https://www.zlib.net/zlib131.zip"
|
|
||||||
"${ZLIB_ARCHIVE}"
|
|
||||||
SHOW_PROGRESS
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${ZLIB_SRC_DIR}/CMakeLists.txt")
|
|
||||||
log("Extracting zlib131.zip to zlib-1.3.1 ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E tar xvf "${ZLIB_ARCHIVE}"
|
|
||||||
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
|
|
||||||
RESULT_VARIABLE _zlib_extract_res
|
|
||||||
)
|
|
||||||
if(NOT _zlib_extract_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "Failed to extract zlib archive")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${ZLIB_BUILD_DIR}")
|
|
||||||
|
|
||||||
# проверяем, собран ли уже zlib
|
|
||||||
set(_have_zlib FALSE)
|
|
||||||
foreach(candidate
|
|
||||||
"${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
|
|
||||||
)
|
|
||||||
if(EXISTS "${candidate}")
|
|
||||||
set(_have_zlib TRUE)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
|
|
||||||
if(NOT _have_zlib)
|
|
||||||
log("Configuring zlib ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-G "${CMAKE_GENERATOR}"
|
|
||||||
-S "${ZLIB_SRC_DIR}"
|
|
||||||
-B "${ZLIB_BUILD_DIR}"
|
|
||||||
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
|
|
||||||
RESULT_VARIABLE _zlib_cfg_res
|
|
||||||
)
|
|
||||||
if(NOT _zlib_cfg_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "zlib configure failed")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach(cfg IN LISTS BUILD_CONFIGS)
|
|
||||||
log("Building ZLIB (${cfg}) ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
--build "${ZLIB_BUILD_DIR}" --config ${cfg}
|
|
||||||
RESULT_VARIABLE _zlib_build_res
|
|
||||||
)
|
|
||||||
if(NOT _zlib_build_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "ZLIB build failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
log("Installing ZLIB (${cfg}) ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
--install "${ZLIB_BUILD_DIR}" --config ${cfg}
|
|
||||||
RESULT_VARIABLE _zlib_inst_res
|
|
||||||
)
|
|
||||||
if(NOT _zlib_inst_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "ZLIB install failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ИСПРАВЛЕНИЕ: Используем свойства для конкретных конфигураций
|
|
||||||
add_library(zlib_external_lib UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(zlib_external_lib PROPERTIES
|
|
||||||
# Динамическая линковка (если zlib.lib - это импорт-библиотека для zlibd.dll)
|
|
||||||
#IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibd.lib"
|
|
||||||
#IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlib.lib"
|
|
||||||
|
|
||||||
# Можно также указать статические библиотеки, если вы хотите их использовать
|
|
||||||
IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibstaticd.lib"
|
|
||||||
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
|
|
||||||
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INSTALL_DIR}/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# 2) SDL2 (release-2.32.10.zip → SDL-release-2.32.10) - без изменений
|
|
||||||
# ===========================================
|
|
||||||
set(SDL2_ARCHIVE "${THIRDPARTY_DIR}/release-2.32.10.zip")
|
|
||||||
set(SDL2_SRC_DIR "${THIRDPARTY_DIR}/SDL-release-2.32.10")
|
|
||||||
set(SDL2_BUILD_DIR "${SDL2_SRC_DIR}/build")
|
|
||||||
set(SDL2_INSTALL_DIR "${SDL2_SRC_DIR}/install")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${SDL2_ARCHIVE}")
|
|
||||||
log("Downloading SDL2 release-2.32.10.zip ...")
|
|
||||||
file(DOWNLOAD
|
|
||||||
"https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip"
|
|
||||||
"${SDL2_ARCHIVE}"
|
|
||||||
SHOW_PROGRESS
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${SDL2_SRC_DIR}/CMakeLists.txt")
|
|
||||||
log("Extracting SDL2 archive to SDL-release-2.32.10 ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E tar xvf "${SDL2_ARCHIVE}"
|
|
||||||
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
|
|
||||||
RESULT_VARIABLE _sdl_extract_res
|
|
||||||
)
|
|
||||||
if(NOT _sdl_extract_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "Failed to extract SDL2 archive")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${SDL2_BUILD_DIR}")
|
|
||||||
|
|
||||||
set(_have_sdl2 FALSE)
|
|
||||||
foreach(candidate
|
|
||||||
"${SDL2_INSTALL_DIR}/lib/SDL2.lib"
|
|
||||||
"${SDL2_INSTALL_DIR}/lib/SDL2-static.lib"
|
|
||||||
"${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
|
|
||||||
)
|
|
||||||
if(EXISTS "${candidate}")
|
|
||||||
set(_have_sdl2 TRUE)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(NOT _have_sdl2)
|
|
||||||
log("Configuring SDL2 ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-G "${CMAKE_GENERATOR}"
|
|
||||||
-S "${SDL2_SRC_DIR}"
|
|
||||||
-B "${SDL2_BUILD_DIR}"
|
|
||||||
-DCMAKE_INSTALL_PREFIX=${SDL2_INSTALL_DIR}
|
|
||||||
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR} # путь к zlib для SDL2
|
|
||||||
RESULT_VARIABLE _sdl_cfg_res
|
|
||||||
)
|
|
||||||
if(NOT _sdl_cfg_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "SDL2 configure failed")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
|
|
||||||
foreach(cfg IN LISTS BUILD_CONFIGS)
|
|
||||||
log("Building SDL2 (${cfg}) ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
--build "${SDL2_BUILD_DIR}" --config ${cfg}
|
|
||||||
RESULT_VARIABLE _sdl_build_res
|
|
||||||
)
|
|
||||||
if(NOT _sdl_build_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "SDL2 build failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
log("Installing SDL2 (${cfg}) ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
--install "${SDL2_BUILD_DIR}" --config ${cfg}
|
|
||||||
RESULT_VARIABLE _sdl_inst_res
|
|
||||||
)
|
|
||||||
if(NOT _sdl_inst_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "SDL2 install failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
# ------------------------------------------------------
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# ИСПРАВЛЕНИЕ: SDL2: Используем свойства для конкретных конфигураций
|
|
||||||
add_library(SDL2_external_lib UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(SDL2_external_lib PROPERTIES
|
|
||||||
# Динамическая линковка SDL2
|
|
||||||
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
|
|
||||||
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2.lib"
|
|
||||||
# Оба include-пути: и include, и include/SDL2
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include;${SDL2_INSTALL_DIR}/include/SDL2"
|
|
||||||
)
|
|
||||||
|
|
||||||
# SDL2main (обычно статическая)
|
|
||||||
add_library(SDL2main_external_lib UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(SDL2main_external_lib PROPERTIES
|
|
||||||
# ИСПРАВЛЕНО: Указываем пути для Debug и Release, используя
|
|
||||||
# соглашение, что Debug имеет суффикс 'd', а Release — нет.
|
|
||||||
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2maind.lib"
|
|
||||||
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2main.lib"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
log("-----${SDL2_INSTALL_DIR}/lib/SDL2maind.lib")
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# 3) libpng (v1.6.51.zip → libpng-1.6.51) - без изменений
|
|
||||||
# ===========================================
|
|
||||||
set(LIBPNG_ARCHIVE "${THIRDPARTY_DIR}/v1.6.51.zip")
|
|
||||||
set(LIBPNG_SRC_DIR "${THIRDPARTY_DIR}/libpng-1.6.51")
|
|
||||||
set(LIBPNG_BUILD_DIR "${LIBPNG_SRC_DIR}/build")
|
|
||||||
set(LIBPNG_INSTALL_DIR "${LIBPNG_SRC_DIR}/install") # на будущее
|
|
||||||
|
|
||||||
if(NOT EXISTS "${LIBPNG_ARCHIVE}")
|
|
||||||
log("Downloading libpng v1.6.51.zip ...")
|
|
||||||
file(DOWNLOAD
|
|
||||||
"https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.51.zip"
|
|
||||||
"${LIBPNG_ARCHIVE}"
|
|
||||||
SHOW_PROGRESS
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${LIBPNG_SRC_DIR}/CMakeLists.txt")
|
|
||||||
log("Extracting libpng v1.6.51.zip to libpng-1.6.51 ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E tar xvf "${LIBPNG_ARCHIVE}"
|
|
||||||
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
|
|
||||||
RESULT_VARIABLE _png_extract_res
|
|
||||||
)
|
|
||||||
if(NOT _png_extract_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "Failed to extract libpng archive")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${LIBPNG_BUILD_DIR}")
|
|
||||||
|
|
||||||
# Проверяем, есть ли уже .lib (build/Debug или install/lib)
|
|
||||||
set(_libpng_candidates
|
|
||||||
"${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
|
|
||||||
"${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(_have_png FALSE)
|
|
||||||
foreach(candidate IN LISTS _libpng_candidates)
|
|
||||||
if(EXISTS "${candidate}")
|
|
||||||
set(_have_png TRUE)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(NOT _have_png)
|
|
||||||
log("Configuring libpng ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-G "${CMAKE_GENERATOR}"
|
|
||||||
-S "${LIBPNG_SRC_DIR}"
|
|
||||||
-B "${LIBPNG_BUILD_DIR}"
|
|
||||||
-DCMAKE_INSTALL_PREFIX=${LIBPNG_INSTALL_DIR}
|
|
||||||
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
|
|
||||||
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
|
|
||||||
RESULT_VARIABLE _png_cfg_res
|
|
||||||
)
|
|
||||||
if(NOT _png_cfg_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "libpng configure failed")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
|
|
||||||
foreach(cfg IN LISTS BUILD_CONFIGS)
|
|
||||||
log("Building libpng (${cfg}) ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
--build "${LIBPNG_BUILD_DIR}" --config ${cfg}
|
|
||||||
RESULT_VARIABLE _png_build_res
|
|
||||||
)
|
|
||||||
if(NOT _png_build_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "libpng build failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Поскольку вы не используете "cmake --install" для libpng,
|
|
||||||
# здесь нет необходимости в дополнительном шаге установки.
|
|
||||||
# Файлы .lib будут сгенерированы в подкаталоге ${LIBPNG_BUILD_DIR}/${cfg} (например, build/Debug или build/Release).
|
|
||||||
|
|
||||||
endforeach()
|
|
||||||
# ------------------------------------------------------
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(libpng_external_lib UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(libpng_external_lib PROPERTIES
|
|
||||||
# Предполагая, что libpng использует статический вариант
|
|
||||||
IMPORTED_LOCATION_DEBUG "${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
|
|
||||||
IMPORTED_LOCATION_RELEASE "${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
|
|
||||||
|
|
||||||
# png.h, pngconf.h – в SRC, pnglibconf.h – в BUILD
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBPNG_SRC_DIR};${LIBPNG_BUILD_DIR}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# 4) libzip (v1.11.4.zip → libzip-1.11.4) - НОВАЯ ЗАВИСИМОСТЬ
|
|
||||||
# ===========================================
|
|
||||||
set(LIBZIP_ARCHIVE "${THIRDPARTY_DIR}/v1.11.4.zip")
|
|
||||||
set(LIBZIP_SRC_DIR "${THIRDPARTY_DIR}/libzip-1.11.4")
|
|
||||||
set(LIBZIP_BUILD_DIR "${LIBZIP_SRC_DIR}/build")
|
|
||||||
#set(LIBZIP_INSTALL_DIR "${LIBZIP_SRC_DIR}/install")
|
|
||||||
set(LIBZIP_BASE_DIR "${LIBZIP_SRC_DIR}/install")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${LIBZIP_ARCHIVE}")
|
|
||||||
log("Downloading libzip v1.11.4.zip ...")
|
|
||||||
file(DOWNLOAD
|
|
||||||
"https://github.com/nih-at/libzip/archive/refs/tags/v1.11.4.zip"
|
|
||||||
"${LIBZIP_ARCHIVE}"
|
|
||||||
SHOW_PROGRESS
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${LIBZIP_SRC_DIR}/CMakeLists.txt")
|
|
||||||
log("Extracting libzip v1.11.4.zip to libzip-1.11.4 ...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E tar xvf "${LIBZIP_ARCHIVE}"
|
|
||||||
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
|
|
||||||
RESULT_VARIABLE _zip_extract_res
|
|
||||||
)
|
|
||||||
if(NOT _zip_extract_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "Failed to extract libzip archive")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${LIBZIP_BUILD_DIR}")
|
|
||||||
|
|
||||||
# Проверяем, собран ли уже libzip
|
|
||||||
set(_have_zip FALSE)
|
|
||||||
foreach(candidate
|
|
||||||
"${LIBZIP_BASE_DIR}-Debug/lib/zip.lib"
|
|
||||||
"${LIBZIP_BASE_DIR}-Release/lib/zip.lib"
|
|
||||||
)
|
|
||||||
if(EXISTS "${candidate}")
|
|
||||||
set(_have_zip TRUE)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
|
|
||||||
if(NOT _have_zip)
|
|
||||||
foreach(cfg IN LISTS BUILD_CONFIGS)
|
|
||||||
log("Configuring libzip (${cfg})...")
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-G "${CMAKE_GENERATOR}"
|
|
||||||
-S "${LIBZIP_SRC_DIR}"
|
|
||||||
-B "${LIBZIP_SRC_DIR}/build-${cfg}"
|
|
||||||
-DCMAKE_INSTALL_PREFIX=${LIBZIP_BASE_DIR}-${cfg}
|
|
||||||
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
|
|
||||||
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
|
|
||||||
-DENABLE_COMMONCRYPTO=OFF
|
|
||||||
-DENABLE_GNUTLS=OFF
|
|
||||||
-DENABLE_MBEDTLS=OFF
|
|
||||||
-DENABLE_OPENSSL=OFF
|
|
||||||
-DENABLE_WINDOWS_CRYPTO=OFF
|
|
||||||
-DENABLE_FUZZ=OFF
|
|
||||||
RESULT_VARIABLE _zip_cfg_res
|
|
||||||
)
|
|
||||||
if(NOT _zip_cfg_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "libzip configure failed")
|
|
||||||
endif()
|
|
||||||
log("Building libzip (${cfg}) ...")
|
|
||||||
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} --build "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
|
|
||||||
RESULT_VARIABLE _zip_build_res
|
|
||||||
)
|
|
||||||
if(NOT _zip_build_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "libzip build failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
log("Installing libzip (${cfg}) ...")
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} --install "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
|
|
||||||
RESULT_VARIABLE _zip_inst_res
|
|
||||||
)
|
|
||||||
if(NOT _zip_inst_res EQUAL 0)
|
|
||||||
message(FATAL_ERROR "libzip install failed for configuration ${cfg}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
add_library(libzip_external_lib UNKNOWN IMPORTED GLOBAL)
|
|
||||||
set_target_properties(libzip_external_lib PROPERTIES
|
|
||||||
IMPORTED_LOCATION_DEBUG "${LIBZIP_BASE_DIR}-Debug/lib/zip.lib" # ИСПРАВЛЕНО
|
|
||||||
IMPORTED_LOCATION_RELEASE "${LIBZIP_BASE_DIR}-Release/lib/zip.lib" # ИСПРАВЛЕНО
|
|
||||||
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/include,${LIBZIP_BASE_DIR}-Release/include>"
|
|
||||||
# libzip требует zlib для линковки
|
|
||||||
INTERFACE_LINK_LIBRARIES zlib_external_lib
|
|
||||||
)
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# Основной проект space-game001
|
|
||||||
# ===========================================
|
|
||||||
add_executable(space-game001
|
|
||||||
main.cpp
|
|
||||||
Game.cpp
|
|
||||||
Game.h
|
|
||||||
Environment.cpp
|
|
||||||
Environment.h
|
|
||||||
Renderer.cpp
|
|
||||||
Renderer.h
|
|
||||||
ShaderManager.cpp
|
|
||||||
ShaderManager.h
|
|
||||||
TextureManager.cpp
|
|
||||||
TextureManager.h
|
|
||||||
TextModel.cpp
|
|
||||||
TextModel.h
|
|
||||||
AudioPlayerAsync.cpp
|
|
||||||
AudioPlayerAsync.h
|
|
||||||
BoneAnimatedModel.cpp
|
|
||||||
BoneAnimatedModel.h
|
|
||||||
ZLMath.cpp
|
|
||||||
ZLMath.h
|
|
||||||
OpenGlExtensions.cpp
|
|
||||||
OpenGlExtensions.h
|
|
||||||
Utils.cpp
|
|
||||||
Utils.h
|
|
||||||
SparkEmitter.cpp
|
|
||||||
SparkEmitter.h
|
|
||||||
PlanetObject.cpp
|
|
||||||
PlanetObject.h
|
|
||||||
PlanetData.cpp
|
|
||||||
PlanetData.h
|
|
||||||
Perlin.cpp
|
|
||||||
Perlin.h
|
|
||||||
StoneObject.cpp
|
|
||||||
StoneObject.h
|
|
||||||
FrameBuffer.cpp
|
|
||||||
FrameBuffer.h
|
|
||||||
)
|
|
||||||
|
|
||||||
# Установка проекта по умолчанию для Visual Studio
|
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT space-game001)
|
|
||||||
|
|
||||||
# include-пути проекта
|
|
||||||
target_include_directories(space-game001 PRIVATE
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
#"${CMAKE_CURRENT_SOURCE_DIR}/gl"
|
|
||||||
#"${CMAKE_CURRENT_SOURCE_DIR}/cmakeaudioplayer/include"
|
|
||||||
#"${SDL2_INSTALL_DIR}/include"
|
|
||||||
#"${SDL2_INSTALL_DIR}/include/SDL2"
|
|
||||||
#"${LIBZIP_INSTALL_DIR}-Release/include" # Добавил include-путь для libzip
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(space-game001 PROPERTIES
|
|
||||||
OUTPUT_NAME "space-game001"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Определения препроцессора:
|
|
||||||
# PNG_ENABLED – включает код PNG в TextureManager
|
|
||||||
# SDL_MAIN_HANDLED – отключает переопределение main -> SDL_main
|
|
||||||
target_compile_definitions(space-game001 PRIVATE
|
|
||||||
PNG_ENABLED
|
|
||||||
SDL_MAIN_HANDLED
|
|
||||||
)
|
|
||||||
|
|
||||||
# Линкуем с SDL2main, если он вообще установлен
|
|
||||||
target_link_libraries(space-game001 PRIVATE SDL2main_external_lib)
|
|
||||||
|
|
||||||
# Линкуем сторонние библиотеки
|
|
||||||
target_link_libraries(space-game001 PRIVATE
|
|
||||||
SDL2_external_lib
|
|
||||||
libpng_external_lib
|
|
||||||
zlib_external_lib
|
|
||||||
libzip_external_lib
|
|
||||||
)
|
|
||||||
|
|
||||||
# Линкуем OpenGL (Windows)
|
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(space-game001 PRIVATE opengl32)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# Копирование SDL2d.dll и zlibd.dll рядом с exe
|
|
||||||
# ===========================================
|
|
||||||
if (WIN32)
|
|
||||||
|
|
||||||
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
|
|
||||||
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
|
|
||||||
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/SDL2d.dll,$<TARGET_FILE_DIR:space-game001>/SDL2.dll>")
|
|
||||||
|
|
||||||
|
|
||||||
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
|
|
||||||
|
|
||||||
add_custom_command(TARGET space-game001 POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
|
|
||||||
|
|
||||||
# Копируем SDL2 (целевое имя всегда SDL2.dll)
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
"${SDL2_DLL_SRC}"
|
|
||||||
"${SDL2_DLL_DST}"
|
|
||||||
|
|
||||||
# Копируем LIBZIP (целевое имя всегда zip.dll)
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
"${LIBZIP_DLL_SRC}"
|
|
||||||
"$<TARGET_FILE_DIR:space-game001>/zip.dll"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# Копирование ресурсов после сборки
|
|
||||||
# ===========================================
|
|
||||||
|
|
||||||
# Какие папки с ресурсами нужно копировать
|
|
||||||
set(RUNTIME_RESOURCE_DIRS
|
|
||||||
"resources"
|
|
||||||
"shaders"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Копируем ресурсы и шейдеры в папку exe и в корень build/
|
|
||||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
|
||||||
add_custom_command(TARGET space-game001 POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
|
|
||||||
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
"${CMAKE_SOURCE_DIR}/${resdir}"
|
|
||||||
"$<TARGET_FILE_DIR:space-game001>/${resdir}"
|
|
||||||
# 2) в корень build, если захочешь запускать из этой папки
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
"${CMAKE_SOURCE_DIR}/${resdir}"
|
|
||||||
"${CMAKE_BINARY_DIR}/${resdir}"
|
|
||||||
)
|
|
||||||
endforeach()
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
#include "Environment.h"
|
|
||||||
|
|
||||||
#include "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
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#endif
|
|
||||||
#include "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
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
#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
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
#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
|
|
||||||
655
Game.cpp
655
Game.cpp
@ -1,655 +0,0 @@
|
|||||||
#include "Game.h"
|
|
||||||
#include "AnimatedModel.h"
|
|
||||||
#include "BoneAnimatedModel.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include "TextureManager.h"
|
|
||||||
#include "TextModel.h"
|
|
||||||
#include "StoneObject.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)
|
|
||||||
{
|
|
||||||
std::vector<Vector3f> emissionPoints = {
|
|
||||||
Vector3f{-2.1f, 0.9f, 5.0f},
|
|
||||||
Vector3f{2.1f, 0.9f, 5.0f}
|
|
||||||
};
|
|
||||||
sparkEmitter = SparkEmitter(emissionPoints, 100.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
sparkTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/spark.png", CONST_ZIP_FILE));
|
|
||||||
|
|
||||||
sparkEmitter.setTexture(sparkTexture);
|
|
||||||
|
|
||||||
|
|
||||||
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 });
|
|
||||||
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
|
||||||
renderer.DrawVertexRenderStruct(spaceship);
|
|
||||||
sparkEmitter.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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTickCount = newTickCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
std::cout << mx << " " << my << '\n';
|
|
||||||
int uiX = mx;
|
|
||||||
int uiY = Environment::height - my;
|
|
||||||
if (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 &&
|
|
||||||
uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) {
|
|
||||||
isDraggingVolume = true;
|
|
||||||
UpdateVolumeFromMouse(mx, my);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Environment::tapDownHold = true;
|
|
||||||
// Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
|
|
||||||
Environment::tapDownStartPos.v[0] = event.button.x;
|
|
||||||
Environment::tapDownStartPos.v[1] = event.button.y;
|
|
||||||
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
|
|
||||||
Environment::tapDownCurrentPos.v[0] = event.button.x;
|
|
||||||
Environment::tapDownCurrentPos.v[1] = event.button.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (event.type == SDL_MOUSEBUTTONUP) {
|
|
||||||
// 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
|
|
||||||
isDraggingVolume = false;
|
|
||||||
Environment::tapDownHold = false;
|
|
||||||
}
|
|
||||||
else if (event.type == SDL_MOUSEMOTION) {
|
|
||||||
// 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
|
|
||||||
int mx = event.motion.x;
|
|
||||||
int my = event.motion.y;
|
|
||||||
|
|
||||||
if (isDraggingVolume) {
|
|
||||||
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà
|
|
||||||
UpdateVolumeFromMouse(mx, my);
|
|
||||||
}
|
|
||||||
if (Environment::tapDownHold) {
|
|
||||||
// Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ
|
|
||||||
Environment::tapDownCurrentPos.v[0] = event.motion.x;
|
|
||||||
Environment::tapDownCurrentPos.v[1] = event.motion.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
95
Game.h
95
Game.h
@ -1,95 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "Environment.h"
|
|
||||||
#include "TextureManager.h"
|
|
||||||
#include "SparkEmitter.h"
|
|
||||||
#include "PlanetObject.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();
|
|
||||||
|
|
||||||
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;
|
|
||||||
PlanetObject planetObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
@ -1,335 +0,0 @@
|
|||||||
#include "OpenGlExtensions.h"
|
|
||||||
|
|
||||||
#include "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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
#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();
|
|
||||||
}
|
|
||||||
73
Perlin.cpp
73
Perlin.cpp
@ -1,73 +0,0 @@
|
|||||||
#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
|
|
||||||
24
Perlin.h
24
Perlin.h
@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
#include "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
|
|
||||||
493
PlanetData.cpp
493
PlanetData.cpp
@ -1,493 +0,0 @@
|
|||||||
#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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
117
PlanetData.h
117
PlanetData.h
@ -1,117 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#include "Perlin.h"
|
|
||||||
#include "Renderer.h" // Äëÿ VertexDataStruct
|
|
||||||
#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
|
|
||||||
570
PlanetObject.cpp
570
PlanetObject.cpp
@ -1,570 +0,0 @@
|
|||||||
#include "PlanetObject.h"
|
|
||||||
#include <random>
|
|
||||||
#include <cmath>
|
|
||||||
#include "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
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "TextureManager.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <array>
|
|
||||||
#include <numeric>
|
|
||||||
#include <random>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include "Perlin.h"
|
|
||||||
#include "PlanetData.h"
|
|
||||||
#include "StoneObject.h"
|
|
||||||
#include "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
|
|
||||||
@ -154,6 +154,12 @@ emcc main.cpp Game.cpp Environment.cpp BoneAnimatedModel.cpp ZLMath.cpp Renderer
|
|||||||
emrun --no_browser --port 8080 .
|
emrun --no_browser --port 8080 .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Emscripten new
|
||||||
|
```
|
||||||
|
emcc src/main.cpp src/Game.cpp src/Environment.cpp src/BoneAnimatedModel.cpp src/TextModel.cpp src/Projectile.cpp src/SparkEmitter.cpp src/UiManager.cpp src/render/Renderer.cpp src/render/ShaderManager.cpp src/render/TextureManager.cpp src/render/FrameBuffer.cpp src/render/OpenGlExtensions.cpp src/utils/Utils.cpp src/utils/TaskManager.cpp src/utils/Perlin.cpp src/planet/PlanetData.cpp src/planet/PlanetObject.cpp src/planet/StoneObject.cpp -O2 -std=c++17 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -fexceptions -I./thirdparty1/eigen-5.0.0 -I./src -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -IC:/Boost/include/boost-1_84 -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sUSE_SDL=2 -sUSE_LIBPNG=1 --preload-file space-game001.zip -o space-game001.html
|
||||||
|
```
|
||||||
|
|
||||||
# License
|
# License
|
||||||
Code: MIT
|
Code: MIT
|
||||||
|
|
||||||
|
|||||||
833
Renderer.cpp
833
Renderer.cpp
@ -1,833 +0,0 @@
|
|||||||
#include "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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
144
Renderer.h
144
Renderer.h
@ -1,144 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include "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);
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -1,215 +0,0 @@
|
|||||||
#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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include "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();
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
299
SparkEmitter.cpp
299
SparkEmitter.cpp
@ -1,299 +0,0 @@
|
|||||||
#include "SparkEmitter.h"
|
|
||||||
#include <random>
|
|
||||||
#include <cmath>
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include "Environment.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
SparkEmitter::SparkEmitter()
|
|
||||||
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200) {
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
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 = 0.04f * 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 (getActiveParticleCount() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!texture) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareDrawData();
|
|
||||||
|
|
||||||
if (drawPositions.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sparkQuad.data.PositionData = drawPositions;
|
|
||||||
sparkQuad.data.TexCoordData = drawTexCoords;
|
|
||||||
sparkQuad.RefreshVBO();
|
|
||||||
|
|
||||||
static const std::string defaultShaderName = "default";
|
|
||||||
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);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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 (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;
|
|
||||||
maxParticles = positions.size() * 100;
|
|
||||||
particles.resize(maxParticles);
|
|
||||||
drawDataDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
|
|
||||||
particle.velocity = getRandomVelocity(emitterIndex);
|
|
||||||
particle.scale = 1.0f;
|
|
||||||
particle.maxLifeTime = 800.0f + (rand() % 400);
|
|
||||||
particle.emitterIndex = emitterIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
|
|
||||||
static std::random_device rd;
|
|
||||||
static std::mt19937 gen(rd());
|
|
||||||
static std::uniform_real_distribution<> angleDist(0, 2 * M_PI);
|
|
||||||
static std::uniform_real_distribution<> speedDist(0.5f, 2.0f);
|
|
||||||
static std::uniform_real_distribution<> zSpeedDist(1.0f, 3.0f);
|
|
||||||
|
|
||||||
float angle = angleDist(gen);
|
|
||||||
float speed = speedDist(gen);
|
|
||||||
float zSpeed = zSpeedDist(gen);
|
|
||||||
|
|
||||||
if (emitterIndex == 0) {
|
|
||||||
return Vector3f{
|
|
||||||
cosf(angle) * speed - 0.3f,
|
|
||||||
sinf(angle) * speed,
|
|
||||||
zSpeed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Vector3f{
|
|
||||||
cosf(angle) * speed + 0.3f,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "TextureManager.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initParticle(SparkParticle& particle, int emitterIndex);
|
|
||||||
Vector3f getRandomVelocity(int emitterIndex);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
276
StoneObject.cpp
276
StoneObject.cpp
@ -1,276 +0,0 @@
|
|||||||
#include "StoneObject.h"
|
|
||||||
|
|
||||||
#include "Utils.h"
|
|
||||||
#include <GL/gl.h>
|
|
||||||
#include <random>
|
|
||||||
#include <cmath>
|
|
||||||
#include "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
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#include "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
|
|
||||||
390
TextModel.cpp
390
TextModel.cpp
@ -1,390 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
12
TextModel.h
12
TextModel.h
@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ZLMath.h"
|
|
||||||
#include "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 = "");
|
|
||||||
}
|
|
||||||
@ -1,430 +0,0 @@
|
|||||||
#include "TextureManager.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
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include "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
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
105
Utils.cpp
105
Utils.cpp
@ -1,105 +0,0 @@
|
|||||||
#include "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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
20
Utils.h
20
Utils.h
@ -1,20 +0,0 @@
|
|||||||
#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);
|
|
||||||
|
|
||||||
}
|
|
||||||
799
ZLMath.cpp
799
ZLMath.cpp
@ -1,799 +0,0 @@
|
|||||||
#include "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];
|
|
||||||
|
|
||||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
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!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
179
ZLMath.h
179
ZLMath.h
@ -1,179 +0,0 @@
|
|||||||
#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);
|
|
||||||
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
49
cmake/FetchDependencies.cmake
Normal file
49
cmake/FetchDependencies.cmake
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# cmake/FetchDependencies.cmake
|
||||||
|
|
||||||
|
set(THIRDPARTY_DIR "${CMAKE_CURRENT_LIST_DIR}/../thirdparty")
|
||||||
|
|
||||||
|
if(NOT EXISTS "${THIRDPARTY_DIR}")
|
||||||
|
file(MAKE_DIRECTORY "${THIRDPARTY_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
macro(check_and_download URL ARCHIVE_NAME EXTRACTED_DIR_NAME CHECK_FILE)
|
||||||
|
set(ARCHIVE_PATH "${THIRDPARTY_DIR}/${ARCHIVE_NAME}")
|
||||||
|
set(SRC_PATH "${THIRDPARTY_DIR}/${EXTRACTED_DIR_NAME}")
|
||||||
|
|
||||||
|
if(NOT EXISTS "${ARCHIVE_PATH}")
|
||||||
|
message(STATUS "Downloading ${ARCHIVE_NAME}...")
|
||||||
|
file(DOWNLOAD "${URL}" "${ARCHIVE_PATH}" SHOW_PROGRESS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT EXISTS "${SRC_PATH}/${CHECK_FILE}")
|
||||||
|
message(STATUS "Extracting ${ARCHIVE_NAME}...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E tar xvf "${ARCHIVE_PATH}"
|
||||||
|
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# 1) ZLIB (Нужна только для инклудов, если не используете emscripten порты)
|
||||||
|
check_and_download("https://www.zlib.net/zlib131.zip" "zlib131.zip" "zlib-1.3.1" "CMakeLists.txt")
|
||||||
|
|
||||||
|
# 2) SDL2
|
||||||
|
check_and_download("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip" "release-2.32.10.zip" "SDL-release-2.32.10" "CMakeLists.txt")
|
||||||
|
|
||||||
|
# 3) LibPNG
|
||||||
|
check_and_download("https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.51.zip" "v1.6.51.zip" "libpng-1.6.51" "CMakeLists.txt")
|
||||||
|
|
||||||
|
# 4) LibZip
|
||||||
|
check_and_download("https://github.com/nih-at/libzip/archive/refs/tags/v1.11.4.zip" "v1.11.4.zip" "libzip-1.11.4" "CMakeLists.txt")
|
||||||
|
|
||||||
|
# 5) Eigen
|
||||||
|
check_and_download("https://gitlab.com/libeigen/eigen/-/archive/5.0.0/eigen-5.0.0.zip" "eigen-5.0.0.zip" "eigen-5.0.0" "signature_of_eigen3_matrix_library")
|
||||||
|
|
||||||
|
# 6) Boost
|
||||||
|
check_and_download("https://archives.boost.io/release/1.90.0/source/boost_1_90_0.zip" "boost_1_90_0.zip" "boost_1_90_0" "boost")
|
||||||
|
|
||||||
|
# 7) FreeType
|
||||||
|
check_and_download("https://download.savannah.gnu.org/releases/freetype/freetype-2.14.1.tar.gz" "freetype-2.14.1.tar.gz" "freetype-2.14.1" "CMakeLists.txt")
|
||||||
|
|
||||||
|
# 8) SDL_ttf
|
||||||
|
check_and_download("https://github.com/libsdl-org/SDL_ttf/archive/refs/tags/release-2.24.0.zip" "release-2.24.0.zip" "SDL_ttf-release-2.24.0" "CMakeLists.txt")
|
||||||
547
cmake/ThirdParty.cmake
Normal file
547
cmake/ThirdParty.cmake
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
# cmake/ThirdParty.cmake
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/FetchDependencies.cmake")
|
||||||
|
|
||||||
|
macro(log msg)
|
||||||
|
message(STATUS "[ThirdParty] ${msg}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
set(BUILD_CONFIGS Debug Release)
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 1) ZLIB (zlib131.zip → zlib-1.3.1) - без изменений
|
||||||
|
# ===========================================
|
||||||
|
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.1")
|
||||||
|
set(ZLIB_BUILD_DIR "${ZLIB_SRC_DIR}/build")
|
||||||
|
set(ZLIB_INSTALL_DIR "${ZLIB_SRC_DIR}/install")
|
||||||
|
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${ZLIB_BUILD_DIR}")
|
||||||
|
|
||||||
|
# проверяем, собран ли уже zlib
|
||||||
|
set(_have_zlib FALSE)
|
||||||
|
foreach(candidate
|
||||||
|
"${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${candidate}")
|
||||||
|
set(_have_zlib TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT _have_zlib)
|
||||||
|
log("Configuring zlib ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-G "${CMAKE_GENERATOR}"
|
||||||
|
-S "${ZLIB_SRC_DIR}"
|
||||||
|
-B "${ZLIB_BUILD_DIR}"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
|
||||||
|
RESULT_VARIABLE _zlib_cfg_res
|
||||||
|
)
|
||||||
|
if(NOT _zlib_cfg_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "zlib configure failed")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
log("Building ZLIB (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--build "${ZLIB_BUILD_DIR}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _zlib_build_res
|
||||||
|
)
|
||||||
|
if(NOT _zlib_build_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "ZLIB build failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Installing ZLIB (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--install "${ZLIB_BUILD_DIR}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _zlib_inst_res
|
||||||
|
)
|
||||||
|
if(NOT _zlib_inst_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "ZLIB install failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ИСПРАВЛЕНИЕ: Используем свойства для конкретных конфигураций
|
||||||
|
add_library(zlib_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(zlib_external_lib PROPERTIES
|
||||||
|
# Динамическая линковка (если zlib.lib - это импорт-библиотека для zlibd.dll)
|
||||||
|
#IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibd.lib"
|
||||||
|
#IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlib.lib"
|
||||||
|
|
||||||
|
# Можно также указать статические библиотеки, если вы хотите их использовать
|
||||||
|
IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibstaticd.lib"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
|
||||||
|
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INSTALL_DIR}/include"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 2) SDL2 (release-2.32.10.zip → SDL-release-2.32.10) - без изменений
|
||||||
|
# ===========================================
|
||||||
|
set(SDL2_SRC_DIR "${THIRDPARTY_DIR}/SDL-release-2.32.10")
|
||||||
|
set(SDL2_BUILD_DIR "${SDL2_SRC_DIR}/build")
|
||||||
|
set(SDL2_INSTALL_DIR "${SDL2_SRC_DIR}/install")
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${SDL2_BUILD_DIR}")
|
||||||
|
|
||||||
|
set(_have_sdl2 FALSE)
|
||||||
|
foreach(candidate
|
||||||
|
"${SDL2_INSTALL_DIR}/lib/SDL2.lib"
|
||||||
|
"${SDL2_INSTALL_DIR}/lib/SDL2-static.lib"
|
||||||
|
"${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${candidate}")
|
||||||
|
set(_have_sdl2 TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _have_sdl2)
|
||||||
|
log("Configuring SDL2 ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-G "${CMAKE_GENERATOR}"
|
||||||
|
-S "${SDL2_SRC_DIR}"
|
||||||
|
-B "${SDL2_BUILD_DIR}"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${SDL2_INSTALL_DIR}
|
||||||
|
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR} # путь к zlib для SDL2
|
||||||
|
RESULT_VARIABLE _sdl_cfg_res
|
||||||
|
)
|
||||||
|
if(NOT _sdl_cfg_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "SDL2 configure failed")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
log("Building SDL2 (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--build "${SDL2_BUILD_DIR}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _sdl_build_res
|
||||||
|
)
|
||||||
|
if(NOT _sdl_build_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "SDL2 build failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Installing SDL2 (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--install "${SDL2_BUILD_DIR}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _sdl_inst_res
|
||||||
|
)
|
||||||
|
if(NOT _sdl_inst_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "SDL2 install failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
# ------------------------------------------------------
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
# ИСПРАВЛЕНИЕ: SDL2: Используем свойства для конкретных конфигураций
|
||||||
|
add_library(SDL2_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(SDL2_external_lib PROPERTIES
|
||||||
|
# Динамическая линковка SDL2
|
||||||
|
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2.lib"
|
||||||
|
# Оба include-пути: и include, и include/SDL2
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include;${SDL2_INSTALL_DIR}/include/SDL2"
|
||||||
|
)
|
||||||
|
|
||||||
|
# SDL2main (обычно статическая)
|
||||||
|
add_library(SDL2main_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(SDL2main_external_lib PROPERTIES
|
||||||
|
# ИСПРАВЛЕНО: Указываем пути для Debug и Release, используя
|
||||||
|
# соглашение, что Debug имеет суффикс 'd', а Release — нет.
|
||||||
|
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2maind.lib"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2main.lib"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include"
|
||||||
|
)
|
||||||
|
|
||||||
|
log("-----${SDL2_INSTALL_DIR}/lib/SDL2maind.lib")
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 3) libpng (v1.6.51.zip → libpng-1.6.51) - без изменений
|
||||||
|
# ===========================================
|
||||||
|
set(LIBPNG_SRC_DIR "${THIRDPARTY_DIR}/libpng-1.6.51")
|
||||||
|
set(LIBPNG_BUILD_DIR "${LIBPNG_SRC_DIR}/build")
|
||||||
|
set(LIBPNG_INSTALL_DIR "${LIBPNG_SRC_DIR}/install") # на будущее
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${LIBPNG_BUILD_DIR}")
|
||||||
|
|
||||||
|
# Проверяем, есть ли уже .lib (build/Debug или install/lib)
|
||||||
|
set(_libpng_candidates
|
||||||
|
"${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
|
||||||
|
"${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(_have_png FALSE)
|
||||||
|
foreach(candidate IN LISTS _libpng_candidates)
|
||||||
|
if(EXISTS "${candidate}")
|
||||||
|
set(_have_png TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _have_png)
|
||||||
|
log("Configuring libpng ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-G "${CMAKE_GENERATOR}"
|
||||||
|
-S "${LIBPNG_SRC_DIR}"
|
||||||
|
-B "${LIBPNG_BUILD_DIR}"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${LIBPNG_INSTALL_DIR}
|
||||||
|
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
|
||||||
|
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
|
||||||
|
RESULT_VARIABLE _png_cfg_res
|
||||||
|
)
|
||||||
|
if(NOT _png_cfg_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "libpng configure failed")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
log("Building libpng (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--build "${LIBPNG_BUILD_DIR}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _png_build_res
|
||||||
|
)
|
||||||
|
if(NOT _png_build_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "libpng build failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Поскольку вы не используете "cmake --install" для libpng,
|
||||||
|
# здесь нет необходимости в дополнительном шаге установки.
|
||||||
|
# Файлы .lib будут сгенерированы в подкаталоге ${LIBPNG_BUILD_DIR}/${cfg} (например, build/Debug или build/Release).
|
||||||
|
|
||||||
|
endforeach()
|
||||||
|
# ------------------------------------------------------
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(libpng_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(libpng_external_lib PROPERTIES
|
||||||
|
# Предполагая, что libpng использует статический вариант
|
||||||
|
IMPORTED_LOCATION_DEBUG "${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
|
||||||
|
|
||||||
|
# png.h, pngconf.h – в SRC, pnglibconf.h – в BUILD
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${LIBPNG_SRC_DIR};${LIBPNG_BUILD_DIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 4) libzip (v1.11.4.zip → libzip-1.11.4) - НОВАЯ ЗАВИСИМОСТЬ
|
||||||
|
# ===========================================
|
||||||
|
set(LIBZIP_SRC_DIR "${THIRDPARTY_DIR}/libzip-1.11.4")
|
||||||
|
set(LIBZIP_BUILD_DIR "${LIBZIP_SRC_DIR}/build")
|
||||||
|
#set(LIBZIP_INSTALL_DIR "${LIBZIP_SRC_DIR}/install")
|
||||||
|
set(LIBZIP_BASE_DIR "${LIBZIP_SRC_DIR}/install")
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${LIBZIP_BUILD_DIR}")
|
||||||
|
|
||||||
|
# Проверяем, собран ли уже libzip
|
||||||
|
set(_have_zip FALSE)
|
||||||
|
foreach(candidate
|
||||||
|
"${LIBZIP_BASE_DIR}-Debug/lib/zip.lib"
|
||||||
|
"${LIBZIP_BASE_DIR}-Release/lib/zip.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${candidate}")
|
||||||
|
set(_have_zip TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _have_zip)
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
log("Configuring libzip (${cfg})...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-G "${CMAKE_GENERATOR}"
|
||||||
|
-S "${LIBZIP_SRC_DIR}"
|
||||||
|
-B "${LIBZIP_SRC_DIR}/build-${cfg}"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${LIBZIP_BASE_DIR}-${cfg}
|
||||||
|
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
|
||||||
|
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
|
||||||
|
-DENABLE_COMMONCRYPTO=OFF
|
||||||
|
-DENABLE_GNUTLS=OFF
|
||||||
|
-DENABLE_MBEDTLS=OFF
|
||||||
|
-DENABLE_OPENSSL=OFF
|
||||||
|
-DENABLE_WINDOWS_CRYPTO=OFF
|
||||||
|
-DENABLE_FUZZ=OFF
|
||||||
|
RESULT_VARIABLE _zip_cfg_res
|
||||||
|
)
|
||||||
|
if(NOT _zip_cfg_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "libzip configure failed")
|
||||||
|
endif()
|
||||||
|
log("Building libzip (${cfg}) ...")
|
||||||
|
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} --build "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
|
||||||
|
RESULT_VARIABLE _zip_build_res
|
||||||
|
)
|
||||||
|
if(NOT _zip_build_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "libzip build failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Installing libzip (${cfg}) ...")
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} --install "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
|
||||||
|
RESULT_VARIABLE _zip_inst_res
|
||||||
|
)
|
||||||
|
if(NOT _zip_inst_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "libzip install failed for configuration ${cfg}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
add_library(libzip_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(libzip_external_lib PROPERTIES
|
||||||
|
IMPORTED_LOCATION_DEBUG "${LIBZIP_BASE_DIR}-Debug/lib/zip.lib" # ИСПРАВЛЕНО
|
||||||
|
IMPORTED_LOCATION_RELEASE "${LIBZIP_BASE_DIR}-Release/lib/zip.lib" # ИСПРАВЛЕНО
|
||||||
|
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/include,${LIBZIP_BASE_DIR}-Release/include>"
|
||||||
|
# libzip требует zlib для линковки
|
||||||
|
INTERFACE_LINK_LIBRARIES zlib_external_lib
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 5) FreeType (2.14.1) - dependency for SDL_ttf
|
||||||
|
# ===========================================
|
||||||
|
set(FREETYPE_SRC_DIR "${THIRDPARTY_DIR}/freetype-2.14.1")
|
||||||
|
set(FREETYPE_BASE_DIR "${FREETYPE_SRC_DIR}/install")
|
||||||
|
|
||||||
|
set(_have_freetype TRUE)
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
if(NOT EXISTS "${FREETYPE_BASE_DIR}-${cfg}/lib/freetype.lib" AND
|
||||||
|
NOT EXISTS "${FREETYPE_BASE_DIR}-${cfg}/lib/freetyped.lib")
|
||||||
|
set(_have_freetype FALSE)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _have_freetype)
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
log("Configuring FreeType (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-G "${CMAKE_GENERATOR}"
|
||||||
|
-S "${FREETYPE_SRC_DIR}"
|
||||||
|
-B "${FREETYPE_SRC_DIR}/build-${cfg}"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${FREETYPE_BASE_DIR}-${cfg}
|
||||||
|
-DCMAKE_PREFIX_PATH="${ZLIB_INSTALL_DIR};${LIBPNG_INSTALL_DIR}"
|
||||||
|
-DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE
|
||||||
|
-DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE
|
||||||
|
-DFT_DISABLE_BROTLI=ON
|
||||||
|
-DBUILD_SHARED_LIBS=OFF
|
||||||
|
RESULT_VARIABLE _ft_cfg_res
|
||||||
|
)
|
||||||
|
if(NOT _ft_cfg_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "FreeType configure failed for ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Building FreeType (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--build "${FREETYPE_SRC_DIR}/build-${cfg}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _ft_build_res
|
||||||
|
)
|
||||||
|
if(NOT _ft_build_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "FreeType build failed for ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Installing FreeType (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--install "${FREETYPE_SRC_DIR}/build-${cfg}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _ft_inst_res
|
||||||
|
)
|
||||||
|
if(NOT _ft_inst_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "FreeType install failed for ${cfg}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_ft_debug_lib "")
|
||||||
|
foreach(cand
|
||||||
|
"${FREETYPE_BASE_DIR}-Debug/lib/freetyped.lib"
|
||||||
|
"${FREETYPE_BASE_DIR}-Debug/lib/freetype.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${cand}")
|
||||||
|
set(_ft_debug_lib "${cand}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(_ft_release_lib "")
|
||||||
|
foreach(cand
|
||||||
|
"${FREETYPE_BASE_DIR}-Release/lib/freetype.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${cand}")
|
||||||
|
set(_ft_release_lib "${cand}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(_ft_debug_lib STREQUAL "" OR _ft_release_lib STREQUAL "")
|
||||||
|
message(FATAL_ERROR "FreeType libs not found in ${FREETYPE_BASE_DIR}-Debug/Release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(freetype_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(freetype_external_lib PROPERTIES
|
||||||
|
IMPORTED_LOCATION_DEBUG "${_ft_debug_lib}"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${_ft_release_lib}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES
|
||||||
|
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include,${FREETYPE_BASE_DIR}-Release/include>"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 6) SDL_ttf (2.24.0)
|
||||||
|
# ===========================================
|
||||||
|
set(SDL2TTF_SRC_DIR "${THIRDPARTY_DIR}/SDL_ttf-release-2.24.0")
|
||||||
|
set(SDL2TTF_BASE_DIR "${SDL2TTF_SRC_DIR}/install")
|
||||||
|
|
||||||
|
set(_have_sdl2ttf TRUE)
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
if(NOT EXISTS "${SDL2TTF_BASE_DIR}-${cfg}/lib/SDL2_ttf.lib" AND
|
||||||
|
NOT EXISTS "${SDL2TTF_BASE_DIR}-${cfg}/lib/SDL2_ttfd.lib")
|
||||||
|
set(_have_sdl2ttf FALSE)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _have_sdl2ttf)
|
||||||
|
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||||
|
|
||||||
|
if(cfg STREQUAL "Debug")
|
||||||
|
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2d.lib")
|
||||||
|
else()
|
||||||
|
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2.lib")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_FT_PREFIX "${FREETYPE_BASE_DIR}-${cfg}")
|
||||||
|
|
||||||
|
set(_FT_LIB "")
|
||||||
|
foreach(cand
|
||||||
|
"${_FT_PREFIX}/lib/freetyped.lib"
|
||||||
|
"${_FT_PREFIX}/lib/freetype.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${cand}")
|
||||||
|
set(_FT_LIB "${cand}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(_FT_LIB STREQUAL "")
|
||||||
|
message(FATAL_ERROR "FreeType library not found for ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Configuring SDL_ttf (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-G "${CMAKE_GENERATOR}"
|
||||||
|
-S "${SDL2TTF_SRC_DIR}"
|
||||||
|
-B "${SDL2TTF_SRC_DIR}/build-${cfg}"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${SDL2TTF_BASE_DIR}-${cfg}
|
||||||
|
-DCMAKE_PREFIX_PATH=${_FT_PREFIX};${SDL2_INSTALL_DIR}
|
||||||
|
-DSDL2_LIBRARY=${_SDL2_LIB}
|
||||||
|
-DSDL2_INCLUDE_DIR=${SDL2_INSTALL_DIR}/include/SDL2
|
||||||
|
-DFREETYPE_LIBRARY=${_FT_LIB}
|
||||||
|
-DFREETYPE_INCLUDE_DIR=${_FT_PREFIX}/include
|
||||||
|
-DFREETYPE_INCLUDE_DIRS=${_FT_PREFIX}/include
|
||||||
|
-DSDL2TTF_VENDORED=OFF
|
||||||
|
-DSDL2TTF_SAMPLES=OFF
|
||||||
|
RESULT_VARIABLE _ttf_cfg_res
|
||||||
|
)
|
||||||
|
if(NOT _ttf_cfg_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "SDL_ttf configure failed for ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Building SDL_ttf (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--build "${SDL2TTF_SRC_DIR}/build-${cfg}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _ttf_build_res
|
||||||
|
)
|
||||||
|
if(NOT _ttf_build_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "SDL_ttf build failed for ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
log("Installing SDL_ttf (${cfg}) ...")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
--install "${SDL2TTF_SRC_DIR}/build-${cfg}" --config ${cfg}
|
||||||
|
RESULT_VARIABLE _ttf_inst_res
|
||||||
|
)
|
||||||
|
if(NOT _ttf_inst_res EQUAL 0)
|
||||||
|
message(FATAL_ERROR "SDL_ttf install failed for ${cfg}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_ttf_debug_lib "")
|
||||||
|
foreach(cand
|
||||||
|
"${SDL2TTF_BASE_DIR}-Debug/lib/SDL2_ttfd.lib"
|
||||||
|
"${SDL2TTF_BASE_DIR}-Debug/lib/SDL2_ttf.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${cand}")
|
||||||
|
set(_ttf_debug_lib "${cand}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(_ttf_release_lib "")
|
||||||
|
foreach(cand
|
||||||
|
"${SDL2TTF_BASE_DIR}-Release/lib/SDL2_ttf.lib"
|
||||||
|
)
|
||||||
|
if(EXISTS "${cand}")
|
||||||
|
set(_ttf_release_lib "${cand}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(_ttf_debug_lib STREQUAL "" OR _ttf_release_lib STREQUAL "")
|
||||||
|
message(FATAL_ERROR "SDL_ttf libs not found in install-Debug / install-Release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(SDL2_ttf_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||||
|
set_target_properties(SDL2_ttf_external_lib PROPERTIES
|
||||||
|
IMPORTED_LOCATION_DEBUG "${_ttf_debug_lib}"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${_ttf_release_lib}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES
|
||||||
|
"$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/include,${SDL2TTF_BASE_DIR}-Release/include>;$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/include/SDL2,${SDL2TTF_BASE_DIR}-Release/include/SDL2>"
|
||||||
|
INTERFACE_LINK_LIBRARIES
|
||||||
|
"SDL2_external_lib;freetype_external_lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 7) Eigen (5.0.0.zip → eigen-5.0.0) - HEADER-ONLY
|
||||||
|
# ===========================================
|
||||||
|
set(EIGEN_SRC_DIR "${THIRDPARTY_DIR}/eigen-5.0.0")
|
||||||
|
|
||||||
|
if(NOT TARGET eigen_external_lib)
|
||||||
|
add_library(eigen_external_lib INTERFACE)
|
||||||
|
target_include_directories(eigen_external_lib INTERFACE "${EIGEN_SRC_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 8) Boost (1.90.0) - HEADER-ONLY
|
||||||
|
# ===========================================
|
||||||
|
set(BOOST_VERSION "1.90.0")
|
||||||
|
set(BOOST_ARCHIVE_NAME "boost_1_90_0.zip")
|
||||||
|
set(BOOST_ARCHIVE "${THIRDPARTY_DIR}/${BOOST_ARCHIVE_NAME}")
|
||||||
|
# Внутри архива папка называется boost_1_90_0
|
||||||
|
set(BOOST_SRC_DIR "${THIRDPARTY_DIR}/boost_1_90_0")
|
||||||
|
|
||||||
|
if(NOT TARGET boost_external_lib)
|
||||||
|
add_library(boost_external_lib INTERFACE)
|
||||||
|
# Boost заголовки находятся непосредственно в корне распакованной папки
|
||||||
|
target_include_directories(boost_external_lib INTERFACE "${BOOST_SRC_DIR}")
|
||||||
|
endif()
|
||||||
156
config/ui.json
Normal file
156
config/ui.json
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1280,
|
||||||
|
"height": 720,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "leftPanel",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 320,
|
||||||
|
"height": 400,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"name": "mainButtons",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"spacing": 10,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 300,
|
||||||
|
"height": 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "playButton",
|
||||||
|
"x": 100,
|
||||||
|
"y": 300,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/button.png",
|
||||||
|
"hover": "./resources/sand.png",
|
||||||
|
"pressed": "./resources/button.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "settingsButton",
|
||||||
|
"x": 100,
|
||||||
|
"y": 200,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "wait",
|
||||||
|
"duration": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/sand.png",
|
||||||
|
"hover": "./resources/button.png",
|
||||||
|
"pressed": "./resources/sand.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "exitButton",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "wait",
|
||||||
|
"duration": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bgScroll": {
|
||||||
|
"repeat": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
1280,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 5.0,
|
||||||
|
"easing": "linear"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/rock.png",
|
||||||
|
"hover": "./resources/button.png",
|
||||||
|
"pressed": "./resources/rock.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Slider",
|
||||||
|
"name": "musicVolumeSlider",
|
||||||
|
"x": 1140,
|
||||||
|
"y": 100,
|
||||||
|
"width": 10,
|
||||||
|
"height": 500,
|
||||||
|
"value": 0.5,
|
||||||
|
"orientation": "vertical",
|
||||||
|
"textures": {
|
||||||
|
"track": "./resources/musicVolumeBarTexture.png",
|
||||||
|
"knob": "./resources/musicVolumeBarButton.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
88
main.cpp
88
main.cpp
@ -1,88 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
65
proj-android/.gitignore
vendored
Normal file
65
proj-android/.gitignore
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Built application files
|
||||||
|
*.apk
|
||||||
|
*.ap_
|
||||||
|
*.aab
|
||||||
|
|
||||||
|
# Files for the ART/Dalvik VM
|
||||||
|
*.dex
|
||||||
|
|
||||||
|
# Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
bin/
|
||||||
|
gen/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Gradle files
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# CMake build files
|
||||||
|
CMakeFiles/
|
||||||
|
cmake_install.cmake
|
||||||
|
CMakeCache.txt
|
||||||
|
Makefile
|
||||||
|
*.cmake
|
||||||
|
|
||||||
|
# Android Studio
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
.cxx/
|
||||||
|
.gradle/
|
||||||
|
.DS_Store
|
||||||
|
/captures/
|
||||||
|
.externalNativeBuild/
|
||||||
|
|
||||||
|
# NDK
|
||||||
|
.obj/
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# SDL2 (если используешь)
|
||||||
|
libs/
|
||||||
|
obj/
|
||||||
|
|
||||||
|
# Временные файлы
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# Проектные специфичные
|
||||||
|
app/build/
|
||||||
|
*.log
|
||||||
|
app/jni/libpng
|
||||||
|
app/jni/SDL
|
||||||
|
app/jni/zlib
|
||||||
|
|
||||||
|
app/src/main/assets/resources
|
||||||
|
|
||||||
1
proj-android/README.md
Normal file
1
proj-android/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
### Перед запуском в папке ```app/jni/```(рядом с src) нужно создать три папки с исходниками библиотек ```libpng```, ```SDL```, ```zlib```
|
||||||
74
proj-android/app/build.gradle
Normal file
74
proj-android/app/build.gradle
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY');
|
||||||
|
def buildAsApplication = !buildAsLibrary
|
||||||
|
if (buildAsApplication) {
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
if (buildAsApplication) {
|
||||||
|
namespace "org.libsdl.app"
|
||||||
|
}
|
||||||
|
compileSdkVersion 34
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 34
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
externalNativeBuild {
|
||||||
|
/*ndkBuild {
|
||||||
|
arguments "APP_PLATFORM=android-19"
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
|
}*/
|
||||||
|
cmake {
|
||||||
|
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
|
||||||
|
cppFlags "-std=c++11 -frtti -fexceptions"
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applicationVariants.all { variant ->
|
||||||
|
tasks["merge${variant.name.capitalize()}Assets"]
|
||||||
|
.dependsOn("externalNativeBuild${variant.name.capitalize()}")
|
||||||
|
}
|
||||||
|
if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) {
|
||||||
|
sourceSets.main {
|
||||||
|
jniLibs.srcDir 'libs'
|
||||||
|
}
|
||||||
|
externalNativeBuild {
|
||||||
|
/*ndkBuild {
|
||||||
|
path 'jni/Android.mk'
|
||||||
|
}*/
|
||||||
|
cmake {
|
||||||
|
path 'jni/CMakeLists.txt'
|
||||||
|
version '3.22.1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
lint {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildAsLibrary) {
|
||||||
|
libraryVariants.all { variant ->
|
||||||
|
variant.outputs.all { output ->
|
||||||
|
if (output.outputFileName != null && output.outputFileName.endsWith(".aar")) {
|
||||||
|
output.outputFileName = "org.libsdl.app.aar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
}
|
||||||
68
proj-android/app/jni/CMakeLists.txt
Normal file
68
proj-android/app/jni/CMakeLists.txt
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.6)
|
||||||
|
|
||||||
|
project(GAME)
|
||||||
|
|
||||||
|
if(POLICY CMP0079)
|
||||||
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# 1. АВТО-ЗАГРУЗКА ИСХОДНИКОВ
|
||||||
|
# ==============================================================================
|
||||||
|
# Путь относительно текущего файла к папке с общими скриптами
|
||||||
|
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/FetchDependencies.cmake")
|
||||||
|
|
||||||
|
# Теперь мы уверены, что папка thirdparty заполнена исходниками.
|
||||||
|
# Определяем базовый путь для удобства:
|
||||||
|
set(TP_ROOT "${THIRDPARTY_DIR}")
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# 2. НАСТРОЙКА ЗАВИСИМОСТЕЙ (СОБИРАЕМ ИЗ ИСХОДНИКОВ)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# --- ZLIB ---
|
||||||
|
add_subdirectory("${TP_ROOT}/zlib-1.3.1" zlib-build)
|
||||||
|
|
||||||
|
# --- LIBPNG ---
|
||||||
|
set(PNG_STATIC ON CACHE BOOL "Build static library" FORCE)
|
||||||
|
set(PNG_SHARED OFF CACHE BOOL "Don't build shared library" FORCE)
|
||||||
|
set(PNG_TESTS OFF CACHE BOOL "Disable tests" FORCE)
|
||||||
|
set(PNG_TOOLS OFF CACHE BOOL "Disable tools" FORCE)
|
||||||
|
set(PNG_EXECUTABLES OFF CACHE BOOL "Disable executables" FORCE)
|
||||||
|
set(PNG_DEBUG OFF CACHE BOOL "Disable debug" FORCE)
|
||||||
|
set(SKIP_INSTALL_ALL ON CACHE BOOL "Skip installation" FORCE)
|
||||||
|
set(PNG_HARDWARE_OPTIMIZATIONS OFF CACHE BOOL "Disable hardware optimizations" FORCE)
|
||||||
|
set(PNG_ARM_NEON "off" CACHE STRING "Disable ARM NEON" FORCE)
|
||||||
|
|
||||||
|
add_subdirectory("${TP_ROOT}/libpng-1.6.51" libpng-build)
|
||||||
|
|
||||||
|
# --- SDL2 ---
|
||||||
|
# Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама
|
||||||
|
add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build)
|
||||||
|
|
||||||
|
# --- LIBZIP ---
|
||||||
|
# Отключаем поиск системных крипто-библиотек, так как в NDK их может не быть в стандартных путях
|
||||||
|
set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_MBEDTLS OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
add_subdirectory("${TP_ROOT}/libzip-1.11.4" libzip-build)
|
||||||
|
|
||||||
|
# --- EIGEN & BOOST (Header-only) ---
|
||||||
|
# Мы создаем интерфейсные таргеты здесь, чтобы они были доступны в подпапке src
|
||||||
|
if(NOT TARGET eigen_external_lib)
|
||||||
|
add_library(eigen_external_lib INTERFACE)
|
||||||
|
target_include_directories(eigen_external_lib INTERFACE "${TP_ROOT}/eigen-5.0.0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET boost_external_lib)
|
||||||
|
add_library(boost_external_lib INTERFACE)
|
||||||
|
target_include_directories(boost_external_lib INTERFACE "${TP_ROOT}/boost_1_90_0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# 3. ОСНОВНОЙ КОД
|
||||||
|
# ==============================================================================
|
||||||
|
add_subdirectory(src)
|
||||||
92
proj-android/app/jni/src/CMakeLists.txt
Normal file
92
proj-android/app/jni/src/CMakeLists.txt
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.6)
|
||||||
|
|
||||||
|
project(MY_APP CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
|
||||||
|
# Определение путей
|
||||||
|
set(SOURCE_RES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../resources")
|
||||||
|
set(TARGET_RES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../src/main/assets/resources")
|
||||||
|
|
||||||
|
# Получаем список всех файлов в исходной директории для отслеживания зависимостей
|
||||||
|
file(GLOB_RECURSE RES_FILES RELATIVE "${SOURCE_RES_DIR}" "${SOURCE_RES_DIR}/*")
|
||||||
|
|
||||||
|
set(COPY_COMMANDS "")
|
||||||
|
|
||||||
|
foreach(RES_FILE ${RES_FILES})
|
||||||
|
set(SRC "${SOURCE_RES_DIR}/${RES_FILE}")
|
||||||
|
set(DST "${TARGET_RES_DIR}/${RES_FILE}")
|
||||||
|
|
||||||
|
# Команда копирования конкретного файла, если он изменился
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${DST}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${TARGET_RES_DIR}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SRC}" "${DST}"
|
||||||
|
DEPENDS "${SRC}"
|
||||||
|
COMMENT "Syncing resource: ${RES_FILE}"
|
||||||
|
)
|
||||||
|
list(APPEND RES_OUTPUTS "${DST}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Создаем кастомную цель, которая будет запускать процесс копирования
|
||||||
|
add_custom_target(sync_resources ALL DEPENDS ${RES_OUTPUTS})
|
||||||
|
|
||||||
|
|
||||||
|
add_library(main SHARED
|
||||||
|
SDL_android_main.c
|
||||||
|
../../../../src/BoneAnimatedModel.cpp
|
||||||
|
../../../../src/Environment.cpp
|
||||||
|
../../../../src/Game.cpp
|
||||||
|
../../../../src/main.cpp
|
||||||
|
../../../../src/Projectile.cpp
|
||||||
|
../../../../src/SparkEmitter.cpp
|
||||||
|
../../../../src/TextModel.cpp
|
||||||
|
../../../../src/UiManager.cpp
|
||||||
|
../../../../src/utils/Perlin.cpp
|
||||||
|
../../../../src/utils/TaskManager.cpp
|
||||||
|
../../../../src/utils/Utils.cpp
|
||||||
|
../../../../src/planet/PlanetData.cpp
|
||||||
|
../../../../src/planet/PlanetObject.cpp
|
||||||
|
../../../../src/planet/StoneObject.cpp
|
||||||
|
../../../../src/render/FrameBuffer.cpp
|
||||||
|
../../../../src/render/Renderer.cpp
|
||||||
|
../../../../src/render/ShaderManager.cpp
|
||||||
|
../../../../src/render/TextureManager.cpp
|
||||||
|
../../../../src/render/OpenGlExtensions.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Подключаем заголовки
|
||||||
|
target_include_directories(main PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../zlib
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../libpng
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/boost_1_90_0
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../../../src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../libzip
|
||||||
|
)
|
||||||
|
|
||||||
|
# ВАЖНО: Линкуемся с png_static (статика) или png_shared (динамика)
|
||||||
|
# Так как мы установили PNG_STATIC=ON и PNG_SHARED=OFF,
|
||||||
|
# должна создаться цель png_static
|
||||||
|
target_link_libraries(main
|
||||||
|
png_static # ← ЭТО ПРАВИЛЬНОЕ ИМЯ ЦЕЛИ!
|
||||||
|
z
|
||||||
|
SDL2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
find_library(OPENGLES2_LIB GLESv2)
|
||||||
|
|
||||||
|
target_link_libraries(main
|
||||||
|
${OPENGLES2_LIB} # OpenGL ES 2.0/3.0
|
||||||
|
log
|
||||||
|
android
|
||||||
|
OpenSLES
|
||||||
|
dl
|
||||||
|
zip
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(main sync_resources)
|
||||||
9
proj-android/app/jni/src/SDL_android_main.c
Normal file
9
proj-android/app/jni/src/SDL_android_main.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
extern int SDL_main(int argc, char *argv[]);
|
||||||
|
|
||||||
|
void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {
|
||||||
|
/* Run the application code! */
|
||||||
|
SDL_main(0, NULL);
|
||||||
|
}
|
||||||
98
proj-android/app/proguard-rules.pro
vendored
Normal file
98
proj-android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in [sdk]/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection {
|
||||||
|
void nativeCommitText(java.lang.String, int);
|
||||||
|
void nativeGenerateScancodeForUnichar(char);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses class org.libsdl.app.SDLActivity {
|
||||||
|
# for some reason these aren't compatible with allowoptimization modifier
|
||||||
|
boolean supportsRelativeMouse();
|
||||||
|
void setWindowStyle(boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
|
||||||
|
java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it
|
||||||
|
boolean onNativeSoftReturnKey();
|
||||||
|
void onNativeKeyboardFocusLost();
|
||||||
|
boolean isScreenKeyboardShown();
|
||||||
|
android.util.DisplayMetrics getDisplayDPI();
|
||||||
|
java.lang.String clipboardGetText();
|
||||||
|
boolean clipboardHasText();
|
||||||
|
void clipboardSetText(java.lang.String);
|
||||||
|
int createCustomCursor(int[], int, int, int, int);
|
||||||
|
void destroyCustomCursor(int);
|
||||||
|
android.content.Context getContext();
|
||||||
|
boolean getManifestEnvironmentVariables();
|
||||||
|
android.view.Surface getNativeSurface();
|
||||||
|
void initTouch();
|
||||||
|
boolean isAndroidTV();
|
||||||
|
boolean isChromebook();
|
||||||
|
boolean isDeXMode();
|
||||||
|
boolean isTablet();
|
||||||
|
void manualBackButton();
|
||||||
|
int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]);
|
||||||
|
void minimizeWindow();
|
||||||
|
int openURL(java.lang.String);
|
||||||
|
void requestPermission(java.lang.String, int);
|
||||||
|
int showToast(java.lang.String, int, int, int, int);
|
||||||
|
boolean sendMessage(int, int);
|
||||||
|
boolean setActivityTitle(java.lang.String);
|
||||||
|
boolean setCustomCursor(int);
|
||||||
|
void setOrientation(int, int, boolean, java.lang.String);
|
||||||
|
boolean setRelativeMouseEnabled(boolean);
|
||||||
|
boolean setSystemCursor(int);
|
||||||
|
boolean shouldMinimizeOnFocusLoss();
|
||||||
|
boolean showTextInput(int, int, int, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager {
|
||||||
|
boolean initialize(boolean, boolean);
|
||||||
|
boolean openDevice(int);
|
||||||
|
int sendOutputReport(int, byte[]);
|
||||||
|
int sendFeatureReport(int, byte[]);
|
||||||
|
boolean getFeatureReport(int, byte[]);
|
||||||
|
void closeDevice(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager {
|
||||||
|
int[] getAudioOutputDevices();
|
||||||
|
int[] getAudioInputDevices();
|
||||||
|
int[] audioOpen(int, int, int, int, int);
|
||||||
|
void audioWriteFloatBuffer(float[]);
|
||||||
|
void audioWriteShortBuffer(short[]);
|
||||||
|
void audioWriteByteBuffer(byte[]);
|
||||||
|
void audioClose();
|
||||||
|
int[] captureOpen(int, int, int, int, int);
|
||||||
|
int captureReadFloatBuffer(float[], boolean);
|
||||||
|
int captureReadShortBuffer(short[], boolean);
|
||||||
|
int captureReadByteBuffer(byte[], boolean);
|
||||||
|
void captureClose();
|
||||||
|
void audioSetThreadPriority(boolean, int);
|
||||||
|
native int nativeSetupJNI();
|
||||||
|
native void removeAudioDevice(boolean, int);
|
||||||
|
native void addAudioDevice(boolean, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager {
|
||||||
|
void pollInputDevices();
|
||||||
|
void pollHapticDevices();
|
||||||
|
void hapticRun(int, float, int);
|
||||||
|
void hapticStop(int);
|
||||||
|
}
|
||||||
99
proj-android/app/src/main/AndroidManifest.xml
Normal file
99
proj-android/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Replace com.test.game with the identifier of your game below, e.g.
|
||||||
|
com.gamemaker.game
|
||||||
|
-->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0"
|
||||||
|
android:installLocation="auto">
|
||||||
|
|
||||||
|
<!-- OpenGL ES 2.0 -->
|
||||||
|
<uses-feature android:glEsVersion="0x00020000" />
|
||||||
|
|
||||||
|
<!-- Touchscreen support -->
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.touchscreen"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- Game controller support -->
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.bluetooth"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.gamepad"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.usb.host"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- External mouse input events -->
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.type.pc"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- Audio recording support -->
|
||||||
|
<!-- if you want to capture audio, uncomment this. -->
|
||||||
|
<!-- <uses-feature
|
||||||
|
android:name="android.hardware.microphone"
|
||||||
|
android:required="false" /> -->
|
||||||
|
|
||||||
|
<!-- Allow downloading to the external storage on Android 5.1 and older -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="22" /> -->
|
||||||
|
|
||||||
|
<!-- Allow access to Bluetooth devices -->
|
||||||
|
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> -->
|
||||||
|
|
||||||
|
<!-- Allow access to the vibrator -->
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
|
||||||
|
<!-- if you want to capture audio, uncomment this. -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
|
||||||
|
|
||||||
|
<!-- Create a Java class extending SDLActivity and place it in a
|
||||||
|
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
|
||||||
|
|
||||||
|
then replace "SDLActivity" with the name of your class (e.g. "MyGame")
|
||||||
|
in the XML below.
|
||||||
|
|
||||||
|
An example Java class can be found in README-android.md
|
||||||
|
-->
|
||||||
|
<application android:label="@string/app_name"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
android:hardwareAccelerated="true" >
|
||||||
|
|
||||||
|
<!-- Example of setting SDL hints from AndroidManifest.xml:
|
||||||
|
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<activity android:name="SDLActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:alwaysRetainTaskState="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||||
|
android:preferMinimalPostProcessing="true"
|
||||||
|
android:exported="true"
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- Let Android know that we can handle some USB devices and should receive this event -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- Drop file event -->
|
||||||
|
<!--
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
-->
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
22
proj-android/app/src/main/java/org/libsdl/app/HIDDevice.java
Normal file
22
proj-android/app/src/main/java/org/libsdl/app/HIDDevice.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
|
||||||
|
interface HIDDevice
|
||||||
|
{
|
||||||
|
public int getId();
|
||||||
|
public int getVendorId();
|
||||||
|
public int getProductId();
|
||||||
|
public String getSerialNumber();
|
||||||
|
public int getVersion();
|
||||||
|
public String getManufacturerName();
|
||||||
|
public String getProductName();
|
||||||
|
public UsbDevice getDevice();
|
||||||
|
public boolean open();
|
||||||
|
public int sendFeatureReport(byte[] report);
|
||||||
|
public int sendOutputReport(byte[] report);
|
||||||
|
public boolean getFeatureReport(byte[] report);
|
||||||
|
public void setFrozen(boolean frozen);
|
||||||
|
public void close();
|
||||||
|
public void shutdown();
|
||||||
|
}
|
||||||
@ -0,0 +1,650 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCallback;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.BluetoothGattService;
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.*;
|
||||||
|
|
||||||
|
//import com.android.internal.util.HexDump;
|
||||||
|
|
||||||
|
import java.lang.Runnable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
|
||||||
|
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
private HIDDeviceManager mManager;
|
||||||
|
private BluetoothDevice mDevice;
|
||||||
|
private int mDeviceId;
|
||||||
|
private BluetoothGatt mGatt;
|
||||||
|
private boolean mIsRegistered = false;
|
||||||
|
private boolean mIsConnected = false;
|
||||||
|
private boolean mIsChromebook = false;
|
||||||
|
private boolean mIsReconnecting = false;
|
||||||
|
private boolean mFrozen = false;
|
||||||
|
private LinkedList<GattOperation> mOperations;
|
||||||
|
GattOperation mCurrentOperation = null;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
|
private static final int TRANSPORT_AUTO = 0;
|
||||||
|
private static final int TRANSPORT_BREDR = 1;
|
||||||
|
private static final int TRANSPORT_LE = 2;
|
||||||
|
|
||||||
|
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
|
||||||
|
|
||||||
|
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
|
||||||
|
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
|
||||||
|
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
|
||||||
|
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
|
||||||
|
|
||||||
|
static class GattOperation {
|
||||||
|
private enum Operation {
|
||||||
|
CHR_READ,
|
||||||
|
CHR_WRITE,
|
||||||
|
ENABLE_NOTIFICATION
|
||||||
|
}
|
||||||
|
|
||||||
|
Operation mOp;
|
||||||
|
UUID mUuid;
|
||||||
|
byte[] mValue;
|
||||||
|
BluetoothGatt mGatt;
|
||||||
|
boolean mResult = true;
|
||||||
|
|
||||||
|
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
|
||||||
|
mGatt = gatt;
|
||||||
|
mOp = operation;
|
||||||
|
mUuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
|
||||||
|
mGatt = gatt;
|
||||||
|
mOp = operation;
|
||||||
|
mUuid = uuid;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
// This is executed in main thread
|
||||||
|
BluetoothGattCharacteristic chr;
|
||||||
|
|
||||||
|
switch (mOp) {
|
||||||
|
case CHR_READ:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
|
||||||
|
if (!mGatt.readCharacteristic(chr)) {
|
||||||
|
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
break;
|
||||||
|
case CHR_WRITE:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
|
||||||
|
chr.setValue(mValue);
|
||||||
|
if (!mGatt.writeCharacteristic(chr)) {
|
||||||
|
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
break;
|
||||||
|
case ENABLE_NOTIFICATION:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
|
||||||
|
if (chr != null) {
|
||||||
|
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||||
|
if (cccd != null) {
|
||||||
|
int properties = chr.getProperties();
|
||||||
|
byte[] value;
|
||||||
|
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
|
||||||
|
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||||
|
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
|
||||||
|
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to start notifications on input characteristic");
|
||||||
|
mResult = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGatt.setCharacteristicNotification(chr, true);
|
||||||
|
cccd.setValue(value);
|
||||||
|
if (!mGatt.writeDescriptor(cccd)) {
|
||||||
|
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finish() {
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||||
|
BluetoothGattService valveService = mGatt.getService(steamControllerService);
|
||||||
|
if (valveService == null)
|
||||||
|
return null;
|
||||||
|
return valveService.getCharacteristic(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
|
||||||
|
return new GattOperation(gatt, Operation.CHR_READ, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
|
||||||
|
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
|
||||||
|
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
|
||||||
|
mManager = manager;
|
||||||
|
mDevice = device;
|
||||||
|
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
|
||||||
|
mIsRegistered = false;
|
||||||
|
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||||
|
mOperations = new LinkedList<GattOperation>();
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
mGatt = connectGatt();
|
||||||
|
// final HIDDeviceBLESteamController finalThis = this;
|
||||||
|
// mHandler.postDelayed(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// finalThis.checkConnectionForChromebookIssue();
|
||||||
|
// }
|
||||||
|
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("SteamController.%s", mDevice.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BluetoothGatt getGatt() {
|
||||||
|
return mGatt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||||
|
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||||
|
private BluetoothGatt connectGatt(boolean managed) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||||
|
try {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BluetoothGatt connectGatt() {
|
||||||
|
return connectGatt(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getConnectionState() {
|
||||||
|
|
||||||
|
Context context = mManager.getContext();
|
||||||
|
if (context == null) {
|
||||||
|
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (btManager == null) {
|
||||||
|
// This device doesn't support Bluetooth. We should never be here, because how did
|
||||||
|
// we instantiate a device to start with?
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reconnect() {
|
||||||
|
|
||||||
|
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkConnectionForChromebookIssue() {
|
||||||
|
if (!mIsChromebook) {
|
||||||
|
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
|
||||||
|
// over and over.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int connectionState = getConnectionState();
|
||||||
|
|
||||||
|
switch (connectionState) {
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
if (!mIsConnected) {
|
||||||
|
// We are in the Bad Chromebook Place. We can force a disconnect
|
||||||
|
// to try to recover.
|
||||||
|
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!isRegistered()) {
|
||||||
|
if (mGatt.getServices().size() > 0) {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
|
||||||
|
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTING:
|
||||||
|
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HIDDeviceBLESteamController finalThis = this;
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finalThis.checkConnectionForChromebookIssue();
|
||||||
|
}
|
||||||
|
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRegistered() {
|
||||||
|
return mIsRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRegistered() {
|
||||||
|
mIsRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean probeService(HIDDeviceBLESteamController controller) {
|
||||||
|
|
||||||
|
if (isRegistered()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIsConnected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v(TAG, "probeService controller=" + controller);
|
||||||
|
|
||||||
|
for (BluetoothGattService service : mGatt.getServices()) {
|
||||||
|
if (service.getUuid().equals(steamControllerService)) {
|
||||||
|
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
|
||||||
|
|
||||||
|
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
|
||||||
|
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||||
|
Log.v(TAG, "Found input characteristic");
|
||||||
|
// Start notifications
|
||||||
|
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||||
|
if (cccd != null) {
|
||||||
|
enableNotification(chr.getUuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
|
||||||
|
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
|
||||||
|
mIsConnected = false;
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void finishCurrentGattOperation() {
|
||||||
|
GattOperation op = null;
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation != null) {
|
||||||
|
op = mCurrentOperation;
|
||||||
|
mCurrentOperation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op != null) {
|
||||||
|
boolean result = op.finish(); // TODO: Maybe in main thread as well?
|
||||||
|
|
||||||
|
// Our operation failed, let's add it back to the beginning of our queue.
|
||||||
|
if (!result) {
|
||||||
|
mOperations.addFirst(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executeNextGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeNextGattOperation() {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mOperations.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCurrentOperation = mOperations.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run in main thread
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation == null) {
|
||||||
|
Log.e(TAG, "Current operation null in executor?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCurrentOperation.run();
|
||||||
|
// now wait for the GATT callback and when it comes, finish this operation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueGattOperation(GattOperation op) {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
mOperations.add(op);
|
||||||
|
}
|
||||||
|
executeNextGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableNotification(UUID chrUuid) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeCharacteristic(UUID uuid, byte[] value) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readCharacteristic(UUID uuid) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////// BluetoothGattCallback overridden methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
|
||||||
|
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
|
||||||
|
mIsReconnecting = false;
|
||||||
|
if (newState == 2) {
|
||||||
|
mIsConnected = true;
|
||||||
|
// Run directly, without GattOperation
|
||||||
|
if (!isRegistered()) {
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mGatt.discoverServices();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (newState == 0) {
|
||||||
|
mIsConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||||
|
//Log.v(TAG, "onServicesDiscovered status=" + status);
|
||||||
|
if (status == 0) {
|
||||||
|
if (gatt.getServices().size() == 0) {
|
||||||
|
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mIsConnected = false;
|
||||||
|
gatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
|
||||||
|
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(reportCharacteristic)) {
|
||||||
|
// Only register controller with the native side once it has been fully configured
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
|
||||||
|
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
|
||||||
|
setRegistered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
// Enable this for verbose logging of controller input reports
|
||||||
|
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
|
||||||
|
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
//Log.v(TAG, "onDescriptorRead status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
|
||||||
|
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
|
||||||
|
|
||||||
|
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||||
|
boolean hasWrittenInputDescriptor = true;
|
||||||
|
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
|
||||||
|
if (reportChr != null) {
|
||||||
|
Log.v(TAG, "Writing report characteristic to enter valve mode");
|
||||||
|
reportChr.setValue(enterValveMode);
|
||||||
|
gatt.writeCharacteristic(reportChr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||||
|
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||||
|
//Log.v(TAG, "onReadRemoteRssi status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||||
|
//Log.v(TAG, "onMtuChanged status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////// Public API
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId() {
|
||||||
|
// Valve Corporation
|
||||||
|
final int VALVE_USB_VID = 0x28DE;
|
||||||
|
return VALVE_USB_VID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId() {
|
||||||
|
// We don't have an easy way to query from the Bluetooth device, but we know what it is
|
||||||
|
final int D0G_BLE2_PID = 0x1106;
|
||||||
|
return D0G_BLE2_PID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerialNumber() {
|
||||||
|
// This will be read later via feature report by Steam
|
||||||
|
return "12345";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturerName() {
|
||||||
|
return "Valve Corporation";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductName() {
|
||||||
|
return "Steam Controller";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsbDevice getDevice() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendFeatureReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to skip the first byte, as that doesn't go over the air
|
||||||
|
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
|
||||||
|
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
|
||||||
|
writeCharacteristic(reportCharacteristic, actual_report);
|
||||||
|
return report.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendOutputReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
|
||||||
|
writeCharacteristic(reportCharacteristic, report);
|
||||||
|
return report.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFeatureReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.v(TAG, "getFeatureReport");
|
||||||
|
readCharacteristic(reportCharacteristic);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
mFrozen = frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
|
||||||
|
BluetoothGatt g = mGatt;
|
||||||
|
if (g != null) {
|
||||||
|
g.disconnect();
|
||||||
|
g.close();
|
||||||
|
mGatt = null;
|
||||||
|
}
|
||||||
|
mManager = null;
|
||||||
|
mIsRegistered = false;
|
||||||
|
mIsConnected = false;
|
||||||
|
mOperations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,698 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HIDDeviceManager {
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
|
||||||
|
|
||||||
|
private static HIDDeviceManager sManager;
|
||||||
|
private static int sManagerRefCount = 0;
|
||||||
|
|
||||||
|
public static HIDDeviceManager acquire(Context context) {
|
||||||
|
if (sManagerRefCount == 0) {
|
||||||
|
sManager = new HIDDeviceManager(context);
|
||||||
|
}
|
||||||
|
++sManagerRefCount;
|
||||||
|
return sManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(HIDDeviceManager manager) {
|
||||||
|
if (manager == sManager) {
|
||||||
|
--sManagerRefCount;
|
||||||
|
if (sManagerRefCount == 0) {
|
||||||
|
sManager.close();
|
||||||
|
sManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
|
||||||
|
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
|
||||||
|
private int mNextDeviceId = 0;
|
||||||
|
private SharedPreferences mSharedPreferences = null;
|
||||||
|
private boolean mIsChromebook = false;
|
||||||
|
private UsbManager mUsbManager;
|
||||||
|
private Handler mHandler;
|
||||||
|
private BluetoothManager mBluetoothManager;
|
||||||
|
private List<BluetoothDevice> mLastBluetoothDevices;
|
||||||
|
|
||||||
|
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDeviceAttached(usbDevice);
|
||||||
|
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDeviceDetached(usbDevice);
|
||||||
|
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
// Bluetooth device was connected. If it was a Steam Controller, handle it
|
||||||
|
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
Log.d(TAG, "Bluetooth device connected: " + device);
|
||||||
|
|
||||||
|
if (isSteamController(device)) {
|
||||||
|
connectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bluetooth device was disconnected, remove from controller manager (if any)
|
||||||
|
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
Log.d(TAG, "Bluetooth device disconnected: " + device);
|
||||||
|
|
||||||
|
disconnectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private HIDDeviceManager(final Context context) {
|
||||||
|
mContext = context;
|
||||||
|
|
||||||
|
HIDDeviceRegisterCallback();
|
||||||
|
|
||||||
|
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
|
||||||
|
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||||
|
|
||||||
|
// if (shouldClear) {
|
||||||
|
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||||
|
// spedit.clear();
|
||||||
|
// spedit.commit();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
{
|
||||||
|
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceIDForIdentifier(String identifier) {
|
||||||
|
SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||||
|
|
||||||
|
int result = mSharedPreferences.getInt(identifier, 0);
|
||||||
|
if (result == 0) {
|
||||||
|
result = mNextDeviceId++;
|
||||||
|
spedit.putInt("next_device_id", mNextDeviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
spedit.putInt(identifier, result);
|
||||||
|
spedit.commit();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUSB() {
|
||||||
|
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
|
||||||
|
if (mUsbManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Logging
|
||||||
|
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||||
|
Log.i(TAG,"Path: " + device.getDeviceName());
|
||||||
|
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
|
||||||
|
Log.i(TAG,"Product: " + device.getProductName());
|
||||||
|
Log.i(TAG,"ID: " + device.getDeviceId());
|
||||||
|
Log.i(TAG,"Class: " + device.getDeviceClass());
|
||||||
|
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
|
||||||
|
Log.i(TAG,"Vendor ID " + device.getVendorId());
|
||||||
|
Log.i(TAG,"Product ID: " + device.getProductId());
|
||||||
|
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
|
||||||
|
Log.i(TAG,"---------------------------------------");
|
||||||
|
|
||||||
|
// Get interface details
|
||||||
|
for (int index = 0; index < device.getInterfaceCount(); index++) {
|
||||||
|
UsbInterface mUsbInterface = device.getInterface(index);
|
||||||
|
Log.i(TAG," ***** *****");
|
||||||
|
Log.i(TAG," Interface index: " + index);
|
||||||
|
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
|
||||||
|
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
|
||||||
|
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
|
||||||
|
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
|
||||||
|
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
|
||||||
|
|
||||||
|
// Get endpoint details
|
||||||
|
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
|
||||||
|
{
|
||||||
|
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
|
||||||
|
Log.i(TAG," ++++ ++++ ++++");
|
||||||
|
Log.i(TAG," Endpoint index: " + epi);
|
||||||
|
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
|
||||||
|
Log.i(TAG," Direction: " + mEndpoint.getDirection());
|
||||||
|
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
|
||||||
|
Log.i(TAG," Interval: " + mEndpoint.getInterval());
|
||||||
|
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
|
||||||
|
Log.i(TAG," Type: " + mEndpoint.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG," No more devices connected.");
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Register for USB broadcasts and permission completions
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||||
|
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||||
|
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||||
|
mContext.registerReceiver(mUsbBroadcast, filter);
|
||||||
|
|
||||||
|
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
|
||||||
|
handleUsbDeviceAttached(usbDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UsbManager getUSBManager() {
|
||||||
|
return mUsbManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownUSB() {
|
||||||
|
try {
|
||||||
|
mContext.unregisterReceiver(mUsbBroadcast);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We may not have registered, that's okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
final int XB360_IFACE_SUBCLASS = 93;
|
||||||
|
final int XB360_IFACE_PROTOCOL = 1; // Wired
|
||||||
|
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||||
|
final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x0079, // GPD Win 2
|
||||||
|
0x044f, // Thrustmaster
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x046d, // Logitech
|
||||||
|
0x056e, // Elecom
|
||||||
|
0x06a3, // Saitek
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x07ff, // Mad Catz
|
||||||
|
0x0e6f, // PDP
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x1038, // SteelSeries
|
||||||
|
0x11c9, // Nacon
|
||||||
|
0x12ab, // Unknown
|
||||||
|
0x1430, // RedOctane
|
||||||
|
0x146b, // BigBen
|
||||||
|
0x1532, // Razer Sabertooth
|
||||||
|
0x15e4, // Numark
|
||||||
|
0x162e, // Joytech
|
||||||
|
0x1689, // Razer Onza
|
||||||
|
0x1949, // Lab126, Inc.
|
||||||
|
0x1bad, // Harmonix
|
||||||
|
0x20d6, // PowerA
|
||||||
|
0x24c6, // PowerA
|
||||||
|
0x2c22, // Qanba
|
||||||
|
0x2dc8, // 8BitDo
|
||||||
|
0x9886, // ASTRO Gaming
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||||
|
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
|
||||||
|
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
|
||||||
|
int vendor_id = usbDevice.getVendorId();
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (vendor_id == supportedVid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
final int XB1_IFACE_SUBCLASS = 71;
|
||||||
|
final int XB1_IFACE_PROTOCOL = 208;
|
||||||
|
final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x03f0, // HP
|
||||||
|
0x044f, // Thrustmaster
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x0b05, // ASUS
|
||||||
|
0x0e6f, // PDP
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x10f5, // Turtle Beach
|
||||||
|
0x1532, // Razer Wildcat
|
||||||
|
0x20d6, // PowerA
|
||||||
|
0x24c6, // PowerA
|
||||||
|
0x2dc8, // 8BitDo
|
||||||
|
0x2e24, // Hyperkin
|
||||||
|
0x3537, // GameSir
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usbInterface.getId() == 0 &&
|
||||||
|
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||||
|
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||||
|
int vendor_id = usbDevice.getVendorId();
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (vendor_id == supportedVid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
|
||||||
|
connectHIDDeviceUSB(usbDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
|
||||||
|
List<Integer> devices = new ArrayList<Integer>();
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
if (usbDevice.equals(device.getDevice())) {
|
||||||
|
devices.add(device.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int id : devices) {
|
||||||
|
HIDDevice device = mDevicesById.get(id);
|
||||||
|
mDevicesById.remove(id);
|
||||||
|
device.shutdown();
|
||||||
|
HIDDeviceDisconnected(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
if (usbDevice.equals(device.getDevice())) {
|
||||||
|
boolean opened = false;
|
||||||
|
if (permission_granted) {
|
||||||
|
opened = device.open();
|
||||||
|
}
|
||||||
|
HIDDeviceOpenResult(device.getId(), opened);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
|
||||||
|
synchronized (this) {
|
||||||
|
int interface_mask = 0;
|
||||||
|
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
|
||||||
|
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
|
||||||
|
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
|
||||||
|
// Check to see if we've already added this interface
|
||||||
|
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
|
||||||
|
int interface_id = usbInterface.getId();
|
||||||
|
if ((interface_mask & (1 << interface_id)) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
interface_mask |= (1 << interface_id);
|
||||||
|
|
||||||
|
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
|
||||||
|
int id = device.getId();
|
||||||
|
mDevicesById.put(id, device);
|
||||||
|
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeBluetooth() {
|
||||||
|
Log.d(TAG, "Initializing Bluetooth");
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
|
||||||
|
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||||
|
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find bonded bluetooth controllers and create SteamControllers for them
|
||||||
|
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (mBluetoothManager == null) {
|
||||||
|
// This device doesn't support Bluetooth.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
|
||||||
|
if (btAdapter == null) {
|
||||||
|
// This device has Bluetooth support in the codebase, but has no available adapters.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get our bonded devices.
|
||||||
|
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
|
||||||
|
|
||||||
|
Log.d(TAG, "Bluetooth device available: " + device);
|
||||||
|
if (isSteamController(device)) {
|
||||||
|
connectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: These don't work on Chromebooks, to my undying dismay.
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||||
|
mContext.registerReceiver(mBluetoothBroadcast, filter);
|
||||||
|
|
||||||
|
if (mIsChromebook) {
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
|
||||||
|
|
||||||
|
// final HIDDeviceManager finalThis = this;
|
||||||
|
// mHandler.postDelayed(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// finalThis.chromebookConnectionHandler();
|
||||||
|
// }
|
||||||
|
// }, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownBluetooth() {
|
||||||
|
try {
|
||||||
|
mContext.unregisterReceiver(mBluetoothBroadcast);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We may not have registered, that's okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
|
||||||
|
// This function provides a sort of dummy version of that, watching for changes in the
|
||||||
|
// connected devices and attempting to add controllers as things change.
|
||||||
|
public void chromebookConnectionHandler() {
|
||||||
|
if (!mIsChromebook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
|
||||||
|
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
|
||||||
|
|
||||||
|
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
|
||||||
|
|
||||||
|
for (BluetoothDevice bluetoothDevice : currentConnected) {
|
||||||
|
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
|
||||||
|
connected.add(bluetoothDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
|
||||||
|
if (!currentConnected.contains(bluetoothDevice)) {
|
||||||
|
disconnected.add(bluetoothDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastBluetoothDevices = currentConnected;
|
||||||
|
|
||||||
|
for (BluetoothDevice bluetoothDevice : disconnected) {
|
||||||
|
disconnectBluetoothDevice(bluetoothDevice);
|
||||||
|
}
|
||||||
|
for (BluetoothDevice bluetoothDevice : connected) {
|
||||||
|
connectBluetoothDevice(bluetoothDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
final HIDDeviceManager finalThis = this;
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finalThis.chromebookConnectionHandler();
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
|
||||||
|
synchronized (this) {
|
||||||
|
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
|
||||||
|
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
|
||||||
|
|
||||||
|
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||||
|
device.reconnect();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
|
||||||
|
int id = device.getId();
|
||||||
|
mBluetoothDevices.put(bluetoothDevice, device);
|
||||||
|
mDevicesById.put(id, device);
|
||||||
|
|
||||||
|
// The Steam Controller will mark itself connected once initialization is complete
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
synchronized (this) {
|
||||||
|
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||||
|
if (device == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int id = device.getId();
|
||||||
|
mBluetoothDevices.remove(bluetoothDevice);
|
||||||
|
mDevicesById.remove(id);
|
||||||
|
device.shutdown();
|
||||||
|
HIDDeviceDisconnected(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
|
||||||
|
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
|
||||||
|
if (bluetoothDevice == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device has no local name, we really don't want to try an equality check against it.
|
||||||
|
if (bluetoothDevice.getName() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
shutdownUSB();
|
||||||
|
shutdownBluetooth();
|
||||||
|
synchronized (this) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
device.shutdown();
|
||||||
|
}
|
||||||
|
mDevicesById.clear();
|
||||||
|
mBluetoothDevices.clear();
|
||||||
|
HIDDeviceReleaseCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
synchronized (this) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
device.setFrozen(frozen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private HIDDevice getDevice(int id) {
|
||||||
|
synchronized (this) {
|
||||||
|
HIDDevice result = mDevicesById.get(id);
|
||||||
|
if (result == null) {
|
||||||
|
Log.v(TAG, "No device for id: " + id);
|
||||||
|
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////// JNI interface functions
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public boolean initialize(boolean usb, boolean bluetooth) {
|
||||||
|
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
|
||||||
|
|
||||||
|
if (usb) {
|
||||||
|
initializeUSB();
|
||||||
|
}
|
||||||
|
if (bluetooth) {
|
||||||
|
initializeBluetooth();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean openDevice(int deviceID) {
|
||||||
|
Log.v(TAG, "openDevice deviceID=" + deviceID);
|
||||||
|
HIDDevice device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look to see if this is a USB device and we have permission to access it
|
||||||
|
UsbDevice usbDevice = device.getDevice();
|
||||||
|
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
|
||||||
|
HIDDeviceOpenPending(deviceID);
|
||||||
|
try {
|
||||||
|
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||||
|
int flags;
|
||||||
|
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||||
|
flags = FLAG_MUTABLE;
|
||||||
|
} else {
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) {
|
||||||
|
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||||
|
intent.setPackage(mContext.getPackageName());
|
||||||
|
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
|
||||||
|
} else {
|
||||||
|
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
|
||||||
|
HIDDeviceOpenResult(deviceID, false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return device.open();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sendOutputReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.sendOutputReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sendFeatureReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.sendFeatureReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getFeatureReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.getFeatureReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeDevice(int deviceID) {
|
||||||
|
try {
|
||||||
|
Log.v(TAG, "closeDevice deviceID=" + deviceID);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/////////////// Native methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private native void HIDDeviceRegisterCallback();
|
||||||
|
private native void HIDDeviceReleaseCallback();
|
||||||
|
|
||||||
|
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
|
||||||
|
native void HIDDeviceOpenPending(int deviceID);
|
||||||
|
native void HIDDeviceOpenResult(int deviceID, boolean opened);
|
||||||
|
native void HIDDeviceDisconnected(int deviceID);
|
||||||
|
|
||||||
|
native void HIDDeviceInputReport(int deviceID, byte[] report);
|
||||||
|
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
|
||||||
|
}
|
||||||
309
proj-android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
Normal file
309
proj-android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
class HIDDeviceUSB implements HIDDevice {
|
||||||
|
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
|
||||||
|
protected HIDDeviceManager mManager;
|
||||||
|
protected UsbDevice mDevice;
|
||||||
|
protected int mInterfaceIndex;
|
||||||
|
protected int mInterface;
|
||||||
|
protected int mDeviceId;
|
||||||
|
protected UsbDeviceConnection mConnection;
|
||||||
|
protected UsbEndpoint mInputEndpoint;
|
||||||
|
protected UsbEndpoint mOutputEndpoint;
|
||||||
|
protected InputThread mInputThread;
|
||||||
|
protected boolean mRunning;
|
||||||
|
protected boolean mFrozen;
|
||||||
|
|
||||||
|
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
|
||||||
|
mManager = manager;
|
||||||
|
mDevice = usbDevice;
|
||||||
|
mInterfaceIndex = interface_index;
|
||||||
|
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
|
||||||
|
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
|
||||||
|
mRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId() {
|
||||||
|
return mDevice.getVendorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId() {
|
||||||
|
return mDevice.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerialNumber() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
try {
|
||||||
|
result = mDevice.getSerialNumber();
|
||||||
|
}
|
||||||
|
catch (SecurityException exception) {
|
||||||
|
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturerName() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
result = mDevice.getManufacturerName();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = String.format("%x", getVendorId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductName() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
result = mDevice.getProductName();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = String.format("%x", getProductId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsbDevice getDevice() {
|
||||||
|
return mDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() {
|
||||||
|
mConnection = mManager.getUSBManager().openDevice(mDevice);
|
||||||
|
if (mConnection == null) {
|
||||||
|
Log.w(TAG, "Unable to open USB device " + getDeviceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force claim our interface
|
||||||
|
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||||
|
if (!mConnection.claimInterface(iface, true)) {
|
||||||
|
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the endpoints
|
||||||
|
for (int j = 0; j < iface.getEndpointCount(); j++) {
|
||||||
|
UsbEndpoint endpt = iface.getEndpoint(j);
|
||||||
|
switch (endpt.getDirection()) {
|
||||||
|
case UsbConstants.USB_DIR_IN:
|
||||||
|
if (mInputEndpoint == null) {
|
||||||
|
mInputEndpoint = endpt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UsbConstants.USB_DIR_OUT:
|
||||||
|
if (mOutputEndpoint == null) {
|
||||||
|
mOutputEndpoint = endpt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the required endpoints were present
|
||||||
|
if (mInputEndpoint == null || mOutputEndpoint == null) {
|
||||||
|
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listening for input
|
||||||
|
mRunning = true;
|
||||||
|
mInputThread = new InputThread();
|
||||||
|
mInputThread.start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendFeatureReport(byte[] report) {
|
||||||
|
int res = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int length = report.length;
|
||||||
|
boolean skipped_report_id = false;
|
||||||
|
byte report_number = report[0];
|
||||||
|
|
||||||
|
if (report_number == 0x0) {
|
||||||
|
++offset;
|
||||||
|
--length;
|
||||||
|
skipped_report_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mConnection.controlTransfer(
|
||||||
|
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
|
||||||
|
0x09/*HID set_report*/,
|
||||||
|
(3/*HID feature*/ << 8) | report_number,
|
||||||
|
mInterface,
|
||||||
|
report, offset, length,
|
||||||
|
1000/*timeout millis*/);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped_report_id) {
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendOutputReport(byte[] report) {
|
||||||
|
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
|
||||||
|
if (r != report.length) {
|
||||||
|
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFeatureReport(byte[] report) {
|
||||||
|
int res = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int length = report.length;
|
||||||
|
boolean skipped_report_id = false;
|
||||||
|
byte report_number = report[0];
|
||||||
|
|
||||||
|
if (report_number == 0x0) {
|
||||||
|
/* Offset the return buffer by 1, so that the report ID
|
||||||
|
will remain in byte 0. */
|
||||||
|
++offset;
|
||||||
|
--length;
|
||||||
|
skipped_report_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mConnection.controlTransfer(
|
||||||
|
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
|
||||||
|
0x01/*HID get_report*/,
|
||||||
|
(3/*HID feature*/ << 8) | report_number,
|
||||||
|
mInterface,
|
||||||
|
report, offset, length,
|
||||||
|
1000/*timeout millis*/);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped_report_id) {
|
||||||
|
++res;
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
if (res == length) {
|
||||||
|
data = report;
|
||||||
|
} else {
|
||||||
|
data = Arrays.copyOfRange(report, 0, res);
|
||||||
|
}
|
||||||
|
mManager.HIDDeviceFeatureReport(mDeviceId, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mRunning = false;
|
||||||
|
if (mInputThread != null) {
|
||||||
|
while (mInputThread.isAlive()) {
|
||||||
|
mInputThread.interrupt();
|
||||||
|
try {
|
||||||
|
mInputThread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Keep trying until we're done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mInputThread = null;
|
||||||
|
}
|
||||||
|
if (mConnection != null) {
|
||||||
|
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||||
|
mConnection.releaseInterface(iface);
|
||||||
|
mConnection.close();
|
||||||
|
mConnection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
mManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
mFrozen = frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class InputThread extends Thread {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int packetSize = mInputEndpoint.getMaxPacketSize();
|
||||||
|
byte[] packet = new byte[packetSize];
|
||||||
|
while (mRunning) {
|
||||||
|
int r;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
// Could be a timeout or an I/O error
|
||||||
|
}
|
||||||
|
if (r > 0) {
|
||||||
|
byte[] data;
|
||||||
|
if (r == packetSize) {
|
||||||
|
data = packet;
|
||||||
|
} else {
|
||||||
|
data = Arrays.copyOfRange(packet, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFrozen) {
|
||||||
|
mManager.HIDDeviceInputReport(mDeviceId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
proj-android/app/src/main/java/org/libsdl/app/SDL.java
Normal file
90
proj-android/app/src/main/java/org/libsdl/app/SDL.java
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
SDL library initialization
|
||||||
|
*/
|
||||||
|
public class SDL {
|
||||||
|
|
||||||
|
// This function should be called first and sets up the native code
|
||||||
|
// so it can call into the Java classes
|
||||||
|
public static void setupJNI() {
|
||||||
|
SDLActivity.nativeSetupJNI();
|
||||||
|
SDLAudioManager.nativeSetupJNI();
|
||||||
|
SDLControllerManager.nativeSetupJNI();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should be called each time the activity is started
|
||||||
|
public static void initialize() {
|
||||||
|
setContext(null);
|
||||||
|
|
||||||
|
SDLActivity.initialize();
|
||||||
|
SDLAudioManager.initialize();
|
||||||
|
SDLControllerManager.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function stores the current activity (SDL or not)
|
||||||
|
public static void setContext(Context context) {
|
||||||
|
SDLAudioManager.setContext(context);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||||
|
loadLibrary(libraryName, mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||||
|
|
||||||
|
if (libraryName == null) {
|
||||||
|
throw new NullPointerException("No library name provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Let's see if we have ReLinker available in the project. This is necessary for
|
||||||
|
// some projects that have huge numbers of local libraries bundled, and thus may
|
||||||
|
// trip a bug in Android's native library loader which ReLinker works around. (If
|
||||||
|
// loadLibrary works properly, ReLinker will simply use the normal Android method
|
||||||
|
// internally.)
|
||||||
|
//
|
||||||
|
// To use ReLinker, just add it as a dependency. For more information, see
|
||||||
|
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
|
||||||
|
//
|
||||||
|
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
|
||||||
|
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
|
||||||
|
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
|
||||||
|
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
|
||||||
|
|
||||||
|
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
|
||||||
|
// they've changed during updates.
|
||||||
|
Method forceMethod = relinkClass.getDeclaredMethod("force");
|
||||||
|
Object relinkInstance = forceMethod.invoke(null);
|
||||||
|
Class<?> relinkInstanceClass = relinkInstance.getClass();
|
||||||
|
|
||||||
|
// Actually load the library!
|
||||||
|
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
|
||||||
|
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
|
||||||
|
}
|
||||||
|
catch (final Throwable e) {
|
||||||
|
// Fall back
|
||||||
|
try {
|
||||||
|
System.loadLibrary(libraryName);
|
||||||
|
}
|
||||||
|
catch (final UnsatisfiedLinkError ule) {
|
||||||
|
throw ule;
|
||||||
|
}
|
||||||
|
catch (final SecurityException se) {
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Context mContext;
|
||||||
|
}
|
||||||
2120
proj-android/app/src/main/java/org/libsdl/app/SDLActivity.java
Normal file
2120
proj-android/app/src/main/java/org/libsdl/app/SDLActivity.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,514 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioDeviceCallback;
|
||||||
|
import android.media.AudioDeviceInfo;
|
||||||
|
import android.media.AudioFormat;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.AudioRecord;
|
||||||
|
import android.media.AudioTrack;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class SDLAudioManager {
|
||||||
|
protected static final String TAG = "SDLAudio";
|
||||||
|
|
||||||
|
protected static AudioTrack mAudioTrack;
|
||||||
|
protected static AudioRecord mAudioRecord;
|
||||||
|
protected static Context mContext;
|
||||||
|
|
||||||
|
private static final int[] NO_DEVICES = {};
|
||||||
|
|
||||||
|
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
mAudioTrack = null;
|
||||||
|
mAudioRecord = null;
|
||||||
|
mAudioDeviceCallback = null;
|
||||||
|
|
||||||
|
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||||
|
{
|
||||||
|
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||||
|
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||||
|
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setContext(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
if (context != null) {
|
||||||
|
registerAudioDeviceCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(Context context) {
|
||||||
|
unregisterAudioDeviceCallback(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
|
||||||
|
protected static String getAudioFormatString(int audioFormat) {
|
||||||
|
switch (audioFormat) {
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
return "8-bit";
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
return "16-bit";
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
return "float";
|
||||||
|
default:
|
||||||
|
return Integer.toString(audioFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
int channelConfig;
|
||||||
|
int sampleSize;
|
||||||
|
int frameSize;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
|
||||||
|
|
||||||
|
/* On older devices let's use known good settings */
|
||||||
|
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
if (desiredChannels > 2) {
|
||||||
|
desiredChannels = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
|
||||||
|
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
|
||||||
|
if (sampleRate < 8000) {
|
||||||
|
sampleRate = 8000;
|
||||||
|
} else if (sampleRate > 48000) {
|
||||||
|
sampleRate = 48000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
|
||||||
|
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
|
||||||
|
if (Build.VERSION.SDK_INT < minSDKVersion) {
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (audioFormat)
|
||||||
|
{
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
sampleSize = 1;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
sampleSize = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
|
||||||
|
desiredChannels = 6;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Log.v(TAG, "Speaker configuration (and order of channels):");
|
||||||
|
|
||||||
|
if ((channelConfig & 0x00000004) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000008) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000010) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000020) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000040) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000080) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000100) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000200) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000400) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000800) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00001000) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
frameSize = (sampleSize * desiredChannels);
|
||||||
|
|
||||||
|
// Let the user pick a larger buffer if they really want -- but ye
|
||||||
|
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||||
|
// latency already
|
||||||
|
int minBufferSize;
|
||||||
|
if (isCapture) {
|
||||||
|
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
} else {
|
||||||
|
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
}
|
||||||
|
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
|
||||||
|
|
||||||
|
int[] results = new int[4];
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
if (mAudioRecord == null) {
|
||||||
|
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
|
||||||
|
channelConfig, audioFormat, desiredFrames * frameSize);
|
||||||
|
|
||||||
|
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
|
||||||
|
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||||
|
Log.e(TAG, "Failed during initialization of AudioRecord");
|
||||||
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||||
|
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioRecord.startRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioRecord.getSampleRate();
|
||||||
|
results[1] = mAudioRecord.getAudioFormat();
|
||||||
|
results[2] = mAudioRecord.getChannelCount();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
|
||||||
|
|
||||||
|
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
|
||||||
|
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
|
||||||
|
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
|
||||||
|
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
|
||||||
|
/* Try again, with safer values */
|
||||||
|
|
||||||
|
Log.e(TAG, "Failed during initialization of Audio Track");
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||||
|
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioTrack.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioTrack.getSampleRate();
|
||||||
|
results[1] = mAudioTrack.getAudioFormat();
|
||||||
|
results[2] = mAudioTrack.getChannelCount();
|
||||||
|
}
|
||||||
|
results[3] = desiredFrames;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
|
||||||
|
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
|
||||||
|
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerAudioDeviceCallback() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unregisterAudioDeviceCallback(Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] getAudioOutputDevices() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||||
|
} else {
|
||||||
|
return NO_DEVICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] getAudioInputDevices() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||||
|
} else {
|
||||||
|
return NO_DEVICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteFloatBuffer(float[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length;) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(float)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteShortBuffer(short[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length;) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(short)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteByteBuffer(byte[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length; ) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(byte)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void audioClose() {
|
||||||
|
if (mAudioTrack != null) {
|
||||||
|
mAudioTrack.stop();
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void captureClose() {
|
||||||
|
if (mAudioRecord != null) {
|
||||||
|
mAudioRecord.stop();
|
||||||
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
/* Set thread name */
|
||||||
|
if (iscapture) {
|
||||||
|
Thread.currentThread().setName("SDLAudioC" + device_id);
|
||||||
|
} else {
|
||||||
|
Thread.currentThread().setName("SDLAudioP" + device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set thread priority */
|
||||||
|
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "modify thread properties failed " + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native int nativeSetupJNI();
|
||||||
|
|
||||||
|
public static native void removeAudioDevice(boolean isCapture, int deviceId);
|
||||||
|
|
||||||
|
public static native void addAudioDevice(boolean isCapture, int deviceId);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,856 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
|
||||||
|
public class SDLControllerManager
|
||||||
|
{
|
||||||
|
|
||||||
|
public static native int nativeSetupJNI();
|
||||||
|
|
||||||
|
public static native int nativeAddJoystick(int device_id, String name, String desc,
|
||||||
|
int vendor_id, int product_id,
|
||||||
|
boolean is_accelerometer, int button_mask,
|
||||||
|
int naxes, int axis_mask, int nhats, int nballs);
|
||||||
|
public static native int nativeRemoveJoystick(int device_id);
|
||||||
|
public static native int nativeAddHaptic(int device_id, String name);
|
||||||
|
public static native int nativeRemoveHaptic(int device_id);
|
||||||
|
public static native int onNativePadDown(int device_id, int keycode);
|
||||||
|
public static native int onNativePadUp(int device_id, int keycode);
|
||||||
|
public static native void onNativeJoy(int device_id, int axis,
|
||||||
|
float value);
|
||||||
|
public static native void onNativeHat(int device_id, int hat_id,
|
||||||
|
int x, int y);
|
||||||
|
|
||||||
|
protected static SDLJoystickHandler mJoystickHandler;
|
||||||
|
protected static SDLHapticHandler mHapticHandler;
|
||||||
|
|
||||||
|
private static final String TAG = "SDLControllerManager";
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
if (mJoystickHandler == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||||
|
mJoystickHandler = new SDLJoystickHandler_API19();
|
||||||
|
} else {
|
||||||
|
mJoystickHandler = new SDLJoystickHandler_API16();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHapticHandler == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||||
|
mHapticHandler = new SDLHapticHandler_API26();
|
||||||
|
} else {
|
||||||
|
mHapticHandler = new SDLHapticHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
||||||
|
public static boolean handleJoystickMotionEvent(MotionEvent event) {
|
||||||
|
return mJoystickHandler.handleMotionEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void pollInputDevices() {
|
||||||
|
mJoystickHandler.pollInputDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void pollHapticDevices() {
|
||||||
|
mHapticHandler.pollHapticDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void hapticRun(int device_id, float intensity, int length) {
|
||||||
|
mHapticHandler.run(device_id, intensity, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void hapticStop(int device_id)
|
||||||
|
{
|
||||||
|
mHapticHandler.stop(device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a given device is considered a possible SDL joystick
|
||||||
|
public static boolean isDeviceSDLJoystick(int deviceId) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceId);
|
||||||
|
// We cannot use InputDevice.isVirtual before API 16, so let's accept
|
||||||
|
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
||||||
|
if ((device == null) || (deviceId < 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int sources = device.getSources();
|
||||||
|
|
||||||
|
/* This is called for every button press, so let's not spam the logs */
|
||||||
|
/*
|
||||||
|
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
|
||||||
|
}
|
||||||
|
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
||||||
|
}
|
||||||
|
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||||
|
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||||
|
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLJoystickHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles given MotionEvent.
|
||||||
|
* @param event the event to be handled.
|
||||||
|
* @return if given event was processed.
|
||||||
|
*/
|
||||||
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles adding and removing of input devices.
|
||||||
|
*/
|
||||||
|
public void pollInputDevices() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual joystick functionality available for API >= 12 devices */
|
||||||
|
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
||||||
|
|
||||||
|
static class SDLJoystick {
|
||||||
|
public int device_id;
|
||||||
|
public String name;
|
||||||
|
public String desc;
|
||||||
|
public ArrayList<InputDevice.MotionRange> axes;
|
||||||
|
public ArrayList<InputDevice.MotionRange> hats;
|
||||||
|
}
|
||||||
|
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||||
|
@Override
|
||||||
|
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||||
|
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
|
||||||
|
int arg0Axis = arg0.getAxis();
|
||||||
|
int arg1Axis = arg1.getAxis();
|
||||||
|
if (arg0Axis == MotionEvent.AXIS_GAS) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_BRAKE;
|
||||||
|
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_GAS;
|
||||||
|
}
|
||||||
|
if (arg1Axis == MotionEvent.AXIS_GAS) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_BRAKE;
|
||||||
|
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_GAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||||
|
// This is because the usual pairing are:
|
||||||
|
// - AXIS_X + AXIS_Y (left stick).
|
||||||
|
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||||
|
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||||
|
// This sorts the axes in the above order, which tends to be correct
|
||||||
|
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||||
|
// triggers on Z/RZ.
|
||||||
|
//
|
||||||
|
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||||
|
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||||
|
//
|
||||||
|
// References:
|
||||||
|
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||||
|
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||||
|
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||||
|
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||||
|
--arg0Axis;
|
||||||
|
}
|
||||||
|
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||||
|
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||||
|
--arg1Axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arg0Axis - arg1Axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<SDLJoystick> mJoysticks;
|
||||||
|
|
||||||
|
public SDLJoystickHandler_API16() {
|
||||||
|
|
||||||
|
mJoysticks = new ArrayList<SDLJoystick>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollInputDevices() {
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
|
||||||
|
for (int device_id : deviceIds) {
|
||||||
|
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
|
||||||
|
SDLJoystick joystick = getJoystick(device_id);
|
||||||
|
if (joystick == null) {
|
||||||
|
InputDevice joystickDevice = InputDevice.getDevice(device_id);
|
||||||
|
joystick = new SDLJoystick();
|
||||||
|
joystick.device_id = device_id;
|
||||||
|
joystick.name = joystickDevice.getName();
|
||||||
|
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||||
|
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||||
|
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||||
|
|
||||||
|
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||||
|
Collections.sort(ranges, new RangeComparator());
|
||||||
|
for (InputDevice.MotionRange range : ranges) {
|
||||||
|
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||||
|
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||||
|
joystick.hats.add(range);
|
||||||
|
} else {
|
||||||
|
joystick.axes.add(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mJoysticks.add(joystick);
|
||||||
|
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||||
|
getVendorId(joystickDevice), getProductId(joystickDevice), false,
|
||||||
|
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check removed devices */
|
||||||
|
ArrayList<Integer> removedDevices = null;
|
||||||
|
for (SDLJoystick joystick : mJoysticks) {
|
||||||
|
int device_id = joystick.device_id;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < deviceIds.length; i++) {
|
||||||
|
if (device_id == deviceIds[i]) break;
|
||||||
|
}
|
||||||
|
if (i == deviceIds.length) {
|
||||||
|
if (removedDevices == null) {
|
||||||
|
removedDevices = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
removedDevices.add(device_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedDevices != null) {
|
||||||
|
for (int device_id : removedDevices) {
|
||||||
|
SDLControllerManager.nativeRemoveJoystick(device_id);
|
||||||
|
for (int i = 0; i < mJoysticks.size(); i++) {
|
||||||
|
if (mJoysticks.get(i).device_id == device_id) {
|
||||||
|
mJoysticks.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDLJoystick getJoystick(int device_id) {
|
||||||
|
for (SDLJoystick joystick : mJoysticks) {
|
||||||
|
if (joystick.device_id == device_id) {
|
||||||
|
return joystick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
|
int actionPointerIndex = event.getActionIndex();
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
if (action == MotionEvent.ACTION_MOVE) {
|
||||||
|
SDLJoystick joystick = getJoystick(event.getDeviceId());
|
||||||
|
if (joystick != null) {
|
||||||
|
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||||
|
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||||
|
/* Normalize the value to -1...1 */
|
||||||
|
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||||
|
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < joystick.hats.size() / 2; i++) {
|
||||||
|
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
|
||||||
|
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
|
||||||
|
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoystickDescriptor(InputDevice joystickDevice) {
|
||||||
|
String desc = joystickDevice.getDescriptor();
|
||||||
|
|
||||||
|
if (desc != null && !desc.isEmpty()) {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return joystickDevice.getName();
|
||||||
|
}
|
||||||
|
public int getProductId(InputDevice joystickDevice) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int getVendorId(InputDevice joystickDevice) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public int getButtonMask(InputDevice joystickDevice) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId(InputDevice joystickDevice) {
|
||||||
|
return joystickDevice.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId(InputDevice joystickDevice) {
|
||||||
|
return joystickDevice.getVendorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||||
|
// For compatibility, keep computing the axis mask like before,
|
||||||
|
// only really distinguishing 2, 4 and 6 axes.
|
||||||
|
int axis_mask = 0;
|
||||||
|
if (ranges.size() >= 2) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||||
|
axis_mask |= 0x0003;
|
||||||
|
}
|
||||||
|
if (ranges.size() >= 4) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||||
|
axis_mask |= 0x000c;
|
||||||
|
}
|
||||||
|
if (ranges.size() >= 6) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||||
|
axis_mask |= 0x0030;
|
||||||
|
}
|
||||||
|
// Also add an indicator bit for whether the sorting order has changed.
|
||||||
|
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||||
|
boolean have_z = false;
|
||||||
|
boolean have_past_z_before_rz = false;
|
||||||
|
for (InputDevice.MotionRange range : ranges) {
|
||||||
|
int axis = range.getAxis();
|
||||||
|
if (axis == MotionEvent.AXIS_Z) {
|
||||||
|
have_z = true;
|
||||||
|
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||||
|
have_past_z_before_rz = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (have_z && have_past_z_before_rz) {
|
||||||
|
// If both these exist, the compare() function changed sorting order.
|
||||||
|
// Set a bit to indicate this fact.
|
||||||
|
axis_mask |= 0x8000;
|
||||||
|
}
|
||||||
|
return axis_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonMask(InputDevice joystickDevice) {
|
||||||
|
int button_mask = 0;
|
||||||
|
int[] keys = new int[] {
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_B,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_X,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Y,
|
||||||
|
KeyEvent.KEYCODE_BACK,
|
||||||
|
KeyEvent.KEYCODE_MENU,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_MODE,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_START,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBL,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBR,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L1,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R1,
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP,
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||||
|
|
||||||
|
// These don't map into any SDL controller buttons directly
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_C,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Z,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_1,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_3,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_4,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_5,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_6,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_7,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_8,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_9,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_10,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_11,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_12,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_13,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_14,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_15,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_16,
|
||||||
|
};
|
||||||
|
int[] masks = new int[] {
|
||||||
|
(1 << 0), // A -> A
|
||||||
|
(1 << 1), // B -> B
|
||||||
|
(1 << 2), // X -> X
|
||||||
|
(1 << 3), // Y -> Y
|
||||||
|
(1 << 4), // BACK -> BACK
|
||||||
|
(1 << 6), // MENU -> START
|
||||||
|
(1 << 5), // MODE -> GUIDE
|
||||||
|
(1 << 6), // START -> START
|
||||||
|
(1 << 7), // THUMBL -> LEFTSTICK
|
||||||
|
(1 << 8), // THUMBR -> RIGHTSTICK
|
||||||
|
(1 << 9), // L1 -> LEFTSHOULDER
|
||||||
|
(1 << 10), // R1 -> RIGHTSHOULDER
|
||||||
|
(1 << 11), // DPAD_UP -> DPAD_UP
|
||||||
|
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
|
||||||
|
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
|
||||||
|
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
|
||||||
|
(1 << 4), // SELECT -> BACK
|
||||||
|
(1 << 0), // DPAD_CENTER -> A
|
||||||
|
(1 << 15), // L2 -> ??
|
||||||
|
(1 << 16), // R2 -> ??
|
||||||
|
(1 << 17), // C -> ??
|
||||||
|
(1 << 18), // Z -> ??
|
||||||
|
(1 << 20), // 1 -> ??
|
||||||
|
(1 << 21), // 2 -> ??
|
||||||
|
(1 << 22), // 3 -> ??
|
||||||
|
(1 << 23), // 4 -> ??
|
||||||
|
(1 << 24), // 5 -> ??
|
||||||
|
(1 << 25), // 6 -> ??
|
||||||
|
(1 << 26), // 7 -> ??
|
||||||
|
(1 << 27), // 8 -> ??
|
||||||
|
(1 << 28), // 9 -> ??
|
||||||
|
(1 << 29), // 10 -> ??
|
||||||
|
(1 << 30), // 11 -> ??
|
||||||
|
(1 << 31), // 12 -> ??
|
||||||
|
// We're out of room...
|
||||||
|
0xFFFFFFFF, // 13 -> ??
|
||||||
|
0xFFFFFFFF, // 14 -> ??
|
||||||
|
0xFFFFFFFF, // 15 -> ??
|
||||||
|
0xFFFFFFFF, // 16 -> ??
|
||||||
|
};
|
||||||
|
boolean[] has_keys = joystickDevice.hasKeys(keys);
|
||||||
|
for (int i = 0; i < keys.length; ++i) {
|
||||||
|
if (has_keys[i]) {
|
||||||
|
button_mask |= masks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return button_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLHapticHandler_API26 extends SDLHapticHandler {
|
||||||
|
@Override
|
||||||
|
public void run(int device_id, float intensity, int length) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
|
||||||
|
if (intensity == 0.0f) {
|
||||||
|
stop(device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vibeValue = Math.round(intensity * 255);
|
||||||
|
|
||||||
|
if (vibeValue > 255) {
|
||||||
|
vibeValue = 255;
|
||||||
|
}
|
||||||
|
if (vibeValue < 1) {
|
||||||
|
stop(device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||||
|
// something went horribly wrong with the Android 8.0 APIs.
|
||||||
|
haptic.vib.vibrate(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLHapticHandler {
|
||||||
|
|
||||||
|
static class SDLHaptic {
|
||||||
|
public int device_id;
|
||||||
|
public String name;
|
||||||
|
public Vibrator vib;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<SDLHaptic> mHaptics;
|
||||||
|
|
||||||
|
public SDLHapticHandler() {
|
||||||
|
mHaptics = new ArrayList<SDLHaptic>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(int device_id, float intensity, int length) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
haptic.vib.vibrate(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(int device_id) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
haptic.vib.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pollHapticDevices() {
|
||||||
|
|
||||||
|
final int deviceId_VIBRATOR_SERVICE = 999999;
|
||||||
|
boolean hasVibratorService = false;
|
||||||
|
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
// It helps processing the device ids in reverse order
|
||||||
|
// For example, in the case of the XBox 360 wireless dongle,
|
||||||
|
// so the first controller seen by SDL matches what the receiver
|
||||||
|
// considers to be the first controller
|
||||||
|
|
||||||
|
for (int i = deviceIds.length - 1; i > -1; i--) {
|
||||||
|
SDLHaptic haptic = getHaptic(deviceIds[i]);
|
||||||
|
if (haptic == null) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
||||||
|
Vibrator vib = device.getVibrator();
|
||||||
|
if (vib != null) {
|
||||||
|
if (vib.hasVibrator()) {
|
||||||
|
haptic = new SDLHaptic();
|
||||||
|
haptic.device_id = deviceIds[i];
|
||||||
|
haptic.name = device.getName();
|
||||||
|
haptic.vib = vib;
|
||||||
|
mHaptics.add(haptic);
|
||||||
|
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check VIBRATOR_SERVICE */
|
||||||
|
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
if (vib != null) {
|
||||||
|
hasVibratorService = vib.hasVibrator();
|
||||||
|
|
||||||
|
if (hasVibratorService) {
|
||||||
|
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
||||||
|
if (haptic == null) {
|
||||||
|
haptic = new SDLHaptic();
|
||||||
|
haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
||||||
|
haptic.name = "VIBRATOR_SERVICE";
|
||||||
|
haptic.vib = vib;
|
||||||
|
mHaptics.add(haptic);
|
||||||
|
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check removed devices */
|
||||||
|
ArrayList<Integer> removedDevices = null;
|
||||||
|
for (SDLHaptic haptic : mHaptics) {
|
||||||
|
int device_id = haptic.device_id;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < deviceIds.length; i++) {
|
||||||
|
if (device_id == deviceIds[i]) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
|
||||||
|
if (i == deviceIds.length) {
|
||||||
|
if (removedDevices == null) {
|
||||||
|
removedDevices = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
removedDevices.add(device_id);
|
||||||
|
}
|
||||||
|
} // else: don't remove the vibrator if it is still present
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedDevices != null) {
|
||||||
|
for (int device_id : removedDevices) {
|
||||||
|
SDLControllerManager.nativeRemoveHaptic(device_id);
|
||||||
|
for (int i = 0; i < mHaptics.size(); i++) {
|
||||||
|
if (mHaptics.get(i).device_id == device_id) {
|
||||||
|
mHaptics.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDLHaptic getHaptic(int device_id) {
|
||||||
|
for (SDLHaptic haptic : mHaptics) {
|
||||||
|
if (haptic.device_id == device_id) {
|
||||||
|
return haptic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
float x, y;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
switch ( event.getSource() ) {
|
||||||
|
case InputDevice.SOURCE_JOYSTICK:
|
||||||
|
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reclaimRelativeMouseModeIfNeeded()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
|
||||||
|
private boolean mRelativeModeEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
|
||||||
|
// Handle relative mouse mode
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
if (action == MotionEvent.ACTION_HOVER_MOVE) {
|
||||||
|
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed, call SDLGenericMotionListener_API12 method
|
||||||
|
return super.onGenericMotion(v, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return mRelativeModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
mRelativeModeEnabled = enabled;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
} else {
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
} else {
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
private boolean mRelativeModeEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
float x, y;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
switch ( event.getSource() ) {
|
||||||
|
case InputDevice.SOURCE_JOYSTICK:
|
||||||
|
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE:
|
||||||
|
// DeX desktop mouse cursor is a separate non-standard input type.
|
||||||
|
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE_RELATIVE:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return mRelativeModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||||
|
if (enabled) {
|
||||||
|
SDLActivity.getContentView().requestPointerCapture();
|
||||||
|
} else {
|
||||||
|
SDLActivity.getContentView().releasePointerCapture();
|
||||||
|
}
|
||||||
|
mRelativeModeEnabled = enabled;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reclaimRelativeMouseModeIfNeeded()
|
||||||
|
{
|
||||||
|
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
|
||||||
|
SDLActivity.getContentView().requestPointerCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
// Relative mouse in capture mode will only have relative for X/Y
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
// Relative mouse in capture mode will only have relative for X/Y
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
405
proj-android/app/src/main/java/org/libsdl/app/SDLSurface.java
Normal file
405
proj-android/app/src/main/java/org/libsdl/app/SDLSurface.java
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||||
|
in order to do anything useful.
|
||||||
|
|
||||||
|
Because of this, that's where we set up the SDL thread
|
||||||
|
*/
|
||||||
|
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||||
|
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||||
|
|
||||||
|
// Sensors
|
||||||
|
protected SensorManager mSensorManager;
|
||||||
|
protected Display mDisplay;
|
||||||
|
|
||||||
|
// Keep track of the surface size to normalize touch events
|
||||||
|
protected float mWidth, mHeight;
|
||||||
|
|
||||||
|
// Is SurfaceView ready for rendering
|
||||||
|
public boolean mIsSurfaceReady;
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
public SDLSurface(Context context) {
|
||||||
|
super(context);
|
||||||
|
getHolder().addCallback(this);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
requestFocus();
|
||||||
|
setOnKeyListener(this);
|
||||||
|
setOnTouchListener(this);
|
||||||
|
|
||||||
|
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||||
|
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
|
||||||
|
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||||
|
|
||||||
|
// Some arbitrary defaults to avoid a potential division by zero
|
||||||
|
mWidth = 1.0f;
|
||||||
|
mHeight = 1.0f;
|
||||||
|
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePause() {
|
||||||
|
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleResume() {
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
requestFocus();
|
||||||
|
setOnKeyListener(this);
|
||||||
|
setOnTouchListener(this);
|
||||||
|
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Surface getNativeSurface() {
|
||||||
|
return getHolder().getSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we have a valid drawing surface
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Log.v("SDL", "surfaceCreated()");
|
||||||
|
SDLActivity.onNativeSurfaceCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we lose the surface
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Log.v("SDL", "surfaceDestroyed()");
|
||||||
|
|
||||||
|
// Transition to pause, if needed
|
||||||
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||||
|
SDLActivity.handleNativeState();
|
||||||
|
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
SDLActivity.onNativeSurfaceDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the surface is resized
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder,
|
||||||
|
int format, int width, int height) {
|
||||||
|
Log.v("SDL", "surfaceChanged()");
|
||||||
|
|
||||||
|
if (SDLActivity.mSingleton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
int nDeviceWidth = width;
|
||||||
|
int nDeviceHeight = height;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
|
||||||
|
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||||
|
mDisplay.getRealMetrics( realMetrics );
|
||||||
|
nDeviceWidth = realMetrics.widthPixels;
|
||||||
|
nDeviceHeight = realMetrics.heightPixels;
|
||||||
|
}
|
||||||
|
} catch(Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(SDLActivity.getContext()) {
|
||||||
|
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||||
|
SDLActivity.getContext().notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||||
|
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||||
|
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||||
|
SDLActivity.onNativeResize();
|
||||||
|
|
||||||
|
// Prevent a screen distortion glitch,
|
||||||
|
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||||
|
boolean skip = false;
|
||||||
|
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||||
|
|
||||||
|
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||||
|
if (mWidth > mHeight) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||||
|
if (mWidth < mHeight) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special Patch for Square Resolution: Black Berry Passport
|
||||||
|
if (skip) {
|
||||||
|
double min = Math.min(mWidth, mHeight);
|
||||||
|
double max = Math.max(mWidth, mHeight);
|
||||||
|
|
||||||
|
if (max / min < 1.20) {
|
||||||
|
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't skip in MultiWindow.
|
||||||
|
if (skip) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||||
|
Log.v("SDL", "Don't skip in Multi-Window");
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||||
|
SDLActivity.onNativeSurfaceChanged();
|
||||||
|
|
||||||
|
/* Surface is ready */
|
||||||
|
mIsSurfaceReady = true;
|
||||||
|
|
||||||
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||||
|
SDLActivity.handleNativeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key events
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touch events
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||||
|
int touchDevId = event.getDeviceId();
|
||||||
|
final int pointerCount = event.getPointerCount();
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
int pointerFingerId;
|
||||||
|
int i = -1;
|
||||||
|
float x,y,p;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||||
|
* Appears when using Android emulator, eg:
|
||||||
|
* adb shell input mouse tap 100 100
|
||||||
|
* adb shell input touchscreen tap 100 100
|
||||||
|
*/
|
||||||
|
if (touchDevId < 0) {
|
||||||
|
touchDevId -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12290 = Samsung DeX mode desktop mouse
|
||||||
|
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||||
|
// 0x2 = SOURCE_CLASS_POINTER
|
||||||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||||
|
int mouseButton = 1;
|
||||||
|
try {
|
||||||
|
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||||
|
if (object != null) {
|
||||||
|
mouseButton = (Integer) object;
|
||||||
|
}
|
||||||
|
} catch(Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||||
|
// if we are. We'll leverage our existing mouse motion listener
|
||||||
|
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||||
|
x = motionListener.getEventX(event);
|
||||||
|
y = motionListener.getEventY(event);
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||||
|
} else {
|
||||||
|
switch(action) {
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
for (i = 0; i < pointerCount; i++) {
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
// Primary pointer up/down, the index is always zero
|
||||||
|
i = 0;
|
||||||
|
/* fallthrough */
|
||||||
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
|
// Non primary pointer up/down
|
||||||
|
if (i == -1) {
|
||||||
|
i = event.getActionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
for (i = 0; i < pointerCount; i++) {
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sensor events
|
||||||
|
public void enableSensor(int sensortype, boolean enabled) {
|
||||||
|
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||||
|
if (enabled) {
|
||||||
|
mSensorManager.registerListener(this,
|
||||||
|
mSensorManager.getDefaultSensor(sensortype),
|
||||||
|
SensorManager.SENSOR_DELAY_GAME, null);
|
||||||
|
} else {
|
||||||
|
mSensorManager.unregisterListener(this,
|
||||||
|
mSensorManager.getDefaultSensor(sensortype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||||
|
|
||||||
|
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||||
|
// We thus should check here.
|
||||||
|
int newOrientation;
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
switch (mDisplay.getRotation()) {
|
||||||
|
case Surface.ROTATION_90:
|
||||||
|
x = -event.values[1];
|
||||||
|
y = event.values[0];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_270:
|
||||||
|
x = event.values[1];
|
||||||
|
y = -event.values[0];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_180:
|
||||||
|
x = -event.values[0];
|
||||||
|
y = -event.values[1];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_0:
|
||||||
|
default:
|
||||||
|
x = event.values[0];
|
||||||
|
y = event.values[1];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||||
|
SDLActivity.mCurrentOrientation = newOrientation;
|
||||||
|
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||||
|
y / SensorManager.GRAVITY_EARTH,
|
||||||
|
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captured pointer events for API 26.
|
||||||
|
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||||
|
{
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||||
|
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||||
|
|
||||||
|
// Change our action value to what SDL's code expects.
|
||||||
|
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||||
|
action = MotionEvent.ACTION_DOWN;
|
||||||
|
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||||
|
action = MotionEvent.ACTION_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
int button = event.getButtonState();
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
proj-android/app/src/main/res/mipmap-hdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
BIN
proj-android/app/src/main/res/mipmap-hdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
proj-android/app/src/main/res/mipmap-mdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
BIN
proj-android/app/src/main/res/mipmap-mdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
proj-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
BIN
proj-android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
proj-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
BIN
proj-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
proj-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
BIN
proj-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
(Stored with Git LFS)
Normal file
Binary file not shown.
6
proj-android/app/src/main/res/values/colors.xml
Normal file
6
proj-android/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
||||||
3
proj-android/app/src/main/res/values/strings.xml
Normal file
3
proj-android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Game</string>
|
||||||
|
</resources>
|
||||||
7
proj-android/app/src/main/res/values/styles.xml
Normal file
7
proj-android/app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
25
proj-android/build.gradle
Normal file
25
proj-android/build.gradle
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:8.1.1'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
17
proj-android/gradle.properties
Normal file
17
proj-android/gradle.properties
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
BIN
proj-android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
proj-android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
proj-android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
proj-android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Sat Jan 10 10:31:05 MSK 2026
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
160
proj-android/gradlew
vendored
Normal file
160
proj-android/gradlew
vendored
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|grep -E -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|grep -E -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
90
proj-android/gradlew.bat
vendored
Normal file
90
proj-android/gradlew.bat
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
proj-android/settings.gradle
Normal file
1
proj-android/settings.gradle
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ':app'
|
||||||
141
proj-web/CMakeLists.txt
Normal file
141
proj-web/CMakeLists.txt
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
# Фикс для Ninja на Windows
|
||||||
|
if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
|
||||||
|
set(POTENTIAL_NINJA "${CMAKE_CURRENT_SOURCE_DIR}/ninja/ninja.exe")
|
||||||
|
if(EXISTS "${POTENTIAL_NINJA}")
|
||||||
|
set(CMAKE_MAKE_PROGRAM "${POTENTIAL_NINJA}" CACHE FILEPATH "Path to ninja" FORCE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(space-game001 CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# --- АВТО-ЗАГРУЗКА ЗАВИСИМОСТЕЙ ---
|
||||||
|
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake")
|
||||||
|
# Теперь гарантированно есть папка ../thirdparty со всеми исходниками
|
||||||
|
|
||||||
|
# Список исходных файлов (без изменений)
|
||||||
|
set(SOURCES
|
||||||
|
../src/main.cpp
|
||||||
|
../src/Game.cpp
|
||||||
|
../src/Environment.cpp
|
||||||
|
../src/BoneAnimatedModel.cpp
|
||||||
|
../src/TextModel.cpp
|
||||||
|
../src/Projectile.cpp
|
||||||
|
../src/SparkEmitter.cpp
|
||||||
|
../src/UiManager.cpp
|
||||||
|
../src/render/Renderer.cpp
|
||||||
|
../src/render/ShaderManager.cpp
|
||||||
|
../src/render/TextureManager.cpp
|
||||||
|
../src/render/FrameBuffer.cpp
|
||||||
|
../src/render/OpenGlExtensions.cpp
|
||||||
|
../src/utils/Utils.cpp
|
||||||
|
../src/utils/TaskManager.cpp
|
||||||
|
../src/utils/Perlin.cpp
|
||||||
|
../src/planet/PlanetData.cpp
|
||||||
|
../src/planet/PlanetObject.cpp
|
||||||
|
../src/planet/StoneObject.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(space-game001 ${SOURCES})
|
||||||
|
|
||||||
|
# Настройка путей к инклудам (используем скачанные исходники)
|
||||||
|
target_include_directories(space-game001 PRIVATE
|
||||||
|
../src
|
||||||
|
../thirdparty/eigen-5.0.0
|
||||||
|
../thirdparty/boost_1_90_0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Сборка libzip через add_subdirectory (Emscripten соберет её из исходников)
|
||||||
|
# Опции для либзипа, чтобы он не искал лишнего в системе
|
||||||
|
set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
|
||||||
|
|
||||||
|
# Линковка:
|
||||||
|
# 'zip' берется из add_subdirectory
|
||||||
|
# 'z' - это системный zlib Emscripten-а (флаг -sUSE_ZLIB=1 добавим ниже)
|
||||||
|
target_link_libraries(space-game001 PRIVATE zip z)
|
||||||
|
|
||||||
|
# Эмскриптен-флаги
|
||||||
|
set(EMSCRIPTEN_FLAGS
|
||||||
|
"-sUSE_SDL=2"
|
||||||
|
"-sUSE_SDL_IMAGE=2"
|
||||||
|
"-sUSE_LIBPNG=1"
|
||||||
|
"-sUSE_ZLIB=1" # Добавили zlib порт
|
||||||
|
"-pthread"
|
||||||
|
"-sUSE_PTHREADS=1"
|
||||||
|
"-fexceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(space-game001 PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
|
||||||
|
|
||||||
|
set(EMSCRIPTEN_LINK_FLAGS
|
||||||
|
${EMSCRIPTEN_FLAGS}
|
||||||
|
"-O2"
|
||||||
|
"-sPTHREAD_POOL_SIZE=4"
|
||||||
|
"-sALLOW_MEMORY_GROWTH=1"
|
||||||
|
"--preload-file resources.zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Применяем настройки линковки
|
||||||
|
target_link_options(space-game001 PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
|
||||||
|
|
||||||
|
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
|
||||||
|
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
|
||||||
|
set_target_properties(space-game001 PROPERTIES
|
||||||
|
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
|
||||||
|
SUFFIX ".html"
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Ресурсы и Деплой (без изменений) ---
|
||||||
|
set(RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../resources")
|
||||||
|
set(RESOURCES_ZIP "${CMAKE_CURRENT_BINARY_DIR}/resources.zip")
|
||||||
|
get_filename_component(RESOURCES_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${RESOURCES_ZIP}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E tar "cf" "${RESOURCES_ZIP}" --format=zip "resources"
|
||||||
|
WORKING_DIRECTORY "${RESOURCES_PARENT_DIR}"
|
||||||
|
DEPENDS "${RESOURCES_PARENT_DIR}/resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
|
||||||
|
add_dependencies(space-game001 pack_resources)
|
||||||
|
|
||||||
|
|
||||||
|
# Определяем путь к директории установки (относительно папки билда)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
|
||||||
|
|
||||||
|
# 1. Устанавливаем основной HTML файл
|
||||||
|
install(TARGETS space-game001
|
||||||
|
RUNTIME DESTINATION .
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
|
||||||
|
# Emscripten создает их в той же папке, что и таргет
|
||||||
|
install(FILES
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.js"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.wasm"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.data"
|
||||||
|
DESTINATION .
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/space-game001plain.html"
|
||||||
|
DESTINATION .
|
||||||
|
)
|
||||||
|
|
||||||
|
# Если вам все еще нужен сам resources.zip отдельно в папке public:
|
||||||
|
#install(FILES "${RESOURCES_ZIP}" DESTINATION .)
|
||||||
|
|
||||||
|
add_custom_command(TARGET space-game001 POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} --install .
|
||||||
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
|
COMMENT "Automatically deploying to public directory..."
|
||||||
|
)
|
||||||
33
proj-web/README.md
Normal file
33
proj-web/README.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# how to build
|
||||||
|
|
||||||
|
|
||||||
|
Activate the environment:
|
||||||
|
```
|
||||||
|
C:\Work\Projects\emsdk\emsdk.bat activate latest
|
||||||
|
C:\Work\Projects\emsdk\emsdk_env.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Optionally clear cache:
|
||||||
|
```
|
||||||
|
emcc --clear-cache
|
||||||
|
embuilder build sdl2 sdl2_ttf sdl2_image sdl2_image_jpg sdl2_image_png
|
||||||
|
```
|
||||||
|
|
||||||
|
Build:
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
emcmake cmake -G Ninja ..
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally install:
|
||||||
|
```
|
||||||
|
cmake --install .
|
||||||
|
```
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```
|
||||||
|
emrun --no_browser --port 8080 public
|
||||||
|
```
|
||||||
BIN
proj-web/ninja/ninja.exe
Normal file
BIN
proj-web/ninja/ninja.exe
Normal file
Binary file not shown.
168
proj-windows/CMakeLists.txt
Normal file
168
proj-windows/CMakeLists.txt
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(space-game001 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Основной проект space-game001
|
||||||
|
# ===========================================
|
||||||
|
add_executable(space-game001
|
||||||
|
../src/main.cpp
|
||||||
|
../src/Game.cpp
|
||||||
|
../src/Game.h
|
||||||
|
../src/Environment.cpp
|
||||||
|
../src/Environment.h
|
||||||
|
../src/render/Renderer.cpp
|
||||||
|
../src/render/Renderer.h
|
||||||
|
../src/render/ShaderManager.cpp
|
||||||
|
../src/render/ShaderManager.h
|
||||||
|
../src/render/TextureManager.cpp
|
||||||
|
../src/render/TextureManager.h
|
||||||
|
../src/TextModel.cpp
|
||||||
|
../src/TextModel.h
|
||||||
|
../src/AudioPlayerAsync.cpp
|
||||||
|
../src/AudioPlayerAsync.h
|
||||||
|
../src/BoneAnimatedModel.cpp
|
||||||
|
../src/BoneAnimatedModel.h
|
||||||
|
../src/render/OpenGlExtensions.cpp
|
||||||
|
../src/render/OpenGlExtensions.h
|
||||||
|
../src/utils/Utils.cpp
|
||||||
|
../src/utils/Utils.h
|
||||||
|
../src/SparkEmitter.cpp
|
||||||
|
../src/SparkEmitter.h
|
||||||
|
../src/planet/PlanetObject.cpp
|
||||||
|
../src/planet/PlanetObject.h
|
||||||
|
../src/planet/PlanetData.cpp
|
||||||
|
../src/planet/PlanetData.h
|
||||||
|
../src/utils/Perlin.cpp
|
||||||
|
../src/utils/Perlin.h
|
||||||
|
../src/utils/TaskManager.cpp
|
||||||
|
../src/utils/TaskManager.h
|
||||||
|
../src/planet/StoneObject.cpp
|
||||||
|
../src/planet/StoneObject.h
|
||||||
|
../src/render/FrameBuffer.cpp
|
||||||
|
../src/render/FrameBuffer.h
|
||||||
|
../src/UiManager.cpp
|
||||||
|
../src/UiManager.h
|
||||||
|
../src/Projectile.h
|
||||||
|
../src/Projectile.cpp
|
||||||
|
../src/network/NetworkInterface.h
|
||||||
|
../src/network/LocalClient.h
|
||||||
|
../src/network/LocalClient.cpp
|
||||||
|
../src/network/ClientState.h
|
||||||
|
../src/network/ClientState.cpp
|
||||||
|
../src/network/WebSocketClient.h
|
||||||
|
../src/network/WebSocketClient.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Установка проекта по умолчанию для Visual Studio
|
||||||
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT space-game001)
|
||||||
|
|
||||||
|
# include-пути проекта
|
||||||
|
target_include_directories(space-game001 PRIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(space-game001 PROPERTIES
|
||||||
|
OUTPUT_NAME "space-game001"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Определения препроцессора:
|
||||||
|
# PNG_ENABLED – включает код PNG в TextureManager
|
||||||
|
# SDL_MAIN_HANDLED – отключает переопределение main -> SDL_main
|
||||||
|
target_compile_definitions(space-game001 PRIVATE
|
||||||
|
WIN32_LEAN_AND_MEAN
|
||||||
|
PNG_ENABLED
|
||||||
|
SDL_MAIN_HANDLED
|
||||||
|
# NETWORK
|
||||||
|
# SIMPLIFIED
|
||||||
|
)
|
||||||
|
|
||||||
|
# Линкуем с SDL2main, если он вообще установлен
|
||||||
|
target_link_libraries(space-game001 PRIVATE SDL2main_external_lib)
|
||||||
|
|
||||||
|
# Линкуем сторонние библиотеки
|
||||||
|
target_link_libraries(space-game001 PRIVATE
|
||||||
|
SDL2_external_lib
|
||||||
|
libpng_external_lib
|
||||||
|
zlib_external_lib
|
||||||
|
libzip_external_lib
|
||||||
|
freetype_external_lib
|
||||||
|
SDL2_ttf_external_lib
|
||||||
|
eigen_external_lib
|
||||||
|
boost_external_lib
|
||||||
|
)
|
||||||
|
|
||||||
|
# Линкуем OpenGL (Windows)
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(space-game001 PRIVATE opengl32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Копирование SDL2d.dll и zlibd.dll рядом с exe
|
||||||
|
# ===========================================
|
||||||
|
if (WIN32)
|
||||||
|
|
||||||
|
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
|
||||||
|
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
|
||||||
|
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/SDL2d.dll,$<TARGET_FILE_DIR:space-game001>/SDL2.dll>")
|
||||||
|
|
||||||
|
|
||||||
|
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
|
||||||
|
|
||||||
|
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zlibd.dll,${ZLIB_INSTALL_DIR}/bin/zlib.dll>")
|
||||||
|
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/zlibd.dll,$<TARGET_FILE_DIR:space-game001>/zlib.dll>")
|
||||||
|
|
||||||
|
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
|
||||||
|
|
||||||
|
|
||||||
|
add_custom_command(TARGET space-game001 POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
|
||||||
|
|
||||||
|
# Копируем SDL2 (целевое имя всегда SDL2.dll)
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"${SDL2_DLL_SRC}"
|
||||||
|
"${SDL2_DLL_DST}"
|
||||||
|
|
||||||
|
# Копируем LIBZIP (целевое имя всегда zip.dll)
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"${LIBZIP_DLL_SRC}"
|
||||||
|
"$<TARGET_FILE_DIR:space-game001>/zip.dll"
|
||||||
|
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"${ZLIB_DLL_SRC}"
|
||||||
|
"${ZLIB_DLL_DST}"
|
||||||
|
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"${SDL2TTF_DLL_SRC}"
|
||||||
|
"$<TARGET_FILE_DIR:space-game001>"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Копирование ресурсов после сборки
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
# Какие папки с ресурсами нужно копировать
|
||||||
|
set(RUNTIME_RESOURCE_DIRS
|
||||||
|
"resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Копируем ресурсы и шейдеры в папку exe и в корень build/
|
||||||
|
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||||
|
add_custom_command(TARGET space-game001 POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
|
||||||
|
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||||
|
"$<TARGET_FILE_DIR:space-game001>/${resdir}"
|
||||||
|
# 2) в корень build, если захочешь запускать из этой папки
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||||
|
"${CMAKE_BINARY_DIR}/${resdir}"
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
BIN
resources/DefaultMaterial_BaseColor.png
(Stored with Git LFS)
BIN
resources/DefaultMaterial_BaseColor.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/DefaultMaterial_BaseColor_shine.png
(Stored with Git LFS)
BIN
resources/DefaultMaterial_BaseColor_shine.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/MainCharacter_Base_color_sRGB.png
(Stored with Git LFS)
Normal file
BIN
resources/MainCharacter_Base_color_sRGB.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/box/box.png
(Stored with Git LFS)
BIN
resources/box/box.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/button.png
(Stored with Git LFS)
BIN
resources/button.png
(Stored with Git LFS)
Binary file not shown.
15
resources/config/explosion_config.json
Normal file
15
resources/config/explosion_config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"emissionPoints": [
|
||||||
|
{ "position": [0.0, 0.0, 0.0] }
|
||||||
|
],
|
||||||
|
"texture": "resources/spark_white.png",
|
||||||
|
"speedRange": [10.0, 30.0],
|
||||||
|
"zSpeedRange": [-1.0, 1.0],
|
||||||
|
"scaleRange": [0.5, 1.0],
|
||||||
|
"lifeTimeRange": [200.0, 800.0],
|
||||||
|
"emissionRate": 50.0,
|
||||||
|
"maxParticles": 5,
|
||||||
|
"particleSize": 2,
|
||||||
|
"biasX": 0.1,
|
||||||
|
"shaderProgramName": "default"
|
||||||
|
}
|
||||||
55
resources/config/game_over.json
Normal file
55
resources/config/game_over.json
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"align": "center",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080,
|
||||||
|
"background": {
|
||||||
|
"color": [0, 0, 0, 0.7]
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "gameOverText",
|
||||||
|
"x": 350,
|
||||||
|
"y": 400,
|
||||||
|
"width": 600,
|
||||||
|
"height": 150,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/gameover.png",
|
||||||
|
"hover": "resources/gameover.png",
|
||||||
|
"pressed": "resources/gameover.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "restartButton",
|
||||||
|
"x": 350,
|
||||||
|
"y": 300,
|
||||||
|
"width": 300,
|
||||||
|
"height": 80,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/shoot_normal.png",
|
||||||
|
"hover": "resources/shoot_normal.png",
|
||||||
|
"pressed": "resources/shoot_normal.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "gameOverExitButton",
|
||||||
|
"x": 650,
|
||||||
|
"y": 300,
|
||||||
|
"width": 300,
|
||||||
|
"height": 80,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/sand2.png",
|
||||||
|
"hover": "resources/sand2.png",
|
||||||
|
"pressed": "resources/sand2.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
72
resources/config/settings.json
Normal file
72
resources/config/settings.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1280,
|
||||||
|
"height": 720,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "centerPanel",
|
||||||
|
"x": 480,
|
||||||
|
"y": 160,
|
||||||
|
"width": 320,
|
||||||
|
"height": 400,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"name": "settingsButtons",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"spacing": 10,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 300,
|
||||||
|
"height": 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "Opt1",
|
||||||
|
"x": 100,
|
||||||
|
"y": 300,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/sand2.png",
|
||||||
|
"hover": "resources/sand2.png",
|
||||||
|
"pressed": "resources/sand2.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "Opt2",
|
||||||
|
"x": 100,
|
||||||
|
"y": 200,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/sand2.png",
|
||||||
|
"hover": "resources/sand2.png",
|
||||||
|
"pressed": "resources/sand2.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "backButton",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/sand2.png",
|
||||||
|
"hover": "resources/sand2.png",
|
||||||
|
"pressed": "resources/sand2.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
22
resources/config/spark_config.json
Normal file
22
resources/config/spark_config.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"emissionRate": 100.0,
|
||||||
|
"maxParticles": 200,
|
||||||
|
"particleSize": 0.3,
|
||||||
|
"biasX": 0.3,
|
||||||
|
"emissionPoints": [
|
||||||
|
{
|
||||||
|
"position": [-1.3, 0, 0.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"position": [1.3, 0.0, 0.0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"speedRange": [0.5, 2.0],
|
||||||
|
"zSpeedRange": [1.0, 3.0],
|
||||||
|
"scaleRange": [0.8, 1.2],
|
||||||
|
"lifeTimeRange": [600.0, 1400.0],
|
||||||
|
"texture": "resources/spark.png",
|
||||||
|
"shaderProgramName": "default",
|
||||||
|
"vertexShader": "resources/shaders/spark.vertex",
|
||||||
|
"fragmentShader": "resources/shaders/spark.fragment"
|
||||||
|
}
|
||||||
15
resources/config/spark_projectile_config.json
Normal file
15
resources/config/spark_projectile_config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"emissionPoints": [
|
||||||
|
{ "position": [0.0, 0.0, 0.0] }
|
||||||
|
],
|
||||||
|
"texture": "resources/spark_white.png",
|
||||||
|
"speedRange": [10.0, 30.0],
|
||||||
|
"zSpeedRange": [-1.0, 1.0],
|
||||||
|
"scaleRange": [0.5, 1.0],
|
||||||
|
"lifeTimeRange": [200.0, 800.0],
|
||||||
|
"emissionRate": 50.0,
|
||||||
|
"maxParticles": 10,
|
||||||
|
"particleSize": 0.09,
|
||||||
|
"biasX": 0.1,
|
||||||
|
"shaderProgramName": "default"
|
||||||
|
}
|
||||||
169
resources/config/ui.json
Normal file
169
resources/config/ui.json
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1280,
|
||||||
|
"height": 720,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "leftPanel",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 320,
|
||||||
|
"height": 400,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"name": "mainButtons",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"spacing": 10,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 300,
|
||||||
|
"height": 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "playButton",
|
||||||
|
"x": -1000,
|
||||||
|
"y": 500,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/sand2.png",
|
||||||
|
"hover": "./resources/sand2.png",
|
||||||
|
"pressed": "./resources/sand2.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "settingsButton",
|
||||||
|
"x": -1000,
|
||||||
|
"y": 400,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "wait",
|
||||||
|
"duration": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/sand2.png",
|
||||||
|
"hover": "./resources/sand2.png",
|
||||||
|
"pressed": "./resources/sand2.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "exitButton",
|
||||||
|
"x": -1000,
|
||||||
|
"y": 300,
|
||||||
|
"width": 200,
|
||||||
|
"height": 50,
|
||||||
|
"animations": {
|
||||||
|
"buttonsExit": {
|
||||||
|
"repeat": false,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "wait",
|
||||||
|
"duration": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
-400,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 1.0,
|
||||||
|
"easing": "easein"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bgScroll": {
|
||||||
|
"repeat": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"to": [
|
||||||
|
1280,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"duration": 5.0,
|
||||||
|
"easing": "linear"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"normal": "./resources/sand2.png",
|
||||||
|
"hover": "./resources/sand2.png",
|
||||||
|
"pressed": "./resources/sand2.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Slider",
|
||||||
|
"name": "velocitySlider",
|
||||||
|
"x": 1140,
|
||||||
|
"y": 100,
|
||||||
|
"width": 50,
|
||||||
|
"height": 500,
|
||||||
|
"value": 0.0,
|
||||||
|
"orientation": "vertical",
|
||||||
|
"textures": {
|
||||||
|
"track": "resources/velocitySliderTexture.png",
|
||||||
|
"knob": "resources/velocitySliderButton.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "shootButton",
|
||||||
|
"x": 100,
|
||||||
|
"y": 100,
|
||||||
|
"width": 100,
|
||||||
|
"height": 100,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/shoot_normal.png",
|
||||||
|
"hover": "resources/shoot_hover.png",
|
||||||
|
"pressed": "resources/shoot_pressed.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
resources/gameover.png
(Stored with Git LFS)
Normal file
BIN
resources/gameover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/musicVolumeBarButton.png
(Stored with Git LFS)
BIN
resources/musicVolumeBarButton.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/musicVolumeBarTexture.png
(Stored with Git LFS)
BIN
resources/musicVolumeBarTexture.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/rock.png
(Stored with Git LFS)
BIN
resources/rock.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/rockdark3.png
(Stored with Git LFS)
Normal file
BIN
resources/rockdark3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/rockx.png
(Stored with Git LFS)
BIN
resources/rockx.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/sand.png
(Stored with Git LFS)
BIN
resources/sand.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/sand2.png
(Stored with Git LFS)
BIN
resources/sand2.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/sandx.png
(Stored with Git LFS)
BIN
resources/sandx.png
(Stored with Git LFS)
Binary file not shown.
17
resources/shaders/defaultAtmosphere.vertex
Normal file
17
resources/shaders/defaultAtmosphere.vertex
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Вершинный шейдер:
|
||||||
|
attribute vec3 vPosition;
|
||||||
|
attribute vec3 vNormal;
|
||||||
|
|
||||||
|
uniform mat4 ProjectionModelViewMatrix;
|
||||||
|
uniform mat4 ModelViewMatrix;
|
||||||
|
|
||||||
|
varying vec3 vWorldNormal;
|
||||||
|
varying vec3 vViewNormal;
|
||||||
|
varying vec3 vViewPosition;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vWorldNormal = vPosition; // Локальная позиция вершины на сфере радиуса 1.0
|
||||||
|
vViewPosition = (ModelViewMatrix * vec4(vPosition, 1.0)).xyz;
|
||||||
|
vViewNormal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
|
||||||
|
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user