Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efc696de09 | ||
|
|
074c586edf | ||
|
|
d64659c8d6 | ||
|
|
d557c4b530 | ||
|
|
fd098e8346 | ||
|
|
3622f2b888 | ||
|
|
ce8bc2d142 | ||
|
|
e293f58413 | ||
|
|
0495c3946c | ||
|
|
28a3ace187 | ||
|
|
61d806a7a1 | ||
|
|
e4bcf62afa | ||
|
|
709327b3af | ||
|
|
920376d686 | ||
|
|
a6d9716ddf | ||
|
|
1a6410dc12 | ||
|
|
360d6999c2 | ||
|
|
7fe425bf07 | ||
|
|
c9324bd30b | ||
|
|
777ee30a24 | ||
|
|
c9e9755e28 | ||
|
|
f8b4174eea | ||
|
|
9d4da63e36 | ||
|
|
a0b0db6f6a | ||
|
|
60a1422232 | ||
|
|
25bb79fb42 | ||
|
|
8e0936836e | ||
|
|
542939207f | ||
|
|
2195542974 | ||
|
|
c6bd5385e3 | ||
|
|
abd3da30e2 | ||
|
|
bc62c34cf7 | ||
|
|
0d7d430909 | ||
|
|
57789eed24 | ||
|
|
8efe063749 | ||
|
|
e6f332244c | ||
|
|
77827af150 | ||
|
|
66da012d5a | ||
|
|
b3a5e36e1b | ||
|
|
b633aa01f5 | ||
|
|
8191db025f | ||
|
|
c198f39515 | ||
|
|
01be8852c6 | ||
|
|
1bf54b5367 | ||
|
|
fe01a5d1b2 | ||
|
|
b6e0422d71 | ||
|
|
3778a603f2 | ||
|
|
e310822462 | ||
|
|
3d3c3b2a36 | ||
|
|
b5bcd3a812 | ||
|
|
16e7580418 | ||
|
|
be4dcd4cf9 | ||
|
|
a1dda3fd50 | ||
|
|
92ba3f2b60 | ||
|
|
f2f56125f0 | ||
|
|
2ef2e456f7 | ||
|
|
83cceecccd | ||
|
|
24851a89e8 | ||
|
|
d47cfd1b6b | ||
|
|
71346b4227 | ||
|
|
08e1174eaf | ||
|
|
cc2eba1352 | ||
|
|
cfee60d71a | ||
|
|
dc76986cd3 | ||
|
|
61665434ab | ||
|
|
2494a44e4a | ||
|
|
d8795ec076 | ||
|
|
ceebb13719 | ||
|
|
a47306533e | ||
|
|
f708843747 | ||
|
|
a589765b7e | ||
|
|
4eaefd25d4 | ||
|
|
eff7322140 | ||
|
|
44dd09fcb5 | ||
|
|
de2e18e38f | ||
|
|
984d7489d5 | ||
|
|
f7e6f063d1 | ||
|
|
db8b9b7a18 | ||
|
|
493eb5a5d7 | ||
|
|
422ff1fbe3 | ||
|
|
6465549c3b | ||
|
|
34759326b9 | ||
|
|
7125674308 | ||
|
|
bc2997d86f | ||
|
|
943b6b4d21 | ||
|
|
b3fda88784 | ||
|
|
e1b491be73 | ||
|
|
178f69967b | ||
|
|
90e2a369f4 | ||
|
|
403b93e7f5 | ||
|
|
63c9d7b35b | ||
|
|
995e2c8e5b | ||
|
|
2e4a7ab467 | ||
|
|
a96d0b207a | ||
|
|
7840bfdb4e | ||
|
|
56ce826205 | ||
|
|
5364b97235 | ||
|
|
beac06fd45 | ||
|
|
ca98fea2bd | ||
|
|
a4d5017270 | ||
|
|
45826ab5b5 | ||
|
|
e5296a41b9 | ||
|
|
18674efd8e | ||
|
|
5e7c050c68 | ||
|
|
c88784a669 | ||
|
|
181132e092 | ||
|
|
6fe10eacfc | ||
|
|
9e692c9ec3 | ||
|
|
785b96ce82 | ||
|
|
a8ded217df | ||
|
|
3e8dba36a4 | ||
|
|
84a5e888a0 | ||
|
|
86a9f38c3b | ||
|
|
24aa7007bb | ||
|
|
59b5bea540 | ||
|
|
9da3cc4401 | ||
|
|
4a542fd6c8 | ||
|
|
a06cb77a66 | ||
|
|
3c2c06ca40 | ||
|
|
928600acd4 | ||
|
|
7ccda081b0 | ||
|
|
41f370f2ee | ||
|
|
4af20c65e6 | ||
|
|
ac551122d9 | ||
|
|
8528b4dc90 | ||
|
|
c35e093cc5 | ||
|
|
a8c76dee3c | ||
|
|
e84a094ad9 | ||
|
|
b0b7391939 | ||
|
|
3e2632c5b5 | ||
|
|
70ef730e86 | ||
|
|
92b288d978 | ||
|
|
cca1d0ece2 | ||
|
|
8effdd5dfa | ||
|
|
e78429b600 | ||
|
|
05a13a40e7 | ||
|
|
ef7617701e | ||
|
|
2f50dc1522 | ||
|
|
9eafcd27fb | ||
|
|
3879eb1d75 | ||
|
|
78046e5e2d | ||
|
|
26233f934f | ||
|
|
167c04b107 | ||
|
|
5fcb1d1234 | ||
|
|
70a617b688 | ||
|
|
bb0f584bf6 | ||
|
|
6b4b549b3c | ||
|
|
ffbecbbcde | ||
|
|
2728ca9646 | ||
|
|
7e5aa22fee | ||
|
|
c20e82634a | ||
|
|
9f82e7a2e6 | ||
|
|
7418bbbe27 | ||
|
|
be2aa76f8b | ||
|
|
e8f1786a2a | ||
|
|
e30f8a4c70 | ||
|
|
f8642efa23 | ||
|
|
5da5a754fe | ||
|
|
fb1b0a1c2e | ||
|
|
74c2f786a1 | ||
|
|
5216965496 | ||
|
|
ad7294ceea | ||
|
|
3893038d9a | ||
|
|
cb2e8318e7 | ||
|
|
5cca2413fe | ||
|
|
db4254e205 | ||
|
|
fa3a4c66c9 | ||
|
|
84006380db | ||
|
|
f6cc30a30c | ||
|
|
d883b58260 | ||
|
|
b59a10b7e6 | ||
|
|
5b57696acf | ||
|
|
1265d87bc5 | ||
|
|
46d7a2a25d | ||
|
|
0babfff28b | ||
|
|
ed6e1bacc7 | ||
|
|
9c39782729 | ||
|
|
efad2dde3e | ||
|
|
84d8ee7eee | ||
|
|
7693237aa5 | ||
|
|
528c94e921 | ||
|
|
a8700af1d0 | ||
|
|
9793c5bf06 | ||
|
|
b56fafa0e0 | ||
|
|
22ee99418d | ||
|
|
4eda57b4e4 | ||
|
|
2ffd8124f2 | ||
|
|
210c191d41 | ||
|
|
251a59ddbe | ||
|
|
5df2216da6 | ||
|
|
2c1c077611 | ||
|
|
629c9ba7b1 | ||
|
|
a59bcc0c4b | ||
|
|
cf1ec1e76f | ||
|
|
8a374a4231 | ||
|
|
da2b6e5577 | ||
|
|
aa9590e2f4 | ||
|
|
a1d54b01b0 | ||
|
|
65a2066727 | ||
|
|
c49c4626d0 | ||
|
|
ec156bd679 | ||
|
|
7b1d9363a0 | ||
|
|
82ca994fac | ||
|
|
0d2f705d42 | ||
|
|
e799b4ec79 | ||
|
|
c8076b6018 | ||
|
|
88f68b20ea | ||
|
|
d249f17d22 | ||
|
|
bee3545196 | ||
|
|
e050d7d0ec | ||
|
|
e18484ea62 | ||
|
|
204b79bf06 | ||
|
|
50232c0816 | ||
|
|
e8fb14b809 | ||
|
|
d425167dce | ||
|
|
f41228acf8 | ||
|
|
cacc18dc7e | ||
|
|
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 | ||
|
|
f197dfcf02 | ||
|
|
1af829bf49 | ||
|
|
7287f4b619 | ||
|
|
14b43239b5 | ||
|
|
d57112be35 | ||
|
|
7fc46d36a1 | ||
|
|
bee2fff538 | ||
|
|
29834d528a | ||
|
|
068c319890 | ||
|
|
53ea1f768c | ||
|
|
b369c52270 | ||
|
|
01d362615f | ||
|
|
e690d0f8e2 | ||
|
|
854a425d85 | ||
|
|
0464bed4cf | ||
|
|
7c272a2ee6 | ||
|
|
db25abc305 | ||
|
|
57ff27b0f6 | ||
|
|
a91d7c04e8 | ||
|
|
16ba536481 | ||
|
|
7a1961b5d1 | ||
|
|
af550f56a7 | ||
|
|
27fb7a448c | ||
|
|
2b6af91992 | ||
|
|
4afb9d6f4f | ||
|
|
fe361885c0 | ||
|
|
3887208b8d | ||
|
|
4c837eff0a | ||
|
|
8f9a18d960 | ||
|
|
bc49540a95 | ||
|
|
d2605d9108 | ||
|
|
115cbbb7fa | ||
|
|
ae1ca71a20 | ||
|
|
c02273d29d | ||
|
|
fec7e08c2b | ||
|
|
0f937e2e02 | ||
|
|
b855cff0e6 | ||
|
|
b768b27d49 | ||
|
|
c5044d42de | ||
|
|
88bc764f08 | ||
|
|
72a2b4c47c | ||
|
|
38f4f6b7fc | ||
|
|
af00e77dc2 | ||
|
|
0322098efa | ||
|
|
2b19a3eaba |
No files matched your search
@@ -0,0 +1,7 @@
|
||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.anim filter=lfs diff=lfs merge=lfs -text
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||
+5
-1
@@ -400,4 +400,8 @@ jumpingbird.*
|
||||
jumpingbird.data
|
||||
build
|
||||
build-emcmake
|
||||
thirdparty1
|
||||
thirdparty
|
||||
|
||||
proj-web/build
|
||||
proj-windows/build
|
||||
public
|
||||
@@ -1,93 +0,0 @@
|
||||
#ifdef AUDIO
|
||||
|
||||
#include "AudioPlayerAsync.h"
|
||||
|
||||
|
||||
AudioPlayerAsync::AudioPlayerAsync() : worker(&AudioPlayerAsync::workerThread, this) {}
|
||||
|
||||
AudioPlayerAsync::~AudioPlayerAsync() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
stop = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
worker.join();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::stopAsync() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
audioPlayer->stop();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::resetAsync() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
audioPlayer.reset();
|
||||
audioPlayer = std::make_unique<AudioPlayer>();
|
||||
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::playSoundAsync(std::string soundName) {
|
||||
|
||||
soundNameMutex.lock();
|
||||
latestSoundName = soundName;
|
||||
soundNameMutex.unlock();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
if (audioPlayer) {
|
||||
audioPlayer->playSound(latestSoundName);
|
||||
}
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::playMusicAsync(std::string musicName) {
|
||||
|
||||
musicNameMutex.lock();
|
||||
latestMusicName = musicName;
|
||||
musicNameMutex.unlock();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([this]() {
|
||||
//audioPlayerMutex.lock();
|
||||
if (audioPlayer) {
|
||||
audioPlayer->playMusic(latestMusicName);
|
||||
}
|
||||
//audioPlayerMutex.unlock();
|
||||
});
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::workerThread() {
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock, [this]() { return !taskQueue.empty() || stop; });
|
||||
|
||||
if (stop && taskQueue.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
task = taskQueue.front();
|
||||
taskQueue.pop();
|
||||
}
|
||||
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include "cmakeaudioplayer/include/AudioPlayer.hpp"
|
||||
|
||||
|
||||
class AudioPlayerAsync {
|
||||
public:
|
||||
AudioPlayerAsync();
|
||||
~AudioPlayerAsync();
|
||||
|
||||
void resetAsync();
|
||||
|
||||
void playSoundAsync(std::string soundName);
|
||||
|
||||
void playMusicAsync(std::string musicName);
|
||||
|
||||
void stopAsync();
|
||||
|
||||
void exit()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
std::thread worker;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AudioPlayer> audioPlayer;
|
||||
//std::mutex audioPlayerMutex;
|
||||
|
||||
std::mutex soundNameMutex;
|
||||
std::mutex musicNameMutex;
|
||||
|
||||
std::string latestSoundName;
|
||||
std::string latestMusicName;
|
||||
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
std::queue<std::function<void()>> taskQueue;
|
||||
bool stop = false;
|
||||
|
||||
void workerThread();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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)) {
|
||||
// ��������� ����� (��� �������)
|
||||
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) � ���� ��������
|
||||
// Then multiply by boneMatrixWorld � �� �������� �������� �����
|
||||
|
||||
int parent;
|
||||
std::vector<int> children;
|
||||
};
|
||||
|
||||
struct BoneWeight
|
||||
{
|
||||
int boneIndex = -1;
|
||||
float weight = 0;
|
||||
};
|
||||
|
||||
struct AnimationKeyFrame
|
||||
{
|
||||
int frame;
|
||||
std::vector<Bone> bones;
|
||||
};
|
||||
|
||||
struct Animation
|
||||
{
|
||||
std::vector<AnimationKeyFrame> keyFrames;
|
||||
};
|
||||
|
||||
struct BoneSystem
|
||||
{
|
||||
VertexDataStruct mesh;
|
||||
VertexDataStruct startMesh;
|
||||
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
|
||||
|
||||
Matrix4f armatureMatrix;
|
||||
|
||||
std::vector<Bone> startBones;
|
||||
std::vector<Bone> currentBones;
|
||||
|
||||
std::vector<Animation> animations;
|
||||
|
||||
void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
|
||||
void Interpolate(int frame);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
-441
@@ -1,441 +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(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"
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlib.lib"
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlibd.lib"
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlib1.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()
|
||||
|
||||
log("Building zlib (Debug) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--build "${ZLIB_BUILD_DIR}" --config Debug
|
||||
RESULT_VARIABLE _zlib_build_res
|
||||
)
|
||||
if(NOT _zlib_build_res EQUAL 0)
|
||||
message(FATAL_ERROR "zlib build failed")
|
||||
endif()
|
||||
|
||||
log("Installing zlib (Debug) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--install "${ZLIB_BUILD_DIR}" --config Debug
|
||||
RESULT_VARIABLE _zlib_inst_res
|
||||
)
|
||||
if(NOT _zlib_inst_res EQUAL 0)
|
||||
message(FATAL_ERROR "zlib install failed")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(ZLIB_LIB_FILE "")
|
||||
foreach(candidate
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlib.lib"
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlibd.lib"
|
||||
"${ZLIB_INSTALL_DIR}/lib/zlib1.lib"
|
||||
)
|
||||
if(EXISTS "${candidate}")
|
||||
set(ZLIB_LIB_FILE "${candidate}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(ZLIB_LIB_FILE STREQUAL "")
|
||||
message(FATAL_ERROR "Could not find zlib library in ${ZLIB_INSTALL_DIR}/lib")
|
||||
endif()
|
||||
|
||||
add_library(zlib_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||
set_target_properties(zlib_external_lib PROPERTIES
|
||||
IMPORTED_LOCATION "${ZLIB_LIB_FILE}"
|
||||
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()
|
||||
|
||||
log("Building SDL2 (Debug) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--build "${SDL2_BUILD_DIR}" --config Debug
|
||||
RESULT_VARIABLE _sdl_build_res
|
||||
)
|
||||
if(NOT _sdl_build_res EQUAL 0)
|
||||
message(FATAL_ERROR "SDL2 build failed")
|
||||
endif()
|
||||
|
||||
log("Installing SDL2 (Debug) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--install "${SDL2_BUILD_DIR}" --config Debug
|
||||
RESULT_VARIABLE _sdl_inst_res
|
||||
)
|
||||
if(NOT _sdl_inst_res EQUAL 0)
|
||||
message(FATAL_ERROR "SDL2 install failed")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SDL2_LIB_FILE "")
|
||||
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(SDL2_LIB_FILE "${candidate}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(SDL2_LIB_FILE STREQUAL "")
|
||||
message(FATAL_ERROR "Could not find SDL2 library in ${SDL2_INSTALL_DIR}/lib")
|
||||
endif()
|
||||
|
||||
set(SDL2MAIN_LIB_FILE "")
|
||||
if(EXISTS "${SDL2_INSTALL_DIR}/lib/SDL2main.lib")
|
||||
set(SDL2MAIN_LIB_FILE "${SDL2_INSTALL_DIR}/lib/SDL2main.lib")
|
||||
endif()
|
||||
|
||||
add_library(SDL2_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||
set_target_properties(SDL2_external_lib PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2_LIB_FILE}"
|
||||
# Оба include-пути: и include, и include/SDL2
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include;${SDL2_INSTALL_DIR}/include/SDL2"
|
||||
)
|
||||
|
||||
if(NOT SDL2MAIN_LIB_FILE STREQUAL "")
|
||||
add_library(SDL2main_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||
set_target_properties(SDL2main_external_lib PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2MAIN_LIB_FILE}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include"
|
||||
)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# 3) libpng (v1.6.51.zip → libpng-1.6.51)
|
||||
# ВАЖНО: не вызываем "cmake --install",
|
||||
# берём .lib из build/Debug
|
||||
# ===========================================
|
||||
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(_have_png FALSE)
|
||||
set(_libpng_candidates
|
||||
"${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
|
||||
"${LIBPNG_BUILD_DIR}/Debug/libpng16d.lib"
|
||||
"${LIBPNG_BUILD_DIR}/Debug/png16d.lib"
|
||||
"${LIBPNG_BUILD_DIR}/libpng16_staticd.lib"
|
||||
"${LIBPNG_BUILD_DIR}/libpng16d.lib"
|
||||
"${LIBPNG_INSTALL_DIR}/lib/libpng16_staticd.lib"
|
||||
"${LIBPNG_INSTALL_DIR}/lib/libpng16d.lib"
|
||||
"${LIBPNG_INSTALL_DIR}/lib/png16d.lib"
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
log("Building libpng (Debug) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--build "${LIBPNG_BUILD_DIR}" --config Debug
|
||||
RESULT_VARIABLE _png_build_res
|
||||
)
|
||||
if(NOT _png_build_res EQUAL 0)
|
||||
message(FATAL_ERROR "libpng build failed")
|
||||
endif()
|
||||
|
||||
# без --install, чтобы не упереться в INSTALL target
|
||||
endif()
|
||||
|
||||
# Ищем конкретный .lib для линковки
|
||||
set(LIBPNG_LIB_FILE "")
|
||||
foreach(candidate IN LISTS _libpng_candidates)
|
||||
if(EXISTS "${candidate}")
|
||||
set(LIBPNG_LIB_FILE "${candidate}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(LIBPNG_LIB_FILE STREQUAL "")
|
||||
message(FATAL_ERROR "Could not find libpng library (checked build/Debug and install/lib)")
|
||||
endif()
|
||||
|
||||
add_library(libpng_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||
set_target_properties(libpng_external_lib PROPERTIES
|
||||
IMPORTED_LOCATION "${LIBPNG_LIB_FILE}"
|
||||
# png.h, pngconf.h – в SRC, pnglibconf.h – в BUILD
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBPNG_SRC_DIR};${LIBPNG_BUILD_DIR}"
|
||||
)
|
||||
|
||||
# ===========================================
|
||||
# Основной проект 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
|
||||
Math.cpp
|
||||
ZLMath.h
|
||||
OpenGlExtensions.cpp
|
||||
OpenGlExtensions.h
|
||||
Utils.cpp
|
||||
Utils.h
|
||||
)
|
||||
|
||||
# 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"
|
||||
)
|
||||
|
||||
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, если он вообще установлен
|
||||
if(TARGET SDL2main_external_lib)
|
||||
target_link_libraries(space_game001 PRIVATE SDL2main_external_lib)
|
||||
endif()
|
||||
|
||||
# Линкуем сторонние библиотеки
|
||||
target_link_libraries(space_game001 PRIVATE
|
||||
SDL2_external_lib
|
||||
libpng_external_lib
|
||||
zlib_external_lib
|
||||
)
|
||||
|
||||
# Линкуем OpenGL (Windows)
|
||||
if(WIN32)
|
||||
target_link_libraries(space_game001 PRIVATE opengl32)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# Копирование SDL2d.dll и zlibd.dll рядом с exe
|
||||
# ===========================================
|
||||
if (WIN32)
|
||||
add_custom_command(TARGET space_game001 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying SDL2d.dll and zlibd.dll to output folder..."
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2_BUILD_DIR}/Debug/SDL2d.dll"
|
||||
"$<TARGET_FILE_DIR:space_game001>/SDL2d.dll"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${ZLIB_BUILD_DIR}/Debug/zlibd.dll"
|
||||
"$<TARGET_FILE_DIR:space_game001>/zlibd.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,34 +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 = 20.f;
|
||||
|
||||
bool Environment::leftPressed = false;
|
||||
bool Environment::rightPressed = false;
|
||||
bool Environment::upPressed = false;
|
||||
bool Environment::downPressed = false;
|
||||
|
||||
Vector3f Environment::cameraShift = {0, 0, 0};
|
||||
Vector3f Environment::characterPos = {0, 0, 0};
|
||||
|
||||
float Environment::cameraPhi = 0.f;
|
||||
float Environment::cameraAlpha = 0.3*M_PI / 2.0;
|
||||
|
||||
bool Environment::settings_inverseVertical = false;
|
||||
|
||||
SDL_Window* Environment::window = nullptr;
|
||||
|
||||
bool Environment::showMouse = false;
|
||||
|
||||
bool Environment::exitGameLoop = false;
|
||||
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
@@ -1,39 +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 Vector3f cameraShift;
|
||||
static Vector3f characterPos;
|
||||
static float cameraPhi;
|
||||
static float cameraAlpha;
|
||||
|
||||
|
||||
|
||||
static bool settings_inverseVertical;
|
||||
|
||||
static SDL_Window* window;
|
||||
|
||||
static bool showMouse;
|
||||
static bool exitGameLoop;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -1,165 +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"
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
const char* CONST_ZIP_FILE = "";
|
||||
|
||||
Game::Game()
|
||||
: window(nullptr)
|
||||
, glContext(nullptr)
|
||||
, newTickCount(0)
|
||||
, lastTickCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
#else
|
||||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE);
|
||||
#endif
|
||||
|
||||
//Load texture
|
||||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sship001x.png"));
|
||||
spaceshipBase = LoadFromTextFile02("./resources/spaceship004.txt");
|
||||
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
||||
spaceship.AssignFrom(spaceshipBase);
|
||||
spaceship.RefreshVBO();
|
||||
|
||||
|
||||
renderer.InitOpenGL();
|
||||
|
||||
}
|
||||
|
||||
void Game::drawScene() {
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
|
||||
CheckGlError();
|
||||
|
||||
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),
|
||||
1, 1000);
|
||||
renderer.PushMatrix();
|
||||
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f*Environment::zoom });
|
||||
renderer.RotateMatrix(QuatFromRotateAroundX(M_PI/6.0));
|
||||
//renderer.RotateMatrix(QuatFromRotateAroundX(Environment::cameraAlpha));
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(spaceship);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
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);
|
||||
|
||||
Environment::cameraAlpha = Environment::cameraAlpha + delta * M_PI / 10000.f;
|
||||
|
||||
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_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;
|
||||
}
|
||||
/*if (Environment::zoom > 4) {
|
||||
Environment::zoom = 4;
|
||||
}*/
|
||||
|
||||
//this->modelMeshRender.data.Scale(0.5);
|
||||
//this->modelMeshRender.RefreshVBO();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "OpenGlExtensions.h"
|
||||
#include "Renderer.h"
|
||||
#include "Environment.h"
|
||||
#include "TextureManager.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
|
||||
void setup();
|
||||
void update();
|
||||
void render();
|
||||
|
||||
bool shouldExit() const { return Environment::exitGameLoop; }
|
||||
|
||||
private:
|
||||
void processTickCount();
|
||||
void drawScene();
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
Renderer renderer;
|
||||
|
||||
size_t newTickCount;
|
||||
size_t lastTickCount;
|
||||
|
||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||
|
||||
std::shared_ptr<Texture> spaceshipTexture;
|
||||
VertexDataStruct spaceshipBase;
|
||||
VertexRenderStruct spaceship;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -1,734 +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;
|
||||
}
|
||||
|
||||
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 MakePerspectiveMatrix(float fovY, float aspectRatio, float zNear, float zFar)
|
||||
{
|
||||
float tanHalfFovy = tan(fovY / 2.f);
|
||||
Matrix4f r;
|
||||
|
||||
if (zNear >= zFar || aspectRatio == 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid perspective parameters");
|
||||
}
|
||||
|
||||
r.m[0] = 1.f / (aspectRatio * tanHalfFovy);
|
||||
r.m[1] = 0;
|
||||
r.m[2] = 0;
|
||||
r.m[3] = 0;
|
||||
|
||||
r.m[4] = 0;
|
||||
r.m[5] = 1.f / (tanHalfFovy);
|
||||
r.m[6] = 0;
|
||||
r.m[7] = 0;
|
||||
|
||||
r.m[8] = 0;
|
||||
r.m[9] = 0;
|
||||
r.m[10] = -(zFar + zNear) / (zFar - zNear);
|
||||
r.m[11] = -1;
|
||||
|
||||
r.m[12] = 0;
|
||||
r.m[13] = 0;
|
||||
r.m[14] = -(2.f * zFar * zNear) / (zFar - zNear);
|
||||
r.m[15] = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f QuatToMatrix(const Vector4f& q)
|
||||
{
|
||||
Matrix3f m;
|
||||
|
||||
float wx, wy, wz, xx, yy, yz, xy, xz, zz, s, x2, y2, z2;
|
||||
|
||||
s = 2.0f / (q.v[0] * q.v[0] + q.v[1] * q.v[1] + q.v[2] * q.v[2] + q.v[3] * q.v[3]);
|
||||
|
||||
|
||||
x2 = q.v[0] * s;
|
||||
y2 = q.v[1] * s;
|
||||
z2 = q.v[2] * s;
|
||||
|
||||
wx = q.v[3] * x2; wy = q.v[3] * y2; wz = q.v[3] * z2;
|
||||
xx = q.v[0] * x2; xy = q.v[1] * x2; xz = q.v[2] * x2;
|
||||
yy = q.v[1] * y2; yz = q.v[2] * y2;
|
||||
zz = q.v[2] * z2;
|
||||
|
||||
m.m[0] = 1.0f - (yy + zz);
|
||||
m.m[1] = xy + wz;
|
||||
m.m[2] = xz - wy;
|
||||
|
||||
m.m[3] = xy - wz;
|
||||
m.m[4] = 1.0f - (xx + zz);
|
||||
m.m[5] = yz + wx;
|
||||
|
||||
m.m[6] = xz + wy;
|
||||
m.m[7] = yz - wx;
|
||||
m.m[8] = 1.0f - (xx + yy);
|
||||
|
||||
return m;
|
||||
|
||||
}
|
||||
|
||||
Vector4f MatrixToQuat(const Matrix3f& m)
|
||||
{
|
||||
Vector4f r;
|
||||
float trace = m.m[0] + m.m[4] + m.m[8];
|
||||
|
||||
if (trace > 0)
|
||||
{
|
||||
float s = 0.5f / sqrtf(trace + 1.0f);
|
||||
r.v[3] = 0.25f / s;
|
||||
r.v[0] = (m.m[5] - m.m[7]) * s;
|
||||
r.v[1] = (m.m[6] - m.m[2]) * s;
|
||||
r.v[2] = (m.m[1] - m.m[3]) * s;
|
||||
}
|
||||
else if (m.m[0] > m.m[4] && m.m[0] > m.m[8])
|
||||
{
|
||||
float s = 2.0f * sqrtf(1.0f + m.m[0] - m.m[4] - m.m[8]);
|
||||
r.v[3] = (m.m[5] - m.m[7]) / s;
|
||||
r.v[0] = 0.25f * s;
|
||||
r.v[1] = (m.m[1] + m.m[3]) / s;
|
||||
r.v[2] = (m.m[6] + m.m[2]) / s;
|
||||
}
|
||||
else if (m.m[4] > m.m[8])
|
||||
{
|
||||
float s = 2.0f * sqrtf(1.0f + m.m[4] - m.m[0] - m.m[8]);
|
||||
r.v[3] = (m.m[6] - m.m[2]) / s;
|
||||
r.v[0] = (m.m[1] + m.m[3]) / s;
|
||||
r.v[1] = 0.25f * s;
|
||||
r.v[2] = (m.m[5] + m.m[7]) / s;
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = 2.0f * sqrtf(1.0f + m.m[8] - m.m[0] - m.m[4]);
|
||||
r.v[3] = (m.m[1] - m.m[3]) / s;
|
||||
r.v[0] = (m.m[6] + m.m[2]) / s;
|
||||
r.v[1] = (m.m[5] + m.m[7]) / s;
|
||||
r.v[2] = 0.25f * s;
|
||||
}
|
||||
|
||||
return r.normalized();
|
||||
}
|
||||
|
||||
Vector4f QuatFromRotateAroundX(float angle)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = sinf(angle * 0.5f);
|
||||
result.v[1] = 0.f;
|
||||
result.v[2] = 0.f;
|
||||
result.v[3] = cosf(angle * 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4f QuatFromRotateAroundY(float angle)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = 0.f;
|
||||
result.v[1] = sinf(angle * 0.5f);
|
||||
result.v[2] = 0.f;
|
||||
result.v[3] = cosf(angle * 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector4f QuatFromRotateAroundZ(float angle)
|
||||
{
|
||||
Vector4f result;
|
||||
|
||||
result.v[0] = 0.f;
|
||||
result.v[1] = 0.f;
|
||||
result.v[2] = sinf(angle * 0.5f);
|
||||
result.v[3] = cosf(angle * 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Matrix3f TransposeMatrix(const Matrix3f& m)
|
||||
{
|
||||
Matrix3f r;
|
||||
r.m[0] = m.m[0];
|
||||
r.m[1] = m.m[3];
|
||||
r.m[2] = m.m[6];
|
||||
r.m[3] = m.m[1];
|
||||
r.m[4] = m.m[4];
|
||||
r.m[5] = m.m[7];
|
||||
r.m[6] = m.m[2];
|
||||
r.m[7] = m.m[5];
|
||||
r.m[8] = m.m[8];
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f InverseMatrix(const Matrix3f& m)
|
||||
{
|
||||
float d;
|
||||
Matrix3f r;
|
||||
|
||||
d = m.m[0] * (m.m[4] * m.m[8] - m.m[5] * m.m[7]);
|
||||
d -= m.m[1] * (m.m[3] * m.m[8] - m.m[6] * m.m[5]);
|
||||
d += m.m[2] * (m.m[3] * m.m[7] - m.m[6] * m.m[4]);
|
||||
|
||||
if (fabs(d) < 0.01f)
|
||||
{
|
||||
throw std::runtime_error("Error: matrix cannot be inversed!");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
r.m[0] = (m.m[4] * m.m[8] - m.m[5] * m.m[7]) / d;
|
||||
r.m[1] = -(m.m[1] * m.m[8] - m.m[2] * m.m[7]) / d;
|
||||
r.m[2] = (m.m[1] * m.m[5] - m.m[2] * m.m[4]) / d;
|
||||
|
||||
r.m[3] = -(m.m[3] * m.m[8] - m.m[5] * m.m[6]) / d;
|
||||
r.m[4] = (m.m[0] * m.m[8] - m.m[2] * m.m[6]) / d;
|
||||
r.m[5] = -(m.m[0] * m.m[5] - m.m[2] * m.m[3]) / d;
|
||||
|
||||
r.m[6] = (m.m[3] * m.m[7] - m.m[6] * m.m[4]) / d;
|
||||
r.m[7] = -(m.m[0] * m.m[7] - m.m[6] * m.m[1]) / d;
|
||||
r.m[8] = (m.m[0] * m.m[4] - m.m[1] * m.m[3]) / d;
|
||||
|
||||
};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Matrix4f InverseMatrix(const Matrix4f& mat)
|
||||
{
|
||||
Matrix4f inv;
|
||||
float det;
|
||||
|
||||
inv.m[0] = mat.m[5] * mat.m[10] * mat.m[15] -
|
||||
mat.m[5] * mat.m[11] * mat.m[14] -
|
||||
mat.m[9] * mat.m[6] * mat.m[15] +
|
||||
mat.m[9] * mat.m[7] * mat.m[14] +
|
||||
mat.m[13] * mat.m[6] * mat.m[11] -
|
||||
mat.m[13] * mat.m[7] * mat.m[10];
|
||||
|
||||
inv.m[4] = -mat.m[4] * mat.m[10] * mat.m[15] +
|
||||
mat.m[4] * mat.m[11] * mat.m[14] +
|
||||
mat.m[8] * mat.m[6] * mat.m[15] -
|
||||
mat.m[8] * mat.m[7] * mat.m[14] -
|
||||
mat.m[12] * mat.m[6] * mat.m[11] +
|
||||
mat.m[12] * mat.m[7] * mat.m[10];
|
||||
|
||||
inv.m[8] = mat.m[4] * mat.m[9] * mat.m[15] -
|
||||
mat.m[4] * mat.m[11] * mat.m[13] -
|
||||
mat.m[8] * mat.m[5] * mat.m[15] +
|
||||
mat.m[8] * mat.m[7] * mat.m[13] +
|
||||
mat.m[12] * mat.m[5] * mat.m[11] -
|
||||
mat.m[12] * mat.m[7] * mat.m[9];
|
||||
|
||||
inv.m[12] = -mat.m[4] * mat.m[9] * mat.m[14] +
|
||||
mat.m[4] * mat.m[10] * mat.m[13] +
|
||||
mat.m[8] * mat.m[5] * mat.m[14] -
|
||||
mat.m[8] * mat.m[6] * mat.m[13] -
|
||||
mat.m[12] * mat.m[5] * mat.m[10] +
|
||||
mat.m[12] * mat.m[6] * mat.m[9];
|
||||
|
||||
inv.m[1] = -mat.m[1] * mat.m[10] * mat.m[15] +
|
||||
mat.m[1] * mat.m[11] * mat.m[14] +
|
||||
mat.m[9] * mat.m[2] * mat.m[15] -
|
||||
mat.m[9] * mat.m[3] * mat.m[14] -
|
||||
mat.m[13] * mat.m[2] * mat.m[11] +
|
||||
mat.m[13] * mat.m[3] * mat.m[10];
|
||||
|
||||
inv.m[5] = mat.m[0] * mat.m[10] * mat.m[15] -
|
||||
mat.m[0] * mat.m[11] * mat.m[14] -
|
||||
mat.m[8] * mat.m[2] * mat.m[15] +
|
||||
mat.m[8] * mat.m[3] * mat.m[14] +
|
||||
mat.m[12] * mat.m[2] * mat.m[11] -
|
||||
mat.m[12] * mat.m[3] * mat.m[10];
|
||||
|
||||
inv.m[9] = -mat.m[0] * mat.m[9] * mat.m[15] +
|
||||
mat.m[0] * mat.m[11] * mat.m[13] +
|
||||
mat.m[8] * mat.m[1] * mat.m[15] -
|
||||
mat.m[8] * mat.m[3] * mat.m[13] -
|
||||
mat.m[12] * mat.m[1] * mat.m[11] +
|
||||
mat.m[12] * mat.m[3] * mat.m[9];
|
||||
|
||||
inv.m[13] = mat.m[0] * mat.m[9] * mat.m[14] -
|
||||
mat.m[0] * mat.m[10] * mat.m[13] -
|
||||
mat.m[8] * mat.m[1] * mat.m[14] +
|
||||
mat.m[8] * mat.m[2] * mat.m[13] +
|
||||
mat.m[12] * mat.m[1] * mat.m[10] -
|
||||
mat.m[12] * mat.m[2] * mat.m[9];
|
||||
|
||||
inv.m[2] = mat.m[1] * mat.m[6] * mat.m[15] -
|
||||
mat.m[1] * mat.m[7] * mat.m[14] -
|
||||
mat.m[5] * mat.m[2] * mat.m[15] +
|
||||
mat.m[5] * mat.m[3] * mat.m[14] +
|
||||
mat.m[13] * mat.m[2] * mat.m[7] -
|
||||
mat.m[13] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[6] = -mat.m[0] * mat.m[6] * mat.m[15] +
|
||||
mat.m[0] * mat.m[7] * mat.m[14] +
|
||||
mat.m[4] * mat.m[2] * mat.m[15] -
|
||||
mat.m[4] * mat.m[3] * mat.m[14] -
|
||||
mat.m[12] * mat.m[2] * mat.m[7] +
|
||||
mat.m[12] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[10] = mat.m[0] * mat.m[5] * mat.m[15] -
|
||||
mat.m[0] * mat.m[7] * mat.m[13] -
|
||||
mat.m[4] * mat.m[1] * mat.m[15] +
|
||||
mat.m[4] * mat.m[3] * mat.m[13] +
|
||||
mat.m[12] * mat.m[1] * mat.m[7] -
|
||||
mat.m[12] * mat.m[3] * mat.m[5];
|
||||
|
||||
inv.m[14] = -mat.m[0] * mat.m[5] * mat.m[14] +
|
||||
mat.m[0] * mat.m[6] * mat.m[13] +
|
||||
mat.m[4] * mat.m[1] * mat.m[14] -
|
||||
mat.m[4] * mat.m[2] * mat.m[13] -
|
||||
mat.m[12] * mat.m[1] * mat.m[6] +
|
||||
mat.m[12] * mat.m[2] * mat.m[5];
|
||||
|
||||
inv.m[3] = -mat.m[1] * mat.m[6] * mat.m[11] +
|
||||
mat.m[1] * mat.m[7] * mat.m[10] +
|
||||
mat.m[5] * mat.m[2] * mat.m[11] -
|
||||
mat.m[5] * mat.m[3] * mat.m[10] -
|
||||
mat.m[9] * mat.m[2] * mat.m[7] +
|
||||
mat.m[9] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[7] = mat.m[0] * mat.m[6] * mat.m[11] -
|
||||
mat.m[0] * mat.m[7] * mat.m[10] -
|
||||
mat.m[4] * mat.m[2] * mat.m[11] +
|
||||
mat.m[4] * mat.m[3] * mat.m[10] +
|
||||
mat.m[8] * mat.m[2] * mat.m[7] -
|
||||
mat.m[8] * mat.m[3] * mat.m[6];
|
||||
|
||||
inv.m[11] = -mat.m[0] * mat.m[5] * mat.m[11] +
|
||||
mat.m[0] * mat.m[7] * mat.m[9] +
|
||||
mat.m[4] * mat.m[1] * mat.m[11] -
|
||||
mat.m[4] * mat.m[3] * mat.m[9] -
|
||||
mat.m[8] * mat.m[1] * mat.m[7] +
|
||||
mat.m[8] * mat.m[3] * mat.m[5];
|
||||
|
||||
inv.m[15] = mat.m[0] * mat.m[5] * mat.m[10] -
|
||||
mat.m[0] * mat.m[6] * mat.m[9] -
|
||||
mat.m[4] * mat.m[1] * mat.m[10] +
|
||||
mat.m[4] * mat.m[2] * mat.m[9] +
|
||||
mat.m[8] * mat.m[1] * mat.m[6] -
|
||||
mat.m[8] * mat.m[2] * mat.m[5];
|
||||
|
||||
// ��������� �����������
|
||||
det = mat.m[0] * inv.m[0] + mat.m[1] * inv.m[4] + mat.m[2] * inv.m[8] + mat.m[3] * inv.m[12];
|
||||
|
||||
if (std::fabs(det) < 0.01f)
|
||||
{
|
||||
throw std::runtime_error("Error: matrix cannot be inversed!");
|
||||
}
|
||||
|
||||
// ����� ��� �������� �� �����������
|
||||
det = 1.0f / det;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
inv.m[i] *= det;
|
||||
}
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
Matrix3f CreateZRotationMatrix(float angle)
|
||||
{
|
||||
Matrix3f result = Matrix3f::Identity();
|
||||
|
||||
result.m[0] = cosf(angle);
|
||||
result.m[1] = -sinf(angle);
|
||||
result.m[3] = sinf(angle);
|
||||
result.m[4] = cosf(angle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Matrix4f MultMatrixMatrix(const Matrix4f& m1, const Matrix4f& m2)
|
||||
{
|
||||
Matrix4f rx;
|
||||
|
||||
rx.m[0] = m1.m[0] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[8] * m2.m[2] + m1.m[12] * m2.m[3];
|
||||
rx.m[1] = m1.m[1] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[9] * m2.m[2] + m1.m[13] * m2.m[3];
|
||||
rx.m[2] = m1.m[2] * m2.m[0] + m1.m[6] * m2.m[1] + m1.m[10] * m2.m[2] + m1.m[14] * m2.m[3];
|
||||
rx.m[3] = m1.m[3] * m2.m[0] + m1.m[7] * m2.m[1] + m1.m[11] * m2.m[2] + m1.m[15] * m2.m[3];
|
||||
|
||||
rx.m[4] = m1.m[0] * m2.m[4] + m1.m[4] * m2.m[5] + m1.m[8] * m2.m[6] + m1.m[12] * m2.m[7];
|
||||
rx.m[5] = m1.m[1] * m2.m[4] + m1.m[5] * m2.m[5] + m1.m[9] * m2.m[6] + m1.m[13] * m2.m[7];
|
||||
rx.m[6] = m1.m[2] * m2.m[4] + m1.m[6] * m2.m[5] + m1.m[10] * m2.m[6] + m1.m[14] * m2.m[7];
|
||||
rx.m[7] = m1.m[3] * m2.m[4] + m1.m[7] * m2.m[5] + m1.m[11] * m2.m[6] + m1.m[15] * m2.m[7];
|
||||
|
||||
|
||||
rx.m[8] = m1.m[0] * m2.m[8] + m1.m[4] * m2.m[9] + m1.m[8] * m2.m[10] + m1.m[12] * m2.m[11];
|
||||
rx.m[9] = m1.m[1] * m2.m[8] + m1.m[5] * m2.m[9] + m1.m[9] * m2.m[10] + m1.m[13] * m2.m[11];
|
||||
rx.m[10] = m1.m[2] * m2.m[8] + m1.m[6] * m2.m[9] + m1.m[10] * m2.m[10] + m1.m[14] * m2.m[11];
|
||||
rx.m[11] = m1.m[3] * m2.m[8] + m1.m[7] * m2.m[9] + m1.m[11] * m2.m[10] + m1.m[15] * m2.m[11];
|
||||
|
||||
rx.m[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8] * m2.m[14] + m1.m[12] * m2.m[15];
|
||||
rx.m[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9] * m2.m[14] + m1.m[13] * m2.m[15];
|
||||
rx.m[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
|
||||
rx.m[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
|
||||
|
||||
return rx;
|
||||
}
|
||||
|
||||
Matrix3f MultMatrixMatrix(const Matrix3f& m1, const Matrix3f& m2)
|
||||
{
|
||||
Matrix3f r;
|
||||
|
||||
r.m[0] = m1.m[0] * m2.m[0] + m1.m[3] * m2.m[1] + m1.m[6] * m2.m[2];
|
||||
r.m[1] = m1.m[1] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[7] * m2.m[2];
|
||||
r.m[2] = m1.m[2] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[8] * m2.m[2];
|
||||
|
||||
r.m[3] = m1.m[0] * m2.m[3] + m1.m[3] * m2.m[4] + m1.m[6] * m2.m[5];
|
||||
r.m[4] = m1.m[1] * m2.m[3] + m1.m[4] * m2.m[4] + m1.m[7] * m2.m[5];
|
||||
r.m[5] = m1.m[2] * m2.m[3] + m1.m[5] * m2.m[4] + m1.m[8] * m2.m[5];
|
||||
|
||||
r.m[6] = m1.m[0] * m2.m[6] + m1.m[3] * m2.m[7] + m1.m[6] * m2.m[8] ;
|
||||
r.m[7] = m1.m[1] * m2.m[6] + m1.m[4] * m2.m[7] + m1.m[7] * m2.m[8];
|
||||
r.m[8] = m1.m[2] * m2.m[6] + m1.m[5] * m2.m[7] + m1.m[8] * m2.m[8];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f MakeTranslationMatrix(const Vector3f& p)
|
||||
{
|
||||
Matrix3f r = Matrix3f::Identity();
|
||||
|
||||
r.m[12] = p.v[0];
|
||||
r.m[13] = p.v[1];
|
||||
r.m[14] = p.v[2];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f MakeScaleMatrix(float scale)
|
||||
{
|
||||
Matrix3f r = Matrix3f::Identity();
|
||||
|
||||
r.m[0] = scale;
|
||||
r.m[5] = scale;
|
||||
r.m[10] = scale;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Matrix3f MakeRotationMatrix(const Vector3f& p)
|
||||
{
|
||||
Matrix3f r = Matrix3f::Identity();
|
||||
|
||||
r.m[12] = p.v[0];
|
||||
r.m[13] = p.v[1];
|
||||
r.m[14] = p.v[2];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
download from https://cmake.org/download/
|
||||
|
||||
|
||||
Windows x64 Installer: cmake-4.2.0-windows-x86_64.msi
|
||||
|
||||
|
||||
@@ -83,7 +84,7 @@ zlibstaticd.lib;libpng16_staticd.lib;SDL2d.lib;SDL2maind.lib;opengl32.lib;glu32.
|
||||
```
|
||||
C:\Work\Projects\emsdk\emsdk.bat activate latest
|
||||
C:\Work\Projects\emsdk\emsdk_env.bat
|
||||
emcc main.cpp Game.cpp Math.cpp Physics.cpp Renderer.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -sTOTAL_MEMORY=33554432 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS="[""png""]" -sUSE_SDL=2 --preload-file background.bmp --preload-file bird.bmp32 --preload-file default.fragment --preload-file default.vertex --preload-file game_over.bmp32 --preload-file pipe.bmp32 -o jumpingbird.html
|
||||
emcc main.cpp Game.cpp Math.cpp Physics.cpp Renderer.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -sTOTAL_MEMORY=33554432 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS="[""png""]" -sUSE_SDL=2 --preload-file background.bmp --preload-file bird.bmp32 --preload-file default.fragment --preload-file default.vertex --preload-file game_over.bmp32 --preload-file pipe.bmp32 -o space-game001.html
|
||||
```
|
||||
|
||||
```
|
||||
@@ -143,11 +144,22 @@ emcc main.cpp Game.cpp Environment.cpp GameObjectManager.cpp BoneAnimatedModel.c
|
||||
|
||||
|
||||
# Windows:
|
||||
emcc main.cpp Game.cpp Environment.cpp GameObjectManager.cpp BoneAnimatedModel.cpp GameWorld.cpp InputManager.cpp Inventory.cpp ObjLoader.cpp QuestScripts.cpp RenderSystem.cpp Math.cpp Physics.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sUSE_SDL=2 -sUSE_LIBPNG=1 --preload-file data.zip -o viola.html
|
||||
emcc --clear-cache
|
||||
embuilder build sdl2 sdl2_ttf sdl2_image sdl2_image_jpg sdl2_image_png
|
||||
|
||||
|
||||
|
||||
emcc main.cpp Game.cpp Environment.cpp BoneAnimatedModel.cpp ZLMath.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -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
|
||||
|
||||
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
|
||||
Code: MIT
|
||||
|
||||
|
||||
@@ -1,212 +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;
|
||||
|
||||
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, &infoLogLength, infoLog);
|
||||
|
||||
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,285 +0,0 @@
|
||||
#include "TextureManager.h"
|
||||
#ifdef PNG_ENABLED
|
||||
#include "png.h"
|
||||
#endif
|
||||
|
||||
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()
|
||||
{
|
||||
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
|
||||
|
||||
TextureDataStruct CreateTextureDataFromPng(const std::string& fullFileName)
|
||||
{
|
||||
TextureDataStruct texData;
|
||||
|
||||
FILE* file = fopen(fullFileName.c_str(), "rb");
|
||||
if (!file) {
|
||||
fclose(file);
|
||||
throw std::runtime_error("Could not open file " + fullFileName);
|
||||
}
|
||||
|
||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (!png) {
|
||||
fclose(file);
|
||||
throw std::runtime_error("Could not create PNG read structure");
|
||||
}
|
||||
|
||||
png_infop info = png_create_info_struct(png);
|
||||
if (!info) {
|
||||
fclose(file);
|
||||
png_destroy_read_struct(&png, nullptr, nullptr);
|
||||
throw std::runtime_error("Could not create PNG info structure");
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_read_struct(&png, &info, nullptr);
|
||||
fclose(file);
|
||||
throw std::runtime_error("Error during PNG read");
|
||||
}
|
||||
|
||||
png_init_io(png, file);
|
||||
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);
|
||||
|
||||
fclose(file);
|
||||
|
||||
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 = 0; y < texData.height; y++) { //Go in reverse because of UV coord start point
|
||||
for (int y = texData.height-1; y >= 0; y--) {
|
||||
//png_bytep row = row_pointers[y];//Go in reverse because of UV coord start point
|
||||
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[y]);//Go in reverse because of UV coord start point
|
||||
free(row_pointers[texData.height - 1 - y]);//Go in reverse because of UV coord start point
|
||||
}
|
||||
free(row_pointers);
|
||||
|
||||
png_destroy_read_struct(&png, &info, nullptr);
|
||||
|
||||
return texData;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
#include "Utils.h"
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
//#include <zip.h>
|
||||
|
||||
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) {
|
||||
/*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;*/
|
||||
return {};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -1,111 +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]);
|
||||
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 };
|
||||
};
|
||||
|
||||
struct Vector2f
|
||||
{
|
||||
std::array<float, 2> v = {0.f, 0.f};
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
||||
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 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
|
||||
import bpy
|
||||
|
||||
def append_layered_action_5_0(source_obj_name, target_obj_name):
|
||||
source_obj = bpy.data.objects.get(source_obj_name)
|
||||
target_obj = bpy.data.objects.get(target_obj_name)
|
||||
|
||||
if not (source_obj and target_obj):
|
||||
print("Ошибка: Объекты не найдены")
|
||||
return
|
||||
|
||||
src_action = source_obj.animation_data.action
|
||||
tgt_action = target_obj.animation_data.action
|
||||
|
||||
# 1. Получаем слои (обычно первый)
|
||||
src_layer = src_action.layers[0]
|
||||
tgt_layer = tgt_action.layers[0]
|
||||
|
||||
# 2. Получаем стрипы
|
||||
src_strip = src_layer.strips[0]
|
||||
tgt_strip = tgt_layer.strips[0]
|
||||
|
||||
# Смещение (опираемся на конец диапазона целевого экшена)
|
||||
offset = tgt_action.frame_range[1]
|
||||
|
||||
# 3. Итерируемся по channelbags в исходном стрипе
|
||||
for src_bag in src_strip.channelbags:
|
||||
# Ищем или создаем соответствующий bag в целевом стрипе
|
||||
# Обычно они сопоставляются по имени или типу (например, 'Keyframe Channel Bag')
|
||||
# В простейшем случае берем первый или сопоставляем по индексу
|
||||
dst_bag = None
|
||||
if len(tgt_strip.channelbags) > 0:
|
||||
# Пытаемся найти по названию (если оно есть) или берем тот же индекс
|
||||
dst_bag = tgt_strip.channelbags[0]
|
||||
|
||||
if not dst_bag:
|
||||
# Если в целевом стрипе нет сумок, это странно, но можно создать
|
||||
# (Метод создания может зависеть от конкретного подтипа стрипа в 5.0)
|
||||
continue
|
||||
|
||||
#print(f"Обработка channelbag: {src_bag.name}, кривых: {len(src_bag.fcurves)}")
|
||||
|
||||
# 4. Итерируемся по fcurves внутри сумки
|
||||
for src_fcurve in src_bag.fcurves:
|
||||
dst_fcurve = dst_bag.fcurves.find(src_fcurve.data_path, index=src_fcurve.array_index)
|
||||
|
||||
if not dst_fcurve:
|
||||
dst_fcurve = dst_bag.fcurves.new(data_path=src_fcurve.data_path, index=src_fcurve.array_index)
|
||||
|
||||
# 5. Копируем ключи с офсетом
|
||||
for keyframe in src_fcurve.keyframe_points:
|
||||
new_frame = keyframe.co[0] + offset
|
||||
new_value = keyframe.co[1]
|
||||
|
||||
new_kp = dst_fcurve.keyframe_points.insert(new_frame, new_value, options={'FAST'})
|
||||
new_kp.interpolation = keyframe.interpolation
|
||||
|
||||
# Обновляем интерполяцию
|
||||
for fc in dst_bag.fcurves:
|
||||
fc.update()
|
||||
|
||||
print(f"Анимация успешно дозаписана. Новый конец: {tgt_action.frame_range[1]}")
|
||||
|
||||
append_layered_action_5_0('Armature.001', 'Armature')
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
|
||||
# Имена mesh и арматуры
|
||||
mesh_name = "arm"
|
||||
armature_name = "Reference"
|
||||
|
||||
# Находим объект mesh по имени
|
||||
mesh_obj = bpy.data.objects.get(mesh_name)
|
||||
|
||||
# Находим объект арматуры по имени
|
||||
armature_obj = bpy.data.objects.get(armature_name)
|
||||
|
||||
# Устанавливаем текущий кадр на 0
|
||||
bpy.context.scene.frame_set(0)
|
||||
|
||||
# Принудительно обновляем сцену, чтобы применить анимацию
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
# Открываем файл для записи
|
||||
with open("C:\\Work\\Projects\\witcher001\\resources\\w\\zombie002.txt", "w") as file:
|
||||
# Обработка арматуры и анимации
|
||||
if armature_obj and armature_obj.type == 'ARMATURE':
|
||||
file.write("=== Armature Matrix ===\n")
|
||||
for row in armature_obj.matrix_world:
|
||||
file.write(f"{row}\n")
|
||||
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
|
||||
for bone in armature_obj.data.bones:
|
||||
# Записываем имя кости, длину и связи
|
||||
file.write(f"Bone: {bone.name}\n")
|
||||
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
|
||||
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
|
||||
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
|
||||
for row in bone.matrix:
|
||||
file.write(f" {row}\n")
|
||||
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
|
||||
file.write(f" Children: {[child.name for child in bone.children]}\n")
|
||||
|
||||
# Обработка mesh
|
||||
if mesh_obj and mesh_obj.type == 'MESH':
|
||||
# Создаем копию mesh, чтобы не изменять оригинал
|
||||
mesh_copy = mesh_obj.copy()
|
||||
mesh_copy.data = mesh_obj.data.copy()
|
||||
bpy.context.collection.objects.link(mesh_copy)
|
||||
|
||||
# Убедимся, что объект активен
|
||||
bpy.context.view_layer.objects.active = mesh_copy
|
||||
mesh_copy.select_set(True)
|
||||
|
||||
# Применяем модификатор Armature (если он есть)
|
||||
for modifier in mesh_copy.modifiers:
|
||||
if modifier.type == 'ARMATURE':
|
||||
# Включаем модификатор, если он отключен
|
||||
if not modifier.show_viewport:
|
||||
modifier.show_viewport = True
|
||||
if not modifier.show_render:
|
||||
modifier.show_render = True
|
||||
|
||||
# Проверяем, что модификатор связан с арматурой
|
||||
if modifier.object is None:
|
||||
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
|
||||
continue
|
||||
|
||||
# Временно применяем модификатор, чтобы получить правильные координаты вершин
|
||||
try:
|
||||
bpy.ops.object.modifier_apply(modifier=modifier.name)
|
||||
except RuntimeError as e:
|
||||
print(f"Ошибка при применении модификатора Armature: {e}")
|
||||
continue
|
||||
|
||||
# Переходим в режим редактирования
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
# Получаем BMesh представление mesh
|
||||
bm = bmesh.from_edit_mesh(mesh_copy.data)
|
||||
|
||||
# Записываем список вершин
|
||||
file.write(f"===Vertices: {len(bm.verts)}\n")
|
||||
for vertex in bm.verts:
|
||||
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
|
||||
|
||||
# Убедимся, что у меша есть UV слой
|
||||
uv_layer = bm.loops.layers.uv.active
|
||||
if not uv_layer:
|
||||
file.write("UV слой не найден.\n")
|
||||
|
||||
if uv_layer:
|
||||
file.write(f"===UV Coordinates:\n")
|
||||
file.write(f"Face count: {len(bm.faces)}\n")
|
||||
|
||||
for face in bm.faces:
|
||||
file.write(f"Face {face.index}\n")
|
||||
file.write(f"UV Count: {len(face.loops)}\n")
|
||||
for loop in face.loops:
|
||||
uv_coords = loop[uv_layer].uv
|
||||
file.write(f" UV {uv_coords}\n")
|
||||
|
||||
# Записываем нормали
|
||||
file.write(f"===Normals:\n")
|
||||
for vertex in bm.verts:
|
||||
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
|
||||
|
||||
# Записываем треугольники (индексы вершин)
|
||||
file.write(f"===Triangles: {len(bm.faces)}\n")
|
||||
for face in bm.faces:
|
||||
if len(face.verts) == 3: # Проверяем, что это треугольник
|
||||
verts_indices = [vert.index for vert in face.verts]
|
||||
file.write(f"Triangle: {verts_indices}\n")
|
||||
|
||||
# Возвращаемся в объектный режим
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# Записываем веса вершин
|
||||
file.write("=== Vertex Weights ===\n")
|
||||
for vertex in mesh_copy.data.vertices:
|
||||
file.write(f"Vertex {vertex.index}:\n")
|
||||
file.write(f"Vertex groups: {len(vertex.groups)}\n")
|
||||
for group in vertex.groups:
|
||||
group_name = mesh_copy.vertex_groups[group.group].name
|
||||
file.write(f" Group: '{group_name}', Weight: {group.weight}\n")
|
||||
|
||||
# Удаляем временную копию mesh
|
||||
bpy.data.objects.remove(mesh_copy)
|
||||
else:
|
||||
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
|
||||
|
||||
# Обработка арматуры и анимации
|
||||
if armature_obj and armature_obj.type == 'ARMATURE':
|
||||
|
||||
# Получаем все ключевые кадры для арматуры
|
||||
file.write("=== Animation Keyframes ===\n")
|
||||
if armature_obj.animation_data and armature_obj.animation_data.action:
|
||||
action = armature_obj.animation_data.action
|
||||
|
||||
# Собираем все уникальные ключевые кадры
|
||||
keyframes = set()
|
||||
|
||||
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
|
||||
if hasattr(action, "layers"):
|
||||
for layer in action.layers:
|
||||
if hasattr(layer, "strips"):
|
||||
for strip in layer.strips:
|
||||
# Проверяем наличие channelbags (согласно вашему dir(strip))
|
||||
if hasattr(strip, "channelbags"):
|
||||
for bag in strip.channelbags:
|
||||
for fcurve in bag.fcurves:
|
||||
for keyframe in fcurve.keyframe_points:
|
||||
keyframes.add(int(keyframe.co[0]))
|
||||
|
||||
# На случай, если в этой версии используется единственное число
|
||||
elif hasattr(strip, "channelbag") and strip.channelbag:
|
||||
for fcurve in strip.channelbag.fcurves:
|
||||
for keyframe in fcurve.keyframe_points:
|
||||
keyframes.add(int(keyframe.co[0]))
|
||||
|
||||
# Фоллбек для Legacy экшенов
|
||||
if not keyframes and hasattr(action, "fcurves"):
|
||||
for fcurve in action.fcurves:
|
||||
for keyframe in fcurve.keyframe_points:
|
||||
keyframes.add(int(keyframe.co[0]))
|
||||
|
||||
keyframes = sorted(keyframes)
|
||||
|
||||
# Сортируем ключевые кадры
|
||||
keyframes = sorted(keyframes)
|
||||
|
||||
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
|
||||
file.write("=== Bone Transforms per Keyframe ===\n")
|
||||
file.write(f"Keyframes: {len(keyframes)}\n")
|
||||
for frame in keyframes:
|
||||
# Устанавливаем текущий кадр
|
||||
bpy.context.scene.frame_set(frame)
|
||||
bpy.context.view_layer.update() # Обновляем сцену
|
||||
|
||||
file.write(f"Frame: {frame}\n")
|
||||
for bone in armature_obj.pose.bones:
|
||||
# Получаем координаты и матрицу поворота кости в мировом пространстве
|
||||
matrix = bone.matrix
|
||||
location = matrix.translation
|
||||
rotation = matrix.to_euler()
|
||||
|
||||
# Записываем данные
|
||||
file.write(f" Bone: {bone.name}\n")
|
||||
file.write(f" Location: {location}\n")
|
||||
file.write(f" Rotation: {rotation}\n")
|
||||
file.write(f" Matrix:\n")
|
||||
for row in matrix:
|
||||
file.write(f" {row}\n")
|
||||
else:
|
||||
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
|
||||
|
||||
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'")
|
||||
@@ -0,0 +1,228 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
|
||||
#!
|
||||
|
||||
# Имена mesh и арматуры
|
||||
mesh_name = "Joined"
|
||||
armature_name = "Armature"
|
||||
|
||||
# Находим объект mesh по имени
|
||||
mesh_obj = bpy.data.objects.get(mesh_name)
|
||||
|
||||
# Находим объект арматуры по имени
|
||||
armature_obj = bpy.data.objects.get(armature_name)
|
||||
|
||||
# Устанавливаем текущий кадр на 0
|
||||
bpy.context.scene.frame_set(0)
|
||||
|
||||
# Принудительно обновляем сцену, чтобы применить анимацию
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
# Открываем файл для записи
|
||||
with open("C:\\Work\\Media\\witcher\\2026-04-13\\output\\gg_stand_idle001.txt", "w") as file:
|
||||
# Обработка арматуры и анимации
|
||||
if armature_obj and armature_obj.type == 'ARMATURE':
|
||||
file.write("=== Armature Matrix ===\n")
|
||||
for row in armature_obj.matrix_world:
|
||||
file.write(f"{row}\n")
|
||||
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
|
||||
for bone in armature_obj.data.bones:
|
||||
# Записываем имя кости, длину и связи
|
||||
file.write(f"Bone: {bone.name}\n")
|
||||
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
|
||||
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
|
||||
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
|
||||
for row in bone.matrix:
|
||||
file.write(f" {row}\n")
|
||||
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
|
||||
file.write(f" Children: {[child.name for child in bone.children]}\n")
|
||||
|
||||
# Обработка mesh
|
||||
if mesh_obj and mesh_obj.type == 'MESH':
|
||||
# Создаем копию mesh, чтобы не изменять оригинал
|
||||
mesh_copy = mesh_obj.copy()
|
||||
mesh_copy.data = mesh_obj.data.copy()
|
||||
bpy.context.collection.objects.link(mesh_copy)
|
||||
|
||||
# Убедимся, что объект активен
|
||||
bpy.context.view_layer.objects.active = mesh_copy
|
||||
mesh_copy.select_set(True)
|
||||
|
||||
# Применяем модификатор Armature (если он есть)
|
||||
for modifier in mesh_copy.modifiers:
|
||||
if modifier.type == 'ARMATURE':
|
||||
# Включаем модификатор, если он отключен
|
||||
if not modifier.show_viewport:
|
||||
modifier.show_viewport = True
|
||||
if not modifier.show_render:
|
||||
modifier.show_render = True
|
||||
|
||||
# Проверяем, что модификатор связан с арматурой
|
||||
if modifier.object is None:
|
||||
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
|
||||
continue
|
||||
|
||||
# Временно применяем модификатор, чтобы получить правильные координаты вершин
|
||||
try:
|
||||
bpy.ops.object.modifier_apply(modifier=modifier.name)
|
||||
except RuntimeError as e:
|
||||
print(f"Ошибка при применении модификатора Armature: {e}")
|
||||
continue
|
||||
|
||||
# Переходим в режим редактирования
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
# Получаем BMesh представление mesh
|
||||
bm = bmesh.from_edit_mesh(mesh_copy.data)
|
||||
|
||||
# Записываем список вершин
|
||||
file.write(f"===Vertices: {len(bm.verts)}\n")
|
||||
for vertex in bm.verts:
|
||||
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
|
||||
|
||||
# Убедимся, что у меша есть UV слой
|
||||
uv_layer = bm.loops.layers.uv.active
|
||||
if not uv_layer:
|
||||
file.write("UV слой не найден.\n")
|
||||
|
||||
if uv_layer:
|
||||
file.write(f"===UV Coordinates:\n")
|
||||
file.write(f"Face count: {len(bm.faces)}\n")
|
||||
|
||||
for face in bm.faces:
|
||||
file.write(f"Face {face.index}\n")
|
||||
file.write(f"UV Count: {len(face.loops)}\n")
|
||||
for loop in face.loops:
|
||||
uv_coords = loop[uv_layer].uv
|
||||
file.write(f" UV {uv_coords}\n")
|
||||
|
||||
# Записываем нормали
|
||||
file.write(f"===Normals:\n")
|
||||
for vertex in bm.verts:
|
||||
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
|
||||
|
||||
# Записываем треугольники (индексы вершин)
|
||||
file.write(f"===Triangles: {len(bm.faces)}\n")
|
||||
for face in bm.faces:
|
||||
if len(face.verts) == 3: # Проверяем, что это треугольник
|
||||
verts_indices = [vert.index for vert in face.verts]
|
||||
file.write(f"Triangle: {verts_indices}\n")
|
||||
|
||||
# Возвращаемся в объектный режим
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
# Записываем веса вершин
|
||||
file.write("=== Vertex Weights (Max 5 bones per vertex) ===\n")
|
||||
|
||||
MAX_BONES = 5
|
||||
|
||||
for vertex in mesh_copy.data.vertices:
|
||||
# Извлекаем все группы и веса для текущей вершины
|
||||
all_weights = []
|
||||
for group_element in vertex.groups:
|
||||
all_weights.append({
|
||||
'index': group_element.group,
|
||||
'weight': group_element.weight
|
||||
})
|
||||
|
||||
# Если костей больше лимита, фильтруем и перераспределяем
|
||||
if len(all_weights) > MAX_BONES:
|
||||
# Сортируем по весу (от большего к меньшему)
|
||||
all_weights.sort(key=lambda x: x['weight'], reverse=True)
|
||||
|
||||
# Берем только топ-5
|
||||
kept_weights = all_weights[:MAX_BONES]
|
||||
|
||||
# Считаем сумму весов оставшихся костей для нормализации
|
||||
total_weight = sum(gw['weight'] for gw in kept_weights)
|
||||
|
||||
if total_weight > 0:
|
||||
for gw in kept_weights:
|
||||
gw['weight'] /= total_weight
|
||||
else:
|
||||
# На случай, если у всех веса были по 0.0 (редкий баг меша)
|
||||
kept_weights[0]['weight'] = 1.0
|
||||
|
||||
final_weights = kept_weights
|
||||
else:
|
||||
final_weights = all_weights
|
||||
|
||||
file.write(f"Vertex {vertex.index}:\n")
|
||||
file.write(f"Vertex groups: {len(final_weights)}\n")
|
||||
|
||||
for gw in final_weights:
|
||||
group_name = mesh_copy.vertex_groups[gw['index']].name
|
||||
file.write(f" Group: '{group_name}', Weight: {gw['weight']:.6f}\n")
|
||||
|
||||
# Удаляем временную копию mesh
|
||||
bpy.data.objects.remove(mesh_copy)
|
||||
else:
|
||||
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
|
||||
|
||||
# Обработка арматуры и анимации
|
||||
if armature_obj and armature_obj.type == 'ARMATURE':
|
||||
|
||||
# Получаем все ключевые кадры для арматуры
|
||||
file.write("=== Animation Keyframes ===\n")
|
||||
if armature_obj.animation_data and armature_obj.animation_data.action:
|
||||
action = armature_obj.animation_data.action
|
||||
|
||||
# Собираем все уникальные ключевые кадры
|
||||
keyframes = set()
|
||||
|
||||
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
|
||||
if hasattr(action, "layers"):
|
||||
for layer in action.layers:
|
||||
if hasattr(layer, "strips"):
|
||||
for strip in layer.strips:
|
||||
# Проверяем наличие channelbags (согласно вашему dir(strip))
|
||||
if hasattr(strip, "channelbags"):
|
||||
for bag in strip.channelbags:
|
||||
for fcurve in bag.fcurves:
|
||||
for keyframe in fcurve.keyframe_points:
|
||||
keyframes.add(int(keyframe.co[0]))
|
||||
|
||||
# На случай, если в этой версии используется единственное число
|
||||
elif hasattr(strip, "channelbag") and strip.channelbag:
|
||||
for fcurve in strip.channelbag.fcurves:
|
||||
for keyframe in fcurve.keyframe_points:
|
||||
keyframes.add(int(keyframe.co[0]))
|
||||
|
||||
# Фоллбек для Legacy экшенов
|
||||
if not keyframes and hasattr(action, "fcurves"):
|
||||
for fcurve in action.fcurves:
|
||||
for keyframe in fcurve.keyframe_points:
|
||||
keyframes.add(int(keyframe.co[0]))
|
||||
|
||||
keyframes = sorted(keyframes)
|
||||
|
||||
# Сортируем ключевые кадры
|
||||
keyframes = sorted(keyframes)
|
||||
|
||||
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
|
||||
file.write("=== Bone Transforms per Keyframe ===\n")
|
||||
file.write(f"Keyframes: {len(keyframes)}\n")
|
||||
for frame in keyframes:
|
||||
# Устанавливаем текущий кадр
|
||||
bpy.context.scene.frame_set(frame)
|
||||
bpy.context.view_layer.update() # Обновляем сцену
|
||||
|
||||
file.write(f"Frame: {frame}\n")
|
||||
for bone in armature_obj.pose.bones:
|
||||
# Получаем координаты и матрицу поворота кости в мировом пространстве
|
||||
matrix = bone.matrix
|
||||
location = matrix.translation
|
||||
rotation = matrix.to_euler()
|
||||
|
||||
# Записываем данные
|
||||
file.write(f" Bone: {bone.name}\n")
|
||||
file.write(f" Location: {location}\n")
|
||||
file.write(f" Rotation: {rotation}\n")
|
||||
file.write(f" Matrix:\n")
|
||||
for row in matrix:
|
||||
file.write(f" {row}\n")
|
||||
else:
|
||||
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
|
||||
|
||||
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'")
|
||||
@@ -0,0 +1,111 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
import mathutils
|
||||
import random
|
||||
import math
|
||||
|
||||
class SolidTreeGenerator:
|
||||
def __init__(self, levels=5, length=3.0, radius=0.3):
|
||||
self.levels = levels
|
||||
self.base_length = length
|
||||
self.base_radius = radius
|
||||
|
||||
# Хранилище для данных: (start_pos, end_pos, radius_start, radius_end)
|
||||
self.branches_data = []
|
||||
|
||||
def calculate_tree(self, start_pos, direction, length, radius, level):
|
||||
if level <= 0 or length < 0.1:
|
||||
return
|
||||
|
||||
end_pos = start_pos + direction * length
|
||||
# Сохраняем данные сегмента
|
||||
self.branches_data.append({
|
||||
'start': start_pos.copy(),
|
||||
'end': end_pos.copy(),
|
||||
'r_start': radius,
|
||||
'r_end': radius * 0.7
|
||||
})
|
||||
|
||||
# 1. Основной ствол (продолжение)
|
||||
trunk_dir = (direction + self.get_random_vector(0.1)).normalized()
|
||||
self.calculate_tree(end_pos, trunk_dir, length * 0.8, radius * 0.7, level - 1)
|
||||
|
||||
# 2. Боковые ветки (ветвление)
|
||||
if level > 1:
|
||||
num_sides = random.randint(2, 3) # Минимум 2 ветки для видимости
|
||||
for _ in range(num_sides):
|
||||
# Создаем вектор, сильно отклоненный от ствола (30-60 градусов)
|
||||
axis = self.get_random_vector(1.0).normalized()
|
||||
angle = math.radians(random.uniform(30, 60))
|
||||
|
||||
side_dir = direction.copy()
|
||||
side_dir.rotate(mathutils.Quaternion(axis, angle))
|
||||
|
||||
# Боковые ветки короче
|
||||
self.calculate_tree(end_pos, side_dir, length * 0.6, radius * 0.5, level - 1)
|
||||
|
||||
def get_random_vector(self, intensity):
|
||||
return mathutils.Vector((
|
||||
random.uniform(-intensity, intensity),
|
||||
random.uniform(-intensity, intensity),
|
||||
random.uniform(-intensity, intensity)
|
||||
))
|
||||
|
||||
def build_mesh(self):
|
||||
mesh = bpy.data.meshes.new("TreeMesh")
|
||||
obj = bpy.data.objects.new("Tree", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
|
||||
bm = bmesh.new()
|
||||
skin_layer = bm.verts.layers.skin.verify()
|
||||
|
||||
# Словарь для предотвращения дублирования вершин в одной точке
|
||||
# Ключ - кортеж координат, Значение - объект вершины BMesh
|
||||
vert_map = {}
|
||||
|
||||
for b in self.branches_data:
|
||||
# Превращаем координаты в кортежи для словаря
|
||||
s_key = tuple(round(v, 4) for v in b['start'])
|
||||
e_key = tuple(round(v, 4) for v in b['end'])
|
||||
|
||||
# Получаем или создаем начальную вершину
|
||||
if s_key not in vert_map:
|
||||
v_start = bm.verts.new(b['start'])
|
||||
v_start[skin_layer].radius = (b['r_start'], b['r_start'])
|
||||
vert_map[s_key] = v_start
|
||||
else:
|
||||
v_start = vert_map[s_key]
|
||||
|
||||
# Получаем или создаем конечную вершину
|
||||
if e_key not in vert_map:
|
||||
v_end = bm.verts.new(b['end'])
|
||||
v_end[skin_layer].radius = (b['r_end'], b['r_end'])
|
||||
vert_map[e_key] = v_end
|
||||
else:
|
||||
v_end = vert_map[e_key]
|
||||
|
||||
# Создаем ребро, если его еще нет
|
||||
if not bm.edges.get((v_start, v_end)):
|
||||
bm.edges.new((v_start, v_end))
|
||||
|
||||
# Находим корень (самую нижнюю точку) и помечаем его
|
||||
root_v = min(bm.verts, key=lambda v: v.co.z)
|
||||
root_v[skin_layer].use_root = True
|
||||
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
|
||||
# Модификаторы
|
||||
obj.modifiers.new(name="Skin", type='SKIN')
|
||||
sub = obj.modifiers.new(name="Subdiv", type='SUBSURF')
|
||||
sub.levels = 1 # Для начала 1, чтобы не тормозило
|
||||
|
||||
# Очистка сцены
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete()
|
||||
|
||||
# Запуск
|
||||
generator = SolidTreeGenerator(levels=5, length=3.0, radius=0.4)
|
||||
generator.calculate_tree(mathutils.Vector((0,0,0)), mathutils.Vector((0,0,1)), 3.0, 0.4, 5)
|
||||
generator.build_mesh()
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
|
||||
mesh_name = "chassis_low"
|
||||
output_path = "C:\\Work\\Projects\\space-game001\\blender scripts\\output\\spaceship005.txt"
|
||||
|
||||
mesh_obj = bpy.data.objects.get(mesh_name)
|
||||
|
||||
if mesh_obj and mesh_obj.type == 'MESH':
|
||||
# Обязательно применяем трансформации, чтобы координаты были актуальными
|
||||
# Но для чистоты эксперимента берем mesh data как есть
|
||||
|
||||
# Работаем с копией меша, чтобы применить триангуляцию (если нужно)
|
||||
# и не испортить сцену, но пока используем bmesh напрямую
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh_obj.data)
|
||||
|
||||
# Получаем слой UV
|
||||
uv_layer = bm.loops.layers.uv.active
|
||||
|
||||
# Словарь для хранения уникальных вершин:
|
||||
# Ключ: (x, y, z, nx, ny, nz, u, v) -> Значение: новый_индекс
|
||||
unique_verts_map = {}
|
||||
final_vertices = [] # Список для записи в файл
|
||||
final_indices = [] # Список индексов треугольников
|
||||
|
||||
# Проходим по всем фейсам
|
||||
for face in bm.faces:
|
||||
face_indices = []
|
||||
|
||||
# Проходим по углам (loops) фейса
|
||||
for loop in face.loops:
|
||||
vert = loop.vert
|
||||
|
||||
# 1. Координаты (округляем для надежности сравнения float)
|
||||
co = (round(vert.co.x, 6), round(vert.co.y, 6), round(vert.co.z, 6))
|
||||
|
||||
# 2. Нормаль (если используете Smooth shading, берите vert.normal, если Flat - face.normal)
|
||||
# Для простоты берем нормаль вершины
|
||||
no = (round(vert.normal.x, 6), round(vert.normal.y, 6), round(vert.normal.z, 6))
|
||||
|
||||
# 3. UV координаты
|
||||
if uv_layer:
|
||||
raw_uv = loop[uv_layer].uv
|
||||
uv = (round(raw_uv.x, 6), round(raw_uv.y, 6))
|
||||
else:
|
||||
uv = (0.0, 0.0)
|
||||
|
||||
# Собираем уникальный ключ данных вершины
|
||||
vert_data_key = (co, no, uv)
|
||||
|
||||
# Проверяем, есть ли такая комбинация уже
|
||||
if vert_data_key in unique_verts_map:
|
||||
index = unique_verts_map[vert_data_key]
|
||||
else:
|
||||
index = len(final_vertices)
|
||||
unique_verts_map[vert_data_key] = index
|
||||
final_vertices.append(vert_data_key)
|
||||
|
||||
face_indices.append(index)
|
||||
|
||||
# Триангуляция "на лету" (если фейс - квадрат, делим на два треугольника)
|
||||
# Простейший метод fan (0, 1, 2), (0, 2, 3)...
|
||||
for i in range(1, len(face_indices) - 1):
|
||||
final_indices.append(face_indices[0])
|
||||
final_indices.append(face_indices[i])
|
||||
final_indices.append(face_indices[i+1])
|
||||
|
||||
# --- ЗАПИСЬ В ФАЙЛ ---
|
||||
with open(output_path, "w") as file:
|
||||
file.write(f"===Vertices (Split by UV/Normal): {len(final_vertices)}\n")
|
||||
# Формат строки: ID: X Y Z | NX NY NZ | U V
|
||||
for idx, v_data in enumerate(final_vertices):
|
||||
co, no, uv = v_data
|
||||
file.write(f"V {idx}: Pos({co[0]}, {co[1]}, {co[2]}) Norm({no[0]}, {no[1]}, {no[2]}) UV({uv[0]}, {uv[1]})\n")
|
||||
|
||||
file.write(f"\n===Triangles (Indices): {len(final_indices) // 3}\n")
|
||||
# Записываем тройками
|
||||
for i in range(0, len(final_indices), 3):
|
||||
file.write(f"Tri: {final_indices[i]} {final_indices[i+1]} {final_indices[i+2]}\n")
|
||||
|
||||
bm.free()
|
||||
print(f"Export done. Original verts: {len(mesh_obj.data.vertices)}, Split verts: {len(final_vertices)}")
|
||||
|
||||
else:
|
||||
print("Mesh not found")
|
||||
@@ -0,0 +1,58 @@
|
||||
# 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/zlib132.zip" "zlib132.zip" "zlib-1.3.2" "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")
|
||||
|
||||
# 9) Lua
|
||||
check_and_download("https://github.com/lua/lua/archive/refs/tags/v5.4.8.zip" "lua-v5.4.8.zip" "lua-5.4.8" "lapi.c")
|
||||
|
||||
# 10) sol2 (header-only C++ bindings for Lua)
|
||||
check_and_download("https://github.com/ThePhD/sol2/archive/refs/tags/v3.3.0.zip" "sol2-v3.3.0.zip" "sol2-3.3.0" "include/sol/sol.hpp")
|
||||
|
||||
# 11) SDL2_mixer
|
||||
check_and_download("https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.8.0.zip" "SDL_mixer-release-2.8.0.zip" "SDL_mixer-release-2.8.0" "CMakeLists.txt")
|
||||
@@ -0,0 +1,728 @@
|
||||
# cmake/ThirdParty.cmake
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/FetchDependencies.cmake")
|
||||
|
||||
macro(log msg)
|
||||
message(STATUS "[ThirdParty] ${msg}")
|
||||
endmacro()
|
||||
|
||||
set(BUILD_CONFIGS Debug Release)
|
||||
|
||||
# Map MinSizeRel and RelWithDebInfo to Release libs for all imported targets.
|
||||
# Without this CMake warns that IMPORTED_LOCATION is missing for those configs.
|
||||
set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL Release)
|
||||
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release)
|
||||
|
||||
|
||||
# ===========================================
|
||||
# 1) ZLIB (zlib131.zip → zlib-1.3.2) - без изменений
|
||||
# ===========================================
|
||||
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.2")
|
||||
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/zsd.lib"
|
||||
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zs.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}"
|
||||
)
|
||||
target_include_directories(freetype_external_lib INTERFACE
|
||||
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include/freetype2,${FREETYPE_BASE_DIR}-Release/include/freetype2>"
|
||||
"$<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/freetype2
|
||||
-DFREETYPE_INCLUDE_DIRS=${_FT_PREFIX}/include/freetype2
|
||||
-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()
|
||||
|
||||
# ===========================================
|
||||
# 9) Lua (5.5.0) - embedded scripting language
|
||||
# ===========================================
|
||||
set(LUA_SRC_DIR "${THIRDPARTY_DIR}/lua-5.4.8")
|
||||
|
||||
if(NOT TARGET lua_static)
|
||||
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
|
||||
# Exclude the standalone interpreter, compiler, and unity-build wrapper.
|
||||
# onelua.c #includes all other .c files — compiling it alongside them
|
||||
# causes every symbol to be defined twice.
|
||||
list(REMOVE_ITEM LUA_SOURCES
|
||||
"${LUA_SRC_DIR}/lua.c"
|
||||
"${LUA_SRC_DIR}/luac.c"
|
||||
"${LUA_SRC_DIR}/onelua.c"
|
||||
)
|
||||
|
||||
add_library(lua_static STATIC ${LUA_SOURCES})
|
||||
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
|
||||
target_compile_definitions(lua_static PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# 10) sol2 (3.3.0) - header-only C++ bindings for Lua
|
||||
# ===========================================
|
||||
set(SOL2_SRC_DIR "${THIRDPARTY_DIR}/sol2-3.3.0")
|
||||
|
||||
# Apply patch for Clang/Emscripten compatibility in optional<T&>::emplace().
|
||||
# The sentinel file prevents re-applying on subsequent cmake runs.
|
||||
set(_sol2_sentinel "${SOL2_SRC_DIR}/.patched")
|
||||
if(NOT EXISTS "${_sol2_sentinel}")
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} apply --ignore-whitespace
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/sol2-3.3.0-clang-optional.patch"
|
||||
WORKING_DIRECTORY "${SOL2_SRC_DIR}"
|
||||
RESULT_VARIABLE _sol2_patch_res
|
||||
)
|
||||
if(_sol2_patch_res EQUAL 0)
|
||||
file(WRITE "${_sol2_sentinel}" "patched\n")
|
||||
message(STATUS "Applied sol2 Clang optional patch")
|
||||
else()
|
||||
message(WARNING "sol2 patch failed (exit ${_sol2_patch_res}) — Clang/Emscripten builds may not compile")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Git not found — cannot apply sol2 patch automatically. "
|
||||
"Apply cmake/patches/sol2-3.3.0-clang-optional.patch manually.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET sol2_external_lib)
|
||||
add_library(sol2_external_lib INTERFACE)
|
||||
target_include_directories(sol2_external_lib INTERFACE "${SOL2_SRC_DIR}/include")
|
||||
target_link_libraries(sol2_external_lib INTERFACE lua_static)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# 11) SDL2_mixer (2.8.0) – сборка из исходников
|
||||
# ===========================================
|
||||
set(SDL2MIXER_SRC_DIR "${THIRDPARTY_DIR}/SDL_mixer-release-2.8.0")
|
||||
set(SDL2MIXER_BASE_DIR "${SDL2MIXER_SRC_DIR}/install")
|
||||
set(SDL2MIXER_BASE_DIR "${SDL2MIXER_BASE_DIR}" CACHE PATH "SDL2_mixer install base directory" FORCE)
|
||||
|
||||
set(_have_sdl2mixer TRUE)
|
||||
foreach(cfg IN LISTS BUILD_CONFIGS)
|
||||
if(NOT EXISTS "${SDL2MIXER_BASE_DIR}-${cfg}/lib/SDL2_mixer.lib" AND
|
||||
NOT EXISTS "${SDL2MIXER_BASE_DIR}-${cfg}/lib/SDL2_mixerd.lib")
|
||||
set(_have_sdl2mixer FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _have_sdl2mixer)
|
||||
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()
|
||||
|
||||
log("Configuring SDL2_mixer (${cfg}) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-G "${CMAKE_GENERATOR}"
|
||||
-S "${SDL2MIXER_SRC_DIR}"
|
||||
-B "${SDL2MIXER_SRC_DIR}/build-${cfg}"
|
||||
-DCMAKE_INSTALL_PREFIX=${SDL2MIXER_BASE_DIR}-${cfg}
|
||||
-DCMAKE_PREFIX_PATH=${SDL2_INSTALL_DIR}
|
||||
-DSDL2_LIBRARY=${_SDL2_LIB}
|
||||
-DSDL2_INCLUDE_DIR=${SDL2_INSTALL_DIR}/include/SDL2
|
||||
-DSDL2MIXER_DEPS_SHARED=OFF
|
||||
-DSDL2MIXER_VENDORED=ON
|
||||
-DSDL2MIXER_SAMPLES=OFF
|
||||
-DSDL2MIXER_MUSIC_CMD=OFF
|
||||
-DSDL2MIXER_MOD=OFF
|
||||
-DSDL2MIXER_MIDI=OFF
|
||||
-DSDL2MIXER_OPUS=OFF
|
||||
-DSDL2MIXER_WAVPACK=OFF
|
||||
-DSDL2MIXER_MP3_MPG123=OFF
|
||||
-DSDL2MIXER_MP3_DRMP3=ON
|
||||
-DSDL2MIXER_FLAC_DRFLAC=ON
|
||||
-DSDL2MIXER_OGG_STB=ON
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_OGG=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_Vorbis=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_FLAC=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_MPG123=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_LibModPlug=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_FluidLite=TRUE
|
||||
RESULT_VARIABLE _mixer_cfg_res
|
||||
OUTPUT_VARIABLE _mixer_cfg_out
|
||||
ERROR_VARIABLE _mixer_cfg_err
|
||||
)
|
||||
if(NOT _mixer_cfg_res EQUAL 0)
|
||||
message(STATUS "SDL2_mixer configure stdout: ${_mixer_cfg_out}")
|
||||
message(STATUS "SDL2_mixer configure stderr: ${_mixer_cfg_err}")
|
||||
message(FATAL_ERROR "SDL2_mixer configure failed for ${cfg}")
|
||||
endif()
|
||||
|
||||
log("Building SDL2_mixer (${cfg}) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--build "${SDL2MIXER_SRC_DIR}/build-${cfg}" --config ${cfg}
|
||||
RESULT_VARIABLE _mixer_build_res
|
||||
)
|
||||
if(NOT _mixer_build_res EQUAL 0)
|
||||
message(FATAL_ERROR "SDL2_mixer build failed for ${cfg}")
|
||||
endif()
|
||||
|
||||
log("Installing SDL2_mixer (${cfg}) ...")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
--install "${SDL2MIXER_SRC_DIR}/build-${cfg}" --config ${cfg}
|
||||
RESULT_VARIABLE _mixer_inst_res
|
||||
)
|
||||
if(NOT _mixer_inst_res EQUAL 0)
|
||||
message(FATAL_ERROR "SDL2_mixer install failed for ${cfg}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(_mixer_debug_lib "")
|
||||
foreach(cand
|
||||
"${SDL2MIXER_BASE_DIR}-Debug/lib/SDL2_mixerd.lib"
|
||||
"${SDL2MIXER_BASE_DIR}-Debug/lib/SDL2_mixer.lib"
|
||||
)
|
||||
if(EXISTS "${cand}")
|
||||
set(_mixer_debug_lib "${cand}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(_mixer_release_lib "")
|
||||
foreach(cand
|
||||
"${SDL2MIXER_BASE_DIR}-Release/lib/SDL2_mixer.lib"
|
||||
)
|
||||
if(EXISTS "${cand}")
|
||||
set(_mixer_release_lib "${cand}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(_mixer_debug_lib STREQUAL "" OR _mixer_release_lib STREQUAL "")
|
||||
message(FATAL_ERROR "SDL2_mixer libs not found in ${SDL2MIXER_BASE_DIR}-Debug/Release")
|
||||
endif()
|
||||
|
||||
add_library(SDL2_mixer_external_lib UNKNOWN IMPORTED GLOBAL)
|
||||
set_target_properties(SDL2_mixer_external_lib PROPERTIES
|
||||
IMPORTED_LOCATION_DEBUG "${_mixer_debug_lib}"
|
||||
IMPORTED_LOCATION_RELEASE "${_mixer_release_lib}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
"$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/include,${SDL2MIXER_BASE_DIR}-Release/include>"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"SDL2_external_lib"
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
--- a/include/sol/optional_implementation.hpp
|
||||
+++ b/include/sol/optional_implementation.hpp
|
||||
@@ -2189,7 +2189,10 @@
|
||||
template <class... Args>
|
||||
T& emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
|
||||
|
||||
*this = nullopt;
|
||||
- this->construct(std::forward<Args>(args)...);
|
||||
+ // Reference specialization stores a pointer; set it directly.
|
||||
+ // construct() only exists in the non-reference specialization.
|
||||
+ m_value = std::addressof(std::forward<Args>(args)...);
|
||||
+ return *m_value;
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert a text-based bone animation file to the BSAF binary format.
|
||||
|
||||
Usage:
|
||||
python convert_anim_to_binary.py <input.txt> <output.bin>
|
||||
|
||||
Binary format (BSAF v1) -- all values little-endian:
|
||||
|
||||
HEADER
|
||||
4 bytes magic "BSAF"
|
||||
uint32 version (1)
|
||||
|
||||
BONES
|
||||
uint32 numBones
|
||||
per bone:
|
||||
3 x float boneStartWorld (from HEAD_LOCAL)
|
||||
float boneLength
|
||||
9 x float 3x3 rotation matrix (row-major)
|
||||
int32 parentIndex (-1 if none)
|
||||
uint32 numChildren
|
||||
numChildren x int32 childIndices
|
||||
|
||||
VERTICES
|
||||
uint32 numVertices
|
||||
numVertices x 3 x float positions
|
||||
|
||||
UV COORDINATES
|
||||
uint32 numFaces
|
||||
numFaces x 6 x float 3 UV pairs per face (u0,v0,u1,v1,u2,v2)
|
||||
|
||||
NORMALS
|
||||
numVertices x 3 x float normals
|
||||
|
||||
TRIANGLES
|
||||
uint32 numTriangles
|
||||
numTriangles x 3 x int32 vertex indices
|
||||
|
||||
VERTEX WEIGHTS
|
||||
per vertex (numVertices):
|
||||
uint32 numGroups
|
||||
numGroups x (int32 boneIndex, float weight)
|
||||
|
||||
ANIMATION KEYFRAMES
|
||||
uint32 numKeyframes
|
||||
per keyframe:
|
||||
int32 frameNumber
|
||||
per bone (numBones, in index order 0..N-1):
|
||||
3 x float location
|
||||
16 x float 4x4 matrix (row-major)
|
||||
"""
|
||||
|
||||
import struct
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def parse_floats(line):
|
||||
return [float(x) for x in re.findall(r'[-]?\d+\.\d+', line)]
|
||||
|
||||
|
||||
def parse_first_int(line):
|
||||
m = re.search(r'\d+', line)
|
||||
if m:
|
||||
return int(m.group())
|
||||
raise ValueError(f"No integer found in: {line}")
|
||||
|
||||
|
||||
def parse_children(line):
|
||||
return re.findall(r"'([^']+)'", line)
|
||||
|
||||
|
||||
def convert(input_path, output_path):
|
||||
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
idx = 0
|
||||
|
||||
def next_line():
|
||||
nonlocal idx
|
||||
line = lines[idx].rstrip()
|
||||
idx += 1
|
||||
return line
|
||||
|
||||
# --- Skip armature matrix (5 lines) ---
|
||||
for _ in range(5):
|
||||
next_line()
|
||||
|
||||
# --- Bone count ---
|
||||
line = next_line() # "=== Armature Bones: 65"
|
||||
num_bones = parse_first_int(line)
|
||||
|
||||
bone_names = []
|
||||
bones = []
|
||||
bone_parent_names = []
|
||||
bone_children_names = []
|
||||
|
||||
for _ in range(num_bones):
|
||||
bone = {}
|
||||
|
||||
# "Bone: mixamorig:Hips"
|
||||
line = next_line()
|
||||
bone_name = line[6:]
|
||||
bone_names.append(bone_name)
|
||||
|
||||
# " HEAD_LOCAL: <Vector (x, y, z)>"
|
||||
line = next_line()
|
||||
bone['head'] = parse_floats(line)[:3]
|
||||
|
||||
# " TAIL_LOCAL: ..." -- skip
|
||||
next_line()
|
||||
|
||||
# " Length: 0.123"
|
||||
line = next_line()
|
||||
bone['length'] = parse_floats(line)[0]
|
||||
|
||||
# 3x3 matrix (3 rows)
|
||||
mat = []
|
||||
for _ in range(3):
|
||||
mat.extend(parse_floats(next_line()))
|
||||
bone['matrix_3x3'] = mat
|
||||
|
||||
# " Parent: None" or " Parent: boneName"
|
||||
line = next_line()
|
||||
if line == " Parent: None":
|
||||
bone_parent_names.append(None)
|
||||
else:
|
||||
bone_parent_names.append(line[10:])
|
||||
|
||||
# " Children: ['a', 'b'] or []"
|
||||
line = next_line()
|
||||
bone_children_names.append(parse_children(line))
|
||||
|
||||
bones.append(bone)
|
||||
|
||||
# Build name -> index map
|
||||
name_to_idx = {name: i for i, name in enumerate(bone_names)}
|
||||
|
||||
# Resolve parent / child indices
|
||||
for i in range(num_bones):
|
||||
if bone_parent_names[i] is None:
|
||||
bones[i]['parent'] = -1
|
||||
else:
|
||||
bones[i]['parent'] = name_to_idx[bone_parent_names[i]]
|
||||
bones[i]['children'] = [name_to_idx[c] for c in bone_children_names[i]]
|
||||
|
||||
# --- Vertices ---
|
||||
line = next_line() # "===Vertices: 5140"
|
||||
num_vertices = parse_first_int(line)
|
||||
|
||||
vertices = []
|
||||
for _ in range(num_vertices):
|
||||
vertices.append(parse_floats(next_line())[:3])
|
||||
|
||||
# --- UV Coordinates ---
|
||||
next_line() # "===UV Coordinates:"
|
||||
line = next_line() # "Face count: 8602"
|
||||
num_faces = parse_first_int(line)
|
||||
|
||||
uvs = []
|
||||
for _ in range(num_faces):
|
||||
next_line() # "Face N"
|
||||
next_line() # "UV Count: 3"
|
||||
face_uvs = []
|
||||
for _ in range(3):
|
||||
face_uvs.extend(parse_floats(next_line())[:2])
|
||||
uvs.append(face_uvs) # 6 floats
|
||||
|
||||
# --- Normals ---
|
||||
next_line() # "===Normals:"
|
||||
|
||||
normals = []
|
||||
for _ in range(num_vertices):
|
||||
normals.append(parse_floats(next_line())[:3])
|
||||
|
||||
# --- Triangles ---
|
||||
line = next_line() # "===Triangles: 8602"
|
||||
num_triangles = parse_first_int(line)
|
||||
|
||||
triangles = []
|
||||
for _ in range(num_triangles):
|
||||
line = next_line()
|
||||
ints = [int(x) for x in re.findall(r'[-]?\d+', line)]
|
||||
triangles.append(ints[:3])
|
||||
|
||||
# --- Vertex Weights ---
|
||||
next_line() # "=== Vertex Weights ..."
|
||||
|
||||
vertex_weights = []
|
||||
for _ in range(num_vertices):
|
||||
next_line() # "Vertex N:"
|
||||
line = next_line() # "Vertex groups: 2"
|
||||
num_groups = parse_first_int(line)
|
||||
|
||||
groups = []
|
||||
for _ in range(num_groups):
|
||||
line = next_line()
|
||||
m = re.search(r"'([^']+)'.*?([-]?\d+\.\d+)", line)
|
||||
bone_name = m.group(1)
|
||||
weight = float(m.group(2))
|
||||
groups.append((name_to_idx[bone_name], weight))
|
||||
|
||||
vertex_weights.append(groups)
|
||||
|
||||
# --- Animation Keyframes ---
|
||||
next_line() # "=== Animation Keyframes ==="
|
||||
next_line() # "=== Bone Transforms per Keyframe ==="
|
||||
line = next_line() # "Keyframes: 32"
|
||||
num_keyframes = parse_first_int(line)
|
||||
|
||||
keyframes = []
|
||||
for _ in range(num_keyframes):
|
||||
line = next_line() # "Frame: 0"
|
||||
frame_number = parse_first_int(line)
|
||||
|
||||
bone_data = {}
|
||||
for _ in range(num_bones):
|
||||
line = next_line() # " Bone: mixamorig:Hips"
|
||||
bone_name = line.strip()
|
||||
if bone_name.startswith("Bone: "):
|
||||
bone_name = bone_name[6:]
|
||||
bone_idx = name_to_idx[bone_name]
|
||||
|
||||
# Location
|
||||
location = parse_floats(next_line())[:3]
|
||||
|
||||
# Rotation (skip)
|
||||
next_line()
|
||||
|
||||
# " Matrix:" (skip header)
|
||||
next_line()
|
||||
|
||||
# 4 rows of 4 floats
|
||||
matrix = []
|
||||
for _ in range(4):
|
||||
matrix.extend(parse_floats(next_line()))
|
||||
|
||||
bone_data[bone_idx] = {
|
||||
'location': location,
|
||||
'matrix': matrix,
|
||||
}
|
||||
|
||||
keyframes.append((frame_number, bone_data))
|
||||
|
||||
# ================================================================
|
||||
# Write binary file
|
||||
# ================================================================
|
||||
with open(output_path, 'wb') as out:
|
||||
# Header
|
||||
out.write(b'BSAF')
|
||||
out.write(struct.pack('<I', 1))
|
||||
|
||||
# Bones
|
||||
out.write(struct.pack('<I', num_bones))
|
||||
for i in range(num_bones):
|
||||
b = bones[i]
|
||||
out.write(struct.pack('<3f', *b['head']))
|
||||
out.write(struct.pack('<f', b['length']))
|
||||
out.write(struct.pack('<9f', *b['matrix_3x3']))
|
||||
out.write(struct.pack('<i', b['parent']))
|
||||
out.write(struct.pack('<I', len(b['children'])))
|
||||
for c in b['children']:
|
||||
out.write(struct.pack('<i', c))
|
||||
|
||||
# Vertices
|
||||
out.write(struct.pack('<I', num_vertices))
|
||||
for v in vertices:
|
||||
out.write(struct.pack('<3f', *v))
|
||||
|
||||
# UV Coordinates
|
||||
out.write(struct.pack('<I', num_faces))
|
||||
for uv in uvs:
|
||||
out.write(struct.pack('<6f', *uv))
|
||||
|
||||
# Normals
|
||||
for n in normals:
|
||||
out.write(struct.pack('<3f', *n))
|
||||
|
||||
# Triangles
|
||||
out.write(struct.pack('<I', num_triangles))
|
||||
for t in triangles:
|
||||
out.write(struct.pack('<3i', *t))
|
||||
|
||||
# Vertex Weights
|
||||
for vw in vertex_weights:
|
||||
out.write(struct.pack('<I', len(vw)))
|
||||
for bone_idx, weight in vw:
|
||||
out.write(struct.pack('<if', bone_idx, weight))
|
||||
|
||||
# Animation Keyframes
|
||||
out.write(struct.pack('<I', num_keyframes))
|
||||
for frame_num, bone_data in keyframes:
|
||||
out.write(struct.pack('<i', frame_num))
|
||||
for i in range(num_bones):
|
||||
bd = bone_data[i]
|
||||
out.write(struct.pack('<3f', *bd['location']))
|
||||
out.write(struct.pack('<16f', *bd['matrix']))
|
||||
|
||||
input_size = sum(len(l) for l in lines)
|
||||
import os
|
||||
output_size = os.path.getsize(output_path)
|
||||
print(f"Converted: {input_path} ({input_size:,} bytes text) -> {output_path} ({output_size:,} bytes binary)")
|
||||
print(f" Bones: {num_bones}, Vertices: {num_vertices}, Faces: {num_faces}, "
|
||||
f"Triangles: {num_triangles}, Keyframes: {num_keyframes}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
print(f"Usage: {sys.argv[0]} <input.txt> <output.bin>")
|
||||
sys.exit(1)
|
||||
|
||||
convert(sys.argv[1], sys.argv[2])
|
||||
@@ -0,0 +1,359 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert a text-based multi-mesh bone animation file to the BSMF binary format.
|
||||
|
||||
Usage:
|
||||
python convert_anim_to_binary_new.py <input.txt> <output.bin>
|
||||
|
||||
Binary format (BSMF v1) -- all values little-endian:
|
||||
|
||||
HEADER
|
||||
4 bytes magic "BSMF"
|
||||
uint32 version (1)
|
||||
|
||||
ARMATURE MATRIX
|
||||
16 x float 4x4 matrix (row-major)
|
||||
|
||||
BONES
|
||||
uint32 numBones
|
||||
per bone:
|
||||
3 x float boneStartWorld (from HEAD_LOCAL)
|
||||
float boneLength
|
||||
9 x float 3x3 rotation matrix (row-major)
|
||||
int32 parentIndex (-1 if none)
|
||||
uint32 numChildren
|
||||
numChildren x int32 childIndices
|
||||
|
||||
MESHES
|
||||
uint32 numMeshes
|
||||
per mesh:
|
||||
uint32 nameLength
|
||||
nameLength x char meshName (UTF-8, no null terminator)
|
||||
|
||||
VERTICES
|
||||
uint32 numVertices
|
||||
numVertices x 3 x float positions
|
||||
|
||||
UV COORDINATES
|
||||
uint32 numFaces
|
||||
numFaces x 6 x float 3 UV pairs per face (u0,v0,u1,v1,u2,v2)
|
||||
|
||||
NORMALS
|
||||
numVertices x 3 x float normals
|
||||
|
||||
TRIANGLES
|
||||
uint32 numTriangles
|
||||
numTriangles x 3 x int32 vertex indices
|
||||
|
||||
VERTEX WEIGHTS
|
||||
per vertex (numVertices):
|
||||
uint32 numGroups
|
||||
numGroups x (int32 boneIndex, float weight)
|
||||
|
||||
ANIMATION KEYFRAMES
|
||||
uint32 numKeyframes
|
||||
per keyframe:
|
||||
int32 frameNumber
|
||||
per bone (numBones, in index order 0..N-1):
|
||||
3 x float location
|
||||
16 x float 4x4 matrix (row-major)
|
||||
"""
|
||||
|
||||
import struct
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def parse_floats(line):
|
||||
return [float(x) for x in re.findall(r'[-]?\d+\.\d+', line)]
|
||||
|
||||
|
||||
def parse_first_int(line):
|
||||
m = re.search(r'\d+', line)
|
||||
if m:
|
||||
return int(m.group())
|
||||
raise ValueError(f"No integer found in: {line}")
|
||||
|
||||
|
||||
def parse_children(line):
|
||||
return re.findall(r"'([^']+)'", line)
|
||||
|
||||
|
||||
def convert(input_path, output_path):
|
||||
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
idx = 0
|
||||
|
||||
def next_line():
|
||||
nonlocal idx
|
||||
line = lines[idx].rstrip()
|
||||
idx += 1
|
||||
return line
|
||||
|
||||
# --- Armature matrix header + 4 rows ---
|
||||
next_line() # "=== Armature Matrix ==="
|
||||
armature_matrix = []
|
||||
for _ in range(4):
|
||||
armature_matrix.extend(parse_floats(next_line())[:4])
|
||||
|
||||
# --- Bone count ---
|
||||
line = next_line() # "=== Armature Bones: 65"
|
||||
num_bones = parse_first_int(line)
|
||||
|
||||
bone_names = []
|
||||
bones = []
|
||||
bone_parent_names = []
|
||||
bone_children_names = []
|
||||
|
||||
for _ in range(num_bones):
|
||||
bone = {}
|
||||
|
||||
# "Bone: mixamorig:Hips"
|
||||
line = next_line()
|
||||
bone_name = line[6:]
|
||||
bone_names.append(bone_name)
|
||||
|
||||
# " HEAD_LOCAL: <Vector (x, y, z)>"
|
||||
line = next_line()
|
||||
bone['head'] = parse_floats(line)[:3]
|
||||
|
||||
# " TAIL_LOCAL: ..." -- skip
|
||||
next_line()
|
||||
|
||||
# " Length: 0.123"
|
||||
line = next_line()
|
||||
bone['length'] = parse_floats(line)[0]
|
||||
|
||||
# 3x3 matrix (3 rows)
|
||||
mat = []
|
||||
for _ in range(3):
|
||||
mat.extend(parse_floats(next_line()))
|
||||
bone['matrix_3x3'] = mat
|
||||
|
||||
# " Parent: None" or " Parent: boneName"
|
||||
line = next_line()
|
||||
if line == " Parent: None":
|
||||
bone_parent_names.append(None)
|
||||
else:
|
||||
bone_parent_names.append(line[10:])
|
||||
|
||||
# " Children: ['a', 'b'] or []"
|
||||
line = next_line()
|
||||
bone_children_names.append(parse_children(line))
|
||||
|
||||
bones.append(bone)
|
||||
|
||||
# Build name -> index map
|
||||
name_to_idx = {name: i for i, name in enumerate(bone_names)}
|
||||
|
||||
# Resolve parent / child indices
|
||||
for i in range(num_bones):
|
||||
if bone_parent_names[i] is None:
|
||||
bones[i]['parent'] = -1
|
||||
else:
|
||||
bones[i]['parent'] = name_to_idx[bone_parent_names[i]]
|
||||
bones[i]['children'] = [name_to_idx[c] for c in bone_children_names[i]]
|
||||
|
||||
# --- Multi-mesh header ---
|
||||
line = next_line() # "=== TOTAL MESHES TO EXPORT: 7 ==="
|
||||
num_meshes = parse_first_int(line)
|
||||
|
||||
meshes = []
|
||||
|
||||
for _ in range(num_meshes):
|
||||
# "=== Mesh Object: Name ==="
|
||||
line = next_line()
|
||||
m = re.match(r"===\s*Mesh Object:\s*(.+?)\s*===$", line)
|
||||
if not m:
|
||||
raise ValueError(f"Invalid mesh header: {line}")
|
||||
mesh_name = m.group(1)
|
||||
|
||||
# --- Vertices ---
|
||||
line = next_line() # "===Vertices: N"
|
||||
num_vertices = parse_first_int(line)
|
||||
|
||||
vertices = []
|
||||
for _ in range(num_vertices):
|
||||
vertices.append(parse_floats(next_line())[:3])
|
||||
|
||||
# --- UV Coordinates ---
|
||||
next_line() # "===UV Coordinates:"
|
||||
line = next_line() # "Face count: M"
|
||||
num_faces = parse_first_int(line)
|
||||
|
||||
uvs = []
|
||||
for _ in range(num_faces):
|
||||
next_line() # "Face N"
|
||||
next_line() # "UV Count: 3"
|
||||
face_uvs = []
|
||||
for _ in range(3):
|
||||
face_uvs.extend(parse_floats(next_line())[:2])
|
||||
uvs.append(face_uvs)
|
||||
|
||||
# --- Normals ---
|
||||
next_line() # "===Normals:"
|
||||
normals = []
|
||||
for _ in range(num_vertices):
|
||||
normals.append(parse_floats(next_line())[:3])
|
||||
|
||||
# --- Triangles ---
|
||||
line = next_line() # "===Triangles: M"
|
||||
num_triangles = parse_first_int(line)
|
||||
|
||||
triangles = []
|
||||
for _ in range(num_triangles):
|
||||
line = next_line()
|
||||
ints = [int(x) for x in re.findall(r'[-]?\d+', line)]
|
||||
triangles.append(ints[:3])
|
||||
|
||||
# --- Vertex Weights ---
|
||||
next_line() # "=== Vertex Weights (Max 5 bones per vertex) ==="
|
||||
vertex_weights = []
|
||||
for _ in range(num_vertices):
|
||||
next_line() # "Vertex N:"
|
||||
line = next_line() # "Vertex groups: K"
|
||||
num_groups = parse_first_int(line)
|
||||
|
||||
groups = []
|
||||
for _ in range(num_groups):
|
||||
line = next_line()
|
||||
m = re.search(r"'([^']+)'.*?([-]?\d+\.\d+)", line)
|
||||
bone_name = m.group(1)
|
||||
weight = float(m.group(2))
|
||||
groups.append((name_to_idx[bone_name], weight))
|
||||
|
||||
vertex_weights.append(groups)
|
||||
|
||||
meshes.append({
|
||||
'name': mesh_name,
|
||||
'num_vertices': num_vertices,
|
||||
'vertices': vertices,
|
||||
'num_faces': num_faces,
|
||||
'uvs': uvs,
|
||||
'normals': normals,
|
||||
'num_triangles': num_triangles,
|
||||
'triangles': triangles,
|
||||
'vertex_weights': vertex_weights,
|
||||
})
|
||||
|
||||
# --- Animation Keyframes ---
|
||||
next_line() # "=== Animation Keyframes ==="
|
||||
next_line() # "=== Bone Transforms per Keyframe ==="
|
||||
line = next_line() # "Keyframes: N"
|
||||
num_keyframes = parse_first_int(line)
|
||||
|
||||
keyframes = []
|
||||
for _ in range(num_keyframes):
|
||||
line = next_line() # "Frame: N"
|
||||
frame_number = parse_first_int(line)
|
||||
|
||||
bone_data = {}
|
||||
for _ in range(num_bones):
|
||||
line = next_line() # " Bone: mixamorig:Hips"
|
||||
bone_name = line.strip()
|
||||
if bone_name.startswith("Bone: "):
|
||||
bone_name = bone_name[6:]
|
||||
bone_idx = name_to_idx[bone_name]
|
||||
|
||||
# Location
|
||||
location = parse_floats(next_line())[:3]
|
||||
|
||||
# Rotation (skip)
|
||||
next_line()
|
||||
|
||||
# " Matrix:" (skip header)
|
||||
next_line()
|
||||
|
||||
# 4 rows of 4 floats
|
||||
matrix = []
|
||||
for _ in range(4):
|
||||
matrix.extend(parse_floats(next_line()))
|
||||
|
||||
bone_data[bone_idx] = {
|
||||
'location': location,
|
||||
'matrix': matrix,
|
||||
}
|
||||
|
||||
keyframes.append((frame_number, bone_data))
|
||||
|
||||
# ================================================================
|
||||
# Write binary file
|
||||
# ================================================================
|
||||
with open(output_path, 'wb') as out:
|
||||
# Header
|
||||
out.write(b'BSMF')
|
||||
out.write(struct.pack('<I', 1))
|
||||
|
||||
# Armature matrix (16 floats, row-major)
|
||||
out.write(struct.pack('<16f', *armature_matrix))
|
||||
|
||||
# Bones
|
||||
out.write(struct.pack('<I', num_bones))
|
||||
for i in range(num_bones):
|
||||
b = bones[i]
|
||||
out.write(struct.pack('<3f', *b['head']))
|
||||
out.write(struct.pack('<f', b['length']))
|
||||
out.write(struct.pack('<9f', *b['matrix_3x3']))
|
||||
out.write(struct.pack('<i', b['parent']))
|
||||
out.write(struct.pack('<I', len(b['children'])))
|
||||
for c in b['children']:
|
||||
out.write(struct.pack('<i', c))
|
||||
|
||||
# Meshes
|
||||
out.write(struct.pack('<I', num_meshes))
|
||||
for md in meshes:
|
||||
name_bytes = md['name'].encode('utf-8')
|
||||
out.write(struct.pack('<I', len(name_bytes)))
|
||||
out.write(name_bytes)
|
||||
|
||||
# Vertices
|
||||
out.write(struct.pack('<I', md['num_vertices']))
|
||||
for v in md['vertices']:
|
||||
out.write(struct.pack('<3f', *v))
|
||||
|
||||
# UV Coordinates
|
||||
out.write(struct.pack('<I', md['num_faces']))
|
||||
for uv in md['uvs']:
|
||||
out.write(struct.pack('<6f', *uv))
|
||||
|
||||
# Normals
|
||||
for n in md['normals']:
|
||||
out.write(struct.pack('<3f', *n))
|
||||
|
||||
# Triangles
|
||||
out.write(struct.pack('<I', md['num_triangles']))
|
||||
for t in md['triangles']:
|
||||
out.write(struct.pack('<3i', *t))
|
||||
|
||||
# Vertex weights
|
||||
for vw in md['vertex_weights']:
|
||||
out.write(struct.pack('<I', len(vw)))
|
||||
for bone_idx, weight in vw:
|
||||
out.write(struct.pack('<if', bone_idx, weight))
|
||||
|
||||
# Animation Keyframes
|
||||
out.write(struct.pack('<I', num_keyframes))
|
||||
for frame_num, bone_data in keyframes:
|
||||
out.write(struct.pack('<i', frame_num))
|
||||
for i in range(num_bones):
|
||||
bd = bone_data[i]
|
||||
out.write(struct.pack('<3f', *bd['location']))
|
||||
out.write(struct.pack('<16f', *bd['matrix']))
|
||||
|
||||
input_size = sum(len(l) for l in lines)
|
||||
import os
|
||||
output_size = os.path.getsize(output_path)
|
||||
print(f"Converted: {input_path} ({input_size:,} bytes text) -> {output_path} ({output_size:,} bytes binary)")
|
||||
print(f" Bones: {num_bones}, Meshes: {num_meshes}, Keyframes: {num_keyframes}")
|
||||
for md in meshes:
|
||||
print(f" - {md['name']}: {md['num_vertices']} verts, "
|
||||
f"{md['num_faces']} faces, {md['num_triangles']} tris")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
print(f"Usage: {sys.argv[0]} <input.txt> <output.bin>")
|
||||
sys.exit(1)
|
||||
|
||||
convert(sys.argv[1], sys.argv[2])
|
||||
@@ -1,78 +0,0 @@
|
||||
#include "Game.h"
|
||||
#include "Environment.h"
|
||||
ZL::Game game;
|
||||
void MainLoop() {
|
||||
game.update();
|
||||
}
|
||||
int main(int argc, char* argv[]) {
|
||||
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
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
### Перед запуском в папке ```app/jni/```(рядом с src) нужно создать три папки с исходниками библиотек ```libpng```, ```SDL```, ```zlib```
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1,93 @@
|
||||
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/navigation/PathFinder.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)
|
||||
@@ -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);
|
||||
}
|
||||
Vendored
+98
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Game</string>
|
||||
</resources>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
Binary file not shown.
@@ -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
|
||||
Vendored
+160
@@ -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 "$@"
|
||||
Vendored
+90
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
include ':app'
|
||||
@@ -0,0 +1,231 @@
|
||||
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(bishkek-witcher LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# --- АВТО-ЗАГРУЗКА ЗАВИСИМОСТЕЙ ---
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake")
|
||||
# Теперь гарантированно есть папка ../thirdparty со всеми исходниками
|
||||
|
||||
# ===========================================
|
||||
# Lua (5.5.0) - embedded scripting, pure C, compiles with Emscripten
|
||||
# ===========================================
|
||||
set(LUA_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/lua-5.4.8")
|
||||
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
|
||||
list(REMOVE_ITEM LUA_SOURCES
|
||||
"${LUA_SRC_DIR}/lua.c"
|
||||
"${LUA_SRC_DIR}/luac.c"
|
||||
"${LUA_SRC_DIR}/onelua.c"
|
||||
)
|
||||
add_library(lua_static STATIC ${LUA_SOURCES})
|
||||
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
|
||||
|
||||
# sol2 (header-only)
|
||||
set(SOL2_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/sol2-3.3.0")
|
||||
|
||||
# Apply patch for Clang/Emscripten compatibility in optional<T&>::emplace().
|
||||
set(_sol2_sentinel "${SOL2_SRC_DIR}/.patched")
|
||||
if(NOT EXISTS "${_sol2_sentinel}")
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} apply --ignore-whitespace
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../cmake/patches/sol2-3.3.0-clang-optional.patch"
|
||||
WORKING_DIRECTORY "${SOL2_SRC_DIR}"
|
||||
RESULT_VARIABLE _sol2_patch_res
|
||||
)
|
||||
if(_sol2_patch_res EQUAL 0)
|
||||
file(WRITE "${_sol2_sentinel}" "patched\n")
|
||||
message(STATUS "Applied sol2 Clang optional patch")
|
||||
else()
|
||||
message(WARNING "sol2 patch failed (exit ${_sol2_patch_res}) — Clang/Emscripten builds may not compile")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Git not found — cannot apply sol2 patch automatically. "
|
||||
"Apply cmake/patches/sol2-3.3.0-clang-optional.patch manually.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Список исходных файлов (синхронизирован с proj-windows/CMakeLists.txt)
|
||||
set(SOURCES
|
||||
../src/main.cpp
|
||||
../src/Game.cpp
|
||||
../src/Game.h
|
||||
../src/Character.cpp
|
||||
../src/Character.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/BoneAnimatedModelNew.cpp
|
||||
../src/BoneAnimatedModelNew.h
|
||||
../src/render/OpenGlExtensions.cpp
|
||||
../src/render/OpenGlExtensions.h
|
||||
../src/utils/Utils.cpp
|
||||
../src/utils/Utils.h
|
||||
../src/SparkEmitter.cpp
|
||||
../src/SparkEmitter.h
|
||||
../src/utils/Perlin.cpp
|
||||
../src/utils/Perlin.h
|
||||
../src/utils/TaskManager.cpp
|
||||
../src/utils/TaskManager.h
|
||||
../src/render/FrameBuffer.cpp
|
||||
../src/render/FrameBuffer.h
|
||||
../src/render/ShadowMap.cpp
|
||||
../src/render/ShadowMap.h
|
||||
../src/UiManager.cpp
|
||||
../src/UiManager.h
|
||||
../src/render/TextRenderer.h
|
||||
../src/render/TextRenderer.cpp
|
||||
../src/MenuManager.h
|
||||
../src/MenuManager.cpp
|
||||
../src/Location.h
|
||||
../src/Location.cpp
|
||||
../src/GameConstants.h
|
||||
../src/GameConstants.cpp
|
||||
../src/ScriptEngine.h
|
||||
../src/ScriptEngine.cpp
|
||||
../src/navigation/PathFinder.h
|
||||
../src/navigation/PathFinder.cpp
|
||||
../src/items/GameObjectLoader.h
|
||||
../src/items/GameObjectLoader.cpp
|
||||
../src/items/Item.h
|
||||
../src/items/Item.cpp
|
||||
../src/items/InteractiveObject.h
|
||||
../src/items/InteractiveObject.cpp
|
||||
../src/dialogue/DialogueTypes.h
|
||||
../src/dialogue/DialogueDatabase.h
|
||||
../src/dialogue/DialogueDatabase.cpp
|
||||
../src/dialogue/DialogueRuntime.h
|
||||
../src/dialogue/DialogueRuntime.cpp
|
||||
../src/dialogue/DialogueOverlay.h
|
||||
../src/dialogue/DialogueOverlay.cpp
|
||||
../src/dialogue/DialogueSystem.h
|
||||
../src/dialogue/DialogueSystem.cpp
|
||||
)
|
||||
|
||||
add_executable(bishkek-witcher ${SOURCES})
|
||||
|
||||
# Настройка путей к инклудам (используем скачанные исходники)
|
||||
target_include_directories(bishkek-witcher PRIVATE
|
||||
../src
|
||||
../thirdparty/eigen-5.0.0
|
||||
../thirdparty/boost_1_90_0
|
||||
"${SOL2_SRC_DIR}/include"
|
||||
)
|
||||
|
||||
# Сборка 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)
|
||||
|
||||
target_link_libraries(bishkek-witcher PRIVATE zip z lua_static websocket.js)
|
||||
|
||||
# Эмскриптен-флаги
|
||||
set(EMSCRIPTEN_FLAGS
|
||||
"-sUSE_SDL=2"
|
||||
"-sUSE_SDL_IMAGE=2"
|
||||
"-sUSE_LIBPNG=1"
|
||||
"-sUSE_ZLIB=1"
|
||||
"-sUSE_SDL_TTF=2"
|
||||
"-sUSE_SDL_MIXER=2"
|
||||
"-sSDL2_MIXER_FORMATS=[ogg,mp3]"
|
||||
#"-pthread"
|
||||
#"-sUSE_PTHREADS=1"
|
||||
"-fexceptions"
|
||||
"-DNETWORK"
|
||||
)
|
||||
|
||||
target_compile_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
|
||||
|
||||
# Only loading.png and the shaders used before resources.zip is ready are preloaded.
|
||||
# resources.zip is downloaded asynchronously at runtime and served as a separate file.
|
||||
set(EMSCRIPTEN_LINK_FLAGS
|
||||
${EMSCRIPTEN_FLAGS}
|
||||
"-O2"
|
||||
#"-sPTHREAD_POOL_SIZE=4"
|
||||
"-sALLOW_MEMORY_GROWTH=1"
|
||||
"-sFULL_ES3=1"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
|
||||
)
|
||||
|
||||
# Применяем настройки линковки
|
||||
target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
|
||||
|
||||
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
|
||||
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
|
||||
set_target_properties(bishkek-witcher 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(bishkek-witcher pack_resources)
|
||||
|
||||
|
||||
# Определяем путь к директории установки (относительно папки билда)
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
|
||||
|
||||
# 1. Устанавливаем основной HTML файл
|
||||
install(TARGETS bishkek-witcher
|
||||
RUNTIME DESTINATION .
|
||||
)
|
||||
|
||||
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
|
||||
DESTINATION .
|
||||
)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
|
||||
DESTINATION .
|
||||
)
|
||||
|
||||
# resources.zip is served separately and downloaded asynchronously at runtime
|
||||
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
|
||||
|
||||
add_custom_command(TARGET bishkek-witcher POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} --install .
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
COMMENT "Automatically deploying to public directory..."
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
# how to build
|
||||
|
||||
If emsdk is not installed, you need to clone it from here: https://github.com/emscripten-core/emsdk
|
||||
|
||||
and install:
|
||||
```
|
||||
C:\Work\Projects\emsdk\emsdk.bat install latest
|
||||
```
|
||||
|
||||
Then 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
|
||||
```
|
||||
@@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title>Bishkek Witcher</title>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0; padding: 0; width: 100%; height: 100%;
|
||||
overflow: hidden; background-color: #000;
|
||||
position: fixed;
|
||||
}
|
||||
#canvas {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100vw; height: 100vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#fs-button {
|
||||
position: absolute;
|
||||
top: 10px; right: 10px;
|
||||
padding: 10px;
|
||||
z-index: 10;
|
||||
background: rgba(255,255,255,0.3);
|
||||
color: white; border: 1px solid white;
|
||||
cursor: pointer;
|
||||
font-family: sans-serif;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button id="fs-button">Fullscreen</button>
|
||||
<div id="status">Downloading...</div>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
|
||||
<script>
|
||||
function prepareModuleEnvironment() {
|
||||
window.Module = window.Module || {};
|
||||
var canvasEl = document.getElementById('canvas');
|
||||
window.Module.canvas = canvasEl;
|
||||
window.Module.setStatus = window.Module.setStatus || function (text) {
|
||||
var statusElement = document.getElementById("status");
|
||||
statusElement.innerHTML = text;
|
||||
statusElement.style.display = text ? 'block' : 'none';
|
||||
};
|
||||
}
|
||||
|
||||
function loadGameScript() {
|
||||
var s = document.createElement('script');
|
||||
s.src = 'bishkek-witcher.js';
|
||||
s.async = true;
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
|
||||
document.getElementById('fs-button').addEventListener('click', function() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch(function(e) {
|
||||
console.error('Fullscreen error: ' + e.message);
|
||||
});
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
prepareModuleEnvironment();
|
||||
loadGameScript();
|
||||
});
|
||||
|
||||
window.addEventListener("orientationchange", function() {
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
<!doctypehtml><html lang=en-us><head><meta charset=utf-8><meta content="text/html; charset=utf-8"http-equiv=Content-Type><title>Emscripten-Generated Code</title><style>body{font-family:arial;margin:0;padding:none}.emscripten{padding-right:0;margin-left:auto;margin-right:auto;display:block}div.emscripten{text-align:center}div.emscripten_border{border:1px solid #000}canvas.emscripten{border:0 none;background-color:#000}#emscripten_logo{display:inline-block;margin:0;padding:6px;width:265px}.spinner{height:30px;width:30px;margin:0;margin-top:20px;margin-left:20px;display:inline-block;vertical-align:top;-webkit-animation:rotation .8s linear infinite;-moz-animation:rotation .8s linear infinite;-o-animation:rotation .8s linear infinite;animation:rotation .8s linear infinite;border-left:5px solid #ebebeb;border-right:5px solid #ebebeb;border-bottom:5px solid #ebebeb;border-top:5px solid #787878;border-radius:100%;background-color:#bdd72e}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(360deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(360deg)}}#status{display:inline-block;vertical-align:top;margin-top:30px;margin-left:20px;font-weight:700;color:#787878}#progress{height:20px;width:300px}#controls{display:inline-block;float:right;vertical-align:top;margin-top:30px;margin-right:20px}#output{width:100%;height:200px;margin:0 auto;margin-top:10px;border-left:0;border-right:0px;padding-left:0;padding-right:0;display:block;background-color:#000;color:#fff;font-family:'Lucida Console',Monaco,monospace;outline:0}</style></head><body><script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
||||
<script>eruda.init();</script><a href=http://emscripten.org><img id=emscripten_logo src=""></a><div class=spinner id=spinner></div><div class=emscripten id=status>Downloading...</div><span id=controls><span><input type=checkbox id=resize>Resize canvas</span> <span><input type=checkbox id=pointerLock checked>Lock/hide mouse pointer </span><span><input type=button onclick='Module.requestFullscreen(document.getElementById("pointerLock").checked,document.getElementById("resize").checked)'value=Fullscreen></span></span><div class=emscripten><progress hidden id=progress max=100 value=0></progress></div><div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas></div><textarea id=output rows=8></textarea><script>var statusElement=document.getElementById("status"),progressElement=document.getElementById("progress"),spinnerElement=document.getElementById("spinner"),canvasElement=document.getElementById("canvas"),outputElement=document.getElementById("output");outputElement&&(outputElement.value=""),canvasElement.addEventListener("webglcontextlost",(e=>{alert("WebGL context lost. You will need to reload the page."),e.preventDefault()}),!1);var Module={print(...e){if(console.log(...e),outputElement){var t=e.join(" ");outputElement.value+=t+"\n",outputElement.scrollTop=outputElement.scrollHeight}},canvas:canvasElement,setStatus(e){if(Module.setStatus.last||(Module.setStatus.last={time:Date.now(),text:""}),e!==Module.setStatus.last.text){var t=e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/),n=Date.now();t&&n-Module.setStatus.last.time<30||(Module.setStatus.last.time=n,Module.setStatus.last.text=e,t?(e=t[1],progressElement.value=100*parseInt(t[2]),progressElement.max=100*parseInt(t[4]),progressElement.hidden=!1,spinnerElement.hidden=!1):(progressElement.value=null,progressElement.max=null,progressElement.hidden=!0,e||(spinnerElement.style.display="none")),statusElement.innerHTML=e)}},totalDependencies:0,monitorRunDependencies(e){this.totalDependencies=Math.max(this.totalDependencies,e),Module.setStatus(e?"Preparing... ("+(this.totalDependencies-e)+"/"+this.totalDependencies+")":"All downloads complete.")}};Module.setStatus("Downloading..."),window.onerror=e=>{Module.setStatus("Exception thrown, see JavaScript console"),spinnerElement.style.display="none",Module.setStatus=e=>{e&&console.error("[post-exception status] "+e)}}</script><script async src="space-game001.js" crossorigin="anonymous"></script></body></html>
|
||||
@@ -0,0 +1,200 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title>Sky Trek Tales</title>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0; padding: 0; width: 100%; height: 100%;
|
||||
overflow: hidden; background-color: #000;
|
||||
position: fixed;
|
||||
}
|
||||
#canvas {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100vw; height: 100vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#fs-button {
|
||||
position: absolute;
|
||||
top: 10px; right: 10px;
|
||||
padding: 10px;
|
||||
z-index: 10;
|
||||
background: rgba(255,255,255,0.3);
|
||||
color: white; border: 1px solid white;
|
||||
cursor: pointer;
|
||||
font-family: sans-serif;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
||||
|
||||
/* Nick modal */
|
||||
#nickOverlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0,0,0,0.85);
|
||||
z-index: 9999;
|
||||
}
|
||||
#nickBox {
|
||||
background: #111;
|
||||
border: 1px solid #444;
|
||||
padding: 24px;
|
||||
width: 320px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
|
||||
text-align: center;
|
||||
}
|
||||
#nickBox h2 { margin: 0 0 12px 0; font-size: 18px; color: #eee; }
|
||||
#nickBox input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid #333;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
#nickBox button {
|
||||
padding: 10px 16px;
|
||||
font-size: 16px;
|
||||
background: #2a9fd6;
|
||||
border: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button id="fs-button">Fullscreen</button>
|
||||
<div id="status">Downloading...</div>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
<div id="nickOverlay" style="display:none;">
|
||||
<div id="nickBox">
|
||||
<h2>Enter your nickname</h2>
|
||||
<input id="nickInput" type="text" maxlength="32" placeholder="Player" />
|
||||
<div>
|
||||
<button id="nickSubmit">Start</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Utility: подготовить глобальный Module до загрузки Emscripten-скрипта
|
||||
function prepareModuleEnvironment() {
|
||||
window.Module = window.Module || {};
|
||||
var canvasEl = document.getElementById('canvas');
|
||||
// Устанавливаем canvas для Emscripten, чтобы createContext не падал
|
||||
window.Module.canvas = canvasEl;
|
||||
// Подготовим заглушку setStatus, если ещё нет
|
||||
window.Module.setStatus = window.Module.setStatus || function (text) {
|
||||
var statusElement = document.getElementById("status");
|
||||
statusElement.innerHTML = text;
|
||||
statusElement.style.display = text ? 'block' : 'none';
|
||||
};
|
||||
}
|
||||
|
||||
// Show overlay only if no nickname saved.
|
||||
function loadGameScript() {
|
||||
var s = document.createElement('script');
|
||||
s.src = 'space-game001.js';
|
||||
s.async = true;
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
|
||||
function showNickOverlay() {
|
||||
var overlay = document.getElementById('nickOverlay');
|
||||
overlay.style.display = 'flex';
|
||||
var input = document.getElementById('nickInput');
|
||||
input.focus();
|
||||
}
|
||||
|
||||
function hideNickOverlay() {
|
||||
var overlay = document.getElementById('nickOverlay');
|
||||
overlay.style.display = 'none';
|
||||
}
|
||||
|
||||
function saveNickAndStart(nick) {
|
||||
try {
|
||||
if (!nick || nick.trim() === '') nick = 'Player';
|
||||
localStorage.setItem('spacegame_nick', nick);
|
||||
} catch (e) {
|
||||
console.warn('localStorage not available', e);
|
||||
}
|
||||
hideNickOverlay();
|
||||
// перед загрузкой скрипта гарантируем, что Module.canvas задан
|
||||
prepareModuleEnvironment();
|
||||
loadGameScript();
|
||||
}
|
||||
|
||||
document.getElementById('fs-button').addEventListener('click', function() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch(function(e) {
|
||||
console.error('Fullscreen error: ' + e.message);
|
||||
});
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Готовим Module сразу — даже если откроется модалка, поле canvas будет доступно для скрипта (если он загружается позже)
|
||||
prepareModuleEnvironment();
|
||||
|
||||
var stored = null;
|
||||
try {
|
||||
stored = localStorage.getItem('spacegame_nick');
|
||||
} catch (e) {
|
||||
console.warn('localStorage not available', e);
|
||||
}
|
||||
|
||||
if (stored && stored.trim() !== '') {
|
||||
// Nick is present — start immediately
|
||||
loadGameScript();
|
||||
} else {
|
||||
// Show modal to request nickname before loading WASM
|
||||
showNickOverlay();
|
||||
var submit = document.getElementById('nickSubmit');
|
||||
var input = document.getElementById('nickInput');
|
||||
|
||||
submit.addEventListener('click', function() {
|
||||
saveNickAndStart(input.value);
|
||||
});
|
||||
|
||||
input.addEventListener('input', function() {
|
||||
// Strip any character that is not a-z, A-Z, 0-9 or space
|
||||
var pos = this.selectionStart;
|
||||
var cleaned = this.value.replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
if (cleaned !== this.value) {
|
||||
this.value = cleaned;
|
||||
this.setSelectionRange(pos - 1, pos - 1);
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
saveNickAndStart(input.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("orientationchange", function() {
|
||||
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
||||
// Ждем завершения анимации поворота.
|
||||
setTimeout(() => {
|
||||
// В Emscripten это вызовет ваш onWindowResized в C++
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, 200);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,194 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(space-game001 LANGUAGES C 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/Character.cpp
|
||||
../src/Character.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/BoneAnimatedModelNew.cpp
|
||||
../src/BoneAnimatedModelNew.h
|
||||
../src/render/OpenGlExtensions.cpp
|
||||
../src/render/OpenGlExtensions.h
|
||||
../src/utils/Utils.cpp
|
||||
../src/utils/Utils.h
|
||||
../src/SparkEmitter.cpp
|
||||
../src/SparkEmitter.h
|
||||
../src/utils/Perlin.cpp
|
||||
../src/utils/Perlin.h
|
||||
../src/utils/TaskManager.cpp
|
||||
../src/utils/TaskManager.h
|
||||
../src/render/FrameBuffer.cpp
|
||||
../src/render/FrameBuffer.h
|
||||
../src/render/ShadowMap.cpp
|
||||
../src/render/ShadowMap.h
|
||||
../src/UiManager.cpp
|
||||
../src/UiManager.h
|
||||
../src/render/TextRenderer.h
|
||||
../src/render/TextRenderer.cpp
|
||||
../src/MenuManager.h
|
||||
../src/MenuManager.cpp
|
||||
../src/Location.h
|
||||
../src/Location.cpp
|
||||
../src/GameConstants.h
|
||||
../src/GameConstants.cpp
|
||||
../src/ScriptEngine.h
|
||||
../src/ScriptEngine.cpp
|
||||
../src/navigation/PathFinder.h
|
||||
../src/navigation/PathFinder.cpp
|
||||
../src/items/GameObjectLoader.h
|
||||
../src/items/GameObjectLoader.cpp
|
||||
../src/items/Item.h
|
||||
../src/items/Item.cpp
|
||||
../src/items/InteractiveObject.h
|
||||
../src/items/InteractiveObject.cpp
|
||||
../src/dialogue/DialogueTypes.h
|
||||
../src/dialogue/DialogueDatabase.h
|
||||
../src/dialogue/DialogueDatabase.cpp
|
||||
../src/dialogue/DialogueRuntime.h
|
||||
../src/dialogue/DialogueRuntime.cpp
|
||||
../src/dialogue/DialogueOverlay.h
|
||||
../src/dialogue/DialogueOverlay.cpp
|
||||
../src/dialogue/DialogueSystem.h
|
||||
../src/dialogue/DialogueSystem.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
|
||||
# DEBUG_LIGHT
|
||||
# SHOW_PATH
|
||||
)
|
||||
|
||||
# Линкуем с 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
|
||||
sol2_external_lib
|
||||
SDL2_mixer_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/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
|
||||
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/zd.dll,$<TARGET_FILE_DIR:space-game001>/z.dll>")
|
||||
|
||||
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
|
||||
|
||||
set(SDL2MIXER_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/bin/SDL2_mixerd.dll,${SDL2MIXER_BASE_DIR}-Release/bin/SDL2_mixer.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>"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-$<CONFIG>/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
"$<TARGET_FILE_DIR:space-game001>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
# Копирование ресурсов после сборки
|
||||
# ===========================================
|
||||
|
||||
# Какие папки с ресурсами нужно копировать
|
||||
set(RUNTIME_RESOURCE_DIRS
|
||||
"resources"
|
||||
"audio"
|
||||
)
|
||||
|
||||
# Копируем ресурсы и шейдеры в папку 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
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
===Vertices (Split by UV/Normal): 20
|
||||
V 0: Pos(2.172386, 0.169635, 0.221728) Norm(-0.577351, -0.57735, -0.57735) UV(0.014711, 0.022579)
|
||||
V 1: Pos(2.172386, 0.169635, 0.558757) Norm(-0.578984, -0.585366, 0.56756) UV(0.547197, 0.022579)
|
||||
V 2: Pos(2.172386, 0.7737, 0.577419) Norm(-0.573104, 0.585688, 0.573167) UV(0.576682, 0.976966)
|
||||
V 3: Pos(2.172386, 0.755012, 0.221728) Norm(-0.580152, 0.560631, -0.590861) UV(0.014711, 0.94744)
|
||||
V 4: Pos(2.172386, 0.755012, 0.221728) Norm(-0.580152, 0.560631, -0.590861) UV(0.591716, 0.022579)
|
||||
V 5: Pos(2.181902, 0.755012, 0.221728) Norm(-1e-06, 0.688307, -0.72542) UV(0.606751, 0.022579)
|
||||
V 6: Pos(2.181902, 0.169635, 0.221728) Norm(-0.0, -0.707106, -0.707107) UV(0.606751, 0.94744)
|
||||
V 7: Pos(2.172386, 0.169635, 0.221728) Norm(-0.577351, -0.57735, -0.57735) UV(0.591716, 0.94744)
|
||||
V 8: Pos(2.172386, 0.169635, 0.221728) Norm(-0.577351, -0.57735, -0.57735) UV(0.63682, 0.555065)
|
||||
V 9: Pos(2.181902, 0.169635, 0.221728) Norm(-0.0, -0.707106, -0.707107) UV(0.621785, 0.555065)
|
||||
V 10: Pos(2.181902, 0.169635, 0.558757) Norm(1e-06, -0.717941, 0.696104) UV(0.621785, 0.022579)
|
||||
V 11: Pos(2.172386, 0.169635, 0.558757) Norm(-0.578984, -0.585366, 0.56756) UV(0.63682, 0.022579)
|
||||
V 12: Pos(2.172386, 0.7737, 0.577419) Norm(-0.573104, 0.585688, 0.573167) UV(0.606751, 0.022579)
|
||||
V 13: Pos(2.181902, 0.7737, 0.577419) Norm(0.0, 0.714705, 0.699426) UV(0.621785, 0.022579)
|
||||
V 14: Pos(2.181902, 0.755012, 0.221728) Norm(-1e-06, 0.688307, -0.72542) UV(0.621785, 0.585325)
|
||||
V 15: Pos(2.172386, 0.755012, 0.221728) Norm(-0.580152, 0.560631, -0.590861) UV(0.606751, 0.585325)
|
||||
V 16: Pos(2.172386, 0.169635, 0.558757) Norm(-0.578984, -0.585366, 0.56756) UV(0.591716, 0.977422)
|
||||
V 17: Pos(2.181902, 0.169635, 0.558757) Norm(1e-06, -0.717941, 0.696104) UV(0.576682, 0.977422)
|
||||
V 18: Pos(2.181902, 0.7737, 0.577419) Norm(0.0, 0.714705, 0.699426) UV(0.576682, 0.022579)
|
||||
V 19: Pos(2.172386, 0.7737, 0.577419) Norm(-0.573104, 0.585688, 0.573167) UV(0.591716, 0.022579)
|
||||
|
||||
===Triangles (Indices): 10
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 8 9 10
|
||||
Tri: 8 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 12 14 15
|
||||
Tri: 16 17 18
|
||||
Tri: 16 18 19
|
||||
@@ -0,0 +1,63 @@
|
||||
===Vertices (Split by UV/Normal): 40
|
||||
V 0: Pos(1.996428, 0.338833, 0.198714) Norm(-0.551234, -0.834351, 0.0) UV(0.971407, 0.5)
|
||||
V 1: Pos(1.996428, 0.338833, 0.202329) Norm(-0.450081, -0.681244, 0.57735) UV(0.944505, 0.5)
|
||||
V 2: Pos(1.983381, 0.402683, 0.202329) Norm(-0.681244, 0.450081, 0.57735) UV(0.944505, 0.023447)
|
||||
V 3: Pos(1.983381, 0.402683, 0.198714) Norm(-0.83435, 0.551234, 0.0) UV(0.971408, 0.023447)
|
||||
V 4: Pos(1.983381, 0.402683, 0.198714) Norm(-0.83435, 0.551234, 0.0) UV(0.863798, 0.700024)
|
||||
V 5: Pos(1.983381, 0.402683, 0.202329) Norm(-0.681244, 0.450081, 0.57735) UV(0.836895, 0.700024)
|
||||
V 6: Pos(2.073946, 0.421188, 0.202329) Norm(0.450082, 0.681244, 0.57735) UV(0.836895, 0.023447)
|
||||
V 7: Pos(2.073946, 0.421188, 0.198714) Norm(0.551236, 0.834349, 0.0) UV(0.863798, 0.023447)
|
||||
V 8: Pos(2.073946, 0.421188, 0.198714) Norm(0.551236, 0.834349, 0.0) UV(0.971408, 0.976553)
|
||||
V 9: Pos(2.073946, 0.421188, 0.202329) Norm(0.450082, 0.681244, 0.57735) UV(0.944505, 0.976553)
|
||||
V 10: Pos(2.086992, 0.357338, 0.202329) Norm(0.681243, -0.450082, 0.57735) UV(0.944505, 0.5)
|
||||
V 11: Pos(2.086992, 0.357338, 0.198714) Norm(0.83435, -0.551236, 0.0) UV(0.971408, 0.5)
|
||||
V 12: Pos(2.086992, 0.357338, 0.198714) Norm(0.83435, -0.551236, 0.0) UV(0.8907, 0.700024)
|
||||
V 13: Pos(2.086992, 0.357338, 0.202329) Norm(0.681243, -0.450082, 0.57735) UV(0.863798, 0.700024)
|
||||
V 14: Pos(1.996428, 0.338833, 0.202329) Norm(-0.450081, -0.681244, 0.57735) UV(0.863798, 0.023447)
|
||||
V 15: Pos(1.996428, 0.338833, 0.198714) Norm(-0.551234, -0.834351, 0.0) UV(0.8907, 0.023447)
|
||||
V 16: Pos(2.016325, 0.3535, 0.202405) Norm(-0.212218, -0.977222, 0.0) UV(0.917603, 0.911909)
|
||||
V 17: Pos(2.016325, 0.3535, 0.206019) Norm(-0.173196, -0.797534, 0.577877) UV(0.8907, 0.911909)
|
||||
V 18: Pos(1.992261, 0.390504, 0.206019) Norm(-0.79826, 0.173353, 0.576827) UV(0.8907, 0.589135)
|
||||
V 19: Pos(1.992261, 0.390504, 0.202405) Norm(-0.977223, 0.212217, 0.0) UV(0.917603, 0.589135)
|
||||
V 20: Pos(2.073946, 0.421188, 0.202329) Norm(0.450082, 0.681244, 0.57735) UV(0.508379, 0.023447)
|
||||
V 21: Pos(1.983381, 0.402683, 0.202329) Norm(-0.681244, 0.450081, 0.57735) UV(0.508379, 0.711448)
|
||||
V 22: Pos(1.996428, 0.338833, 0.202329) Norm(-0.450081, -0.681244, 0.57735) UV(0.023323, 0.711448)
|
||||
V 23: Pos(2.086992, 0.357338, 0.202329) Norm(0.681243, -0.450082, 0.57735) UV(0.023323, 0.023447)
|
||||
V 24: Pos(1.992261, 0.390504, 0.202405) Norm(-0.977223, 0.212217, 0.0) UV(0.917603, 0.023447)
|
||||
V 25: Pos(1.992261, 0.390504, 0.206019) Norm(-0.79826, 0.173353, 0.576827) UV(0.944505, 0.023447)
|
||||
V 26: Pos(2.057473, 0.431984, 0.206019) Norm(0.173196, 0.797534, 0.577878) UV(0.944505, 0.589134)
|
||||
V 27: Pos(2.057473, 0.431984, 0.202405) Norm(0.212218, 0.977222, 0.0) UV(0.917603, 0.589134)
|
||||
V 28: Pos(2.057473, 0.431984, 0.202405) Norm(0.212218, 0.977222, 0.0) UV(0.508379, 0.601986)
|
||||
V 29: Pos(2.057473, 0.431984, 0.206019) Norm(0.173196, 0.797534, 0.577878) UV(0.535281, 0.601986)
|
||||
V 30: Pos(2.081536, 0.39498, 0.206019) Norm(0.79826, -0.173354, 0.576827) UV(0.535281, 0.92476)
|
||||
V 31: Pos(2.081536, 0.39498, 0.202405) Norm(0.977222, -0.212219, 0.0) UV(0.508379, 0.92476)
|
||||
V 32: Pos(2.081536, 0.39498, 0.202405) Norm(0.977222, -0.212219, 0.0) UV(0.917603, 0.589135)
|
||||
V 33: Pos(2.081536, 0.39498, 0.206019) Norm(0.79826, -0.173354, 0.576827) UV(0.8907, 0.589135)
|
||||
V 34: Pos(2.016325, 0.3535, 0.206019) Norm(-0.173196, -0.797534, 0.577877) UV(0.8907, 0.023447)
|
||||
V 35: Pos(2.016325, 0.3535, 0.202405) Norm(-0.212218, -0.977222, 0.0) UV(0.917603, 0.023447)
|
||||
V 36: Pos(2.057473, 0.431984, 0.206019) Norm(0.173196, 0.797534, 0.577878) UV(0.508379, 0.598686)
|
||||
V 37: Pos(1.992261, 0.390504, 0.206019) Norm(-0.79826, 0.173353, 0.576827) UV(0.508379, 0.023447)
|
||||
V 38: Pos(2.016325, 0.3535, 0.206019) Norm(-0.173196, -0.797534, 0.577877) UV(0.836895, 0.026746)
|
||||
V 39: Pos(2.081536, 0.39498, 0.206019) Norm(0.79826, -0.173354, 0.576827) UV(0.836895, 0.601986)
|
||||
|
||||
===Triangles (Indices): 20
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 8 9 10
|
||||
Tri: 8 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 12 14 15
|
||||
Tri: 16 17 18
|
||||
Tri: 16 18 19
|
||||
Tri: 20 21 22
|
||||
Tri: 20 22 23
|
||||
Tri: 24 25 26
|
||||
Tri: 24 26 27
|
||||
Tri: 28 29 30
|
||||
Tri: 28 30 31
|
||||
Tri: 32 33 34
|
||||
Tri: 32 34 35
|
||||
Tri: 36 37 38
|
||||
Tri: 36 38 39
|
||||
@@ -0,0 +1,63 @@
|
||||
===Vertices (Split by UV/Normal): 40
|
||||
V 0: Pos(1.884571, 0.582254, 0.198714) Norm(-0.993741, -0.111707, 0.0) UV(0.971407, 0.5)
|
||||
V 1: Pos(1.884571, 0.582254, 0.202329) Norm(-0.811386, -0.091209, 0.577351) UV(0.944505, 0.5)
|
||||
V 2: Pos(1.925217, 0.633195, 0.202329) Norm(-0.091209, 0.811387, 0.57735) UV(0.944505, 0.023447)
|
||||
V 3: Pos(1.925217, 0.633195, 0.198714) Norm(-0.111707, 0.993741, 0.0) UV(0.971408, 0.023447)
|
||||
V 4: Pos(1.925217, 0.633195, 0.198714) Norm(-0.111707, 0.993741, 0.0) UV(0.863798, 0.700024)
|
||||
V 5: Pos(1.925217, 0.633195, 0.202329) Norm(-0.091209, 0.811387, 0.57735) UV(0.836895, 0.700024)
|
||||
V 6: Pos(1.997471, 0.575543, 0.202329) Norm(0.811386, 0.091209, 0.57735) UV(0.836895, 0.023447)
|
||||
V 7: Pos(1.997471, 0.575543, 0.198714) Norm(0.993741, 0.111708, 0.0) UV(0.863798, 0.023447)
|
||||
V 8: Pos(1.997471, 0.575543, 0.198714) Norm(0.993741, 0.111708, 0.0) UV(0.971408, 0.976553)
|
||||
V 9: Pos(1.997471, 0.575543, 0.202329) Norm(0.811386, 0.091209, 0.57735) UV(0.944505, 0.976553)
|
||||
V 10: Pos(1.956826, 0.524602, 0.202329) Norm(0.091209, -0.811386, 0.57735) UV(0.944505, 0.5)
|
||||
V 11: Pos(1.956826, 0.524602, 0.198714) Norm(0.111708, -0.993741, 0.0) UV(0.971408, 0.5)
|
||||
V 12: Pos(1.956826, 0.524602, 0.198714) Norm(0.111708, -0.993741, 0.0) UV(0.8907, 0.700024)
|
||||
V 13: Pos(1.956826, 0.524602, 0.202329) Norm(0.091209, -0.811386, 0.57735) UV(0.863798, 0.700024)
|
||||
V 14: Pos(1.884571, 0.582254, 0.202329) Norm(-0.811386, -0.091209, 0.577351) UV(0.863798, 0.023447)
|
||||
V 15: Pos(1.884571, 0.582254, 0.198714) Norm(-0.993741, -0.111707, 0.0) UV(0.8907, 0.023447)
|
||||
V 16: Pos(1.908582, 0.576383, 0.202405) Norm(-0.886097, -0.4635, 0.0) UV(0.917603, 0.911909)
|
||||
V 17: Pos(1.908582, 0.576383, 0.206019) Norm(-0.723165, -0.378274, 0.577877) UV(0.8907, 0.911909)
|
||||
V 18: Pos(1.921561, 0.618572, 0.206019) Norm(-0.378618, 0.723823, 0.576827) UV(0.8907, 0.589135)
|
||||
V 19: Pos(1.921561, 0.618572, 0.202405) Norm(-0.4635, 0.886097, 0.0) UV(0.917603, 0.589135)
|
||||
V 20: Pos(1.997471, 0.575543, 0.202329) Norm(0.811386, 0.091209, 0.57735) UV(0.508379, 0.023447)
|
||||
V 21: Pos(1.925217, 0.633195, 0.202329) Norm(-0.091209, 0.811387, 0.57735) UV(0.508379, 0.711448)
|
||||
V 22: Pos(1.884571, 0.582254, 0.202329) Norm(-0.811386, -0.091209, 0.577351) UV(0.023323, 0.711448)
|
||||
V 23: Pos(1.956826, 0.524602, 0.202329) Norm(0.091209, -0.811386, 0.57735) UV(0.023323, 0.023447)
|
||||
V 24: Pos(1.921561, 0.618572, 0.202405) Norm(-0.4635, 0.886097, 0.0) UV(0.917603, 0.023447)
|
||||
V 25: Pos(1.921561, 0.618572, 0.206019) Norm(-0.378618, 0.723823, 0.576827) UV(0.944505, 0.023447)
|
||||
V 26: Pos(1.995198, 0.595107, 0.206019) Norm(0.723164, 0.378273, 0.577878) UV(0.944505, 0.589134)
|
||||
V 27: Pos(1.995198, 0.595107, 0.202405) Norm(0.886097, 0.4635, 0.0) UV(0.917603, 0.589134)
|
||||
V 28: Pos(1.995198, 0.595107, 0.202405) Norm(0.886097, 0.4635, 0.0) UV(0.508379, 0.601986)
|
||||
V 29: Pos(1.995198, 0.595107, 0.206019) Norm(0.723164, 0.378273, 0.577878) UV(0.535281, 0.601986)
|
||||
V 30: Pos(1.98222, 0.552918, 0.206019) Norm(0.378618, -0.723823, 0.576827) UV(0.535281, 0.92476)
|
||||
V 31: Pos(1.98222, 0.552918, 0.202405) Norm(0.4635, -0.886097, 0.0) UV(0.508379, 0.92476)
|
||||
V 32: Pos(1.98222, 0.552918, 0.202405) Norm(0.4635, -0.886097, 0.0) UV(0.917603, 0.589135)
|
||||
V 33: Pos(1.98222, 0.552918, 0.206019) Norm(0.378618, -0.723823, 0.576827) UV(0.8907, 0.589135)
|
||||
V 34: Pos(1.908582, 0.576383, 0.206019) Norm(-0.723165, -0.378274, 0.577877) UV(0.8907, 0.023447)
|
||||
V 35: Pos(1.908582, 0.576383, 0.202405) Norm(-0.886097, -0.4635, 0.0) UV(0.917603, 0.023447)
|
||||
V 36: Pos(1.995198, 0.595107, 0.206019) Norm(0.723164, 0.378273, 0.577878) UV(0.508379, 0.598686)
|
||||
V 37: Pos(1.921561, 0.618572, 0.206019) Norm(-0.378618, 0.723823, 0.576827) UV(0.508379, 0.023447)
|
||||
V 38: Pos(1.908582, 0.576383, 0.206019) Norm(-0.723165, -0.378274, 0.577877) UV(0.836895, 0.026746)
|
||||
V 39: Pos(1.98222, 0.552918, 0.206019) Norm(0.378618, -0.723823, 0.576827) UV(0.836895, 0.601986)
|
||||
|
||||
===Triangles (Indices): 20
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 8 9 10
|
||||
Tri: 8 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 12 14 15
|
||||
Tri: 16 17 18
|
||||
Tri: 16 18 19
|
||||
Tri: 20 21 22
|
||||
Tri: 20 22 23
|
||||
Tri: 24 25 26
|
||||
Tri: 24 26 27
|
||||
Tri: 28 29 30
|
||||
Tri: 28 30 31
|
||||
Tri: 32 33 34
|
||||
Tri: 32 34 35
|
||||
Tri: 36 37 38
|
||||
Tri: 36 38 39
|
||||
@@ -0,0 +1,29 @@
|
||||
===Vertices (Split by UV/Normal): 14
|
||||
V 0: Pos(1.928162, 0.364475, 0.015009) Norm(-0.690923, -0.414235, -0.592482) UV(0.380616, 0.022463)
|
||||
V 1: Pos(1.924941, 0.362544, 0.107039) Norm(-0.709291, -0.425247, 0.562202) UV(0.619384, 0.022463)
|
||||
V 2: Pos(1.948582, 0.45697, 0.107039) Norm(-0.425247, 0.709291, 0.562202) UV(0.619384, 0.261232)
|
||||
V 3: Pos(1.950513, 0.453749, 0.015009) Norm(-0.414235, 0.690923, -0.592482) UV(0.380616, 0.261232)
|
||||
V 4: Pos(2.043009, 0.433329, 0.107039) Norm(0.709291, 0.425246, 0.562202) UV(0.619384, 0.5)
|
||||
V 5: Pos(2.039788, 0.431398, 0.015009) Norm(0.690923, 0.414234, -0.592482) UV(0.380616, 0.5)
|
||||
V 6: Pos(2.019368, 0.338903, 0.107039) Norm(0.425246, -0.709292, 0.562202) UV(0.619384, 0.738768)
|
||||
V 7: Pos(2.017437, 0.342124, 0.015009) Norm(0.414235, -0.690924, -0.592481) UV(0.380616, 0.738768)
|
||||
V 8: Pos(1.924941, 0.362544, 0.107039) Norm(-0.709291, -0.425247, 0.562202) UV(0.619384, 0.977537)
|
||||
V 9: Pos(1.928162, 0.364475, 0.015009) Norm(-0.690923, -0.414235, -0.592482) UV(0.380616, 0.977537)
|
||||
V 10: Pos(1.950513, 0.453749, 0.015009) Norm(-0.414235, 0.690923, -0.592482) UV(0.141847, 0.5)
|
||||
V 11: Pos(1.928162, 0.364475, 0.015009) Norm(-0.690923, -0.414235, -0.592482) UV(0.141847, 0.738768)
|
||||
V 12: Pos(1.948582, 0.45697, 0.107039) Norm(-0.425247, 0.709291, 0.562202) UV(0.858153, 0.5)
|
||||
V 13: Pos(1.924941, 0.362544, 0.107039) Norm(-0.709291, -0.425247, 0.562202) UV(0.858153, 0.738768)
|
||||
|
||||
===Triangles (Indices): 12
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 3 2 4
|
||||
Tri: 3 4 5
|
||||
Tri: 5 4 6
|
||||
Tri: 5 6 7
|
||||
Tri: 7 6 8
|
||||
Tri: 7 8 9
|
||||
Tri: 10 5 7
|
||||
Tri: 10 7 11
|
||||
Tri: 4 12 13
|
||||
Tri: 4 13 6
|
||||
@@ -0,0 +1,29 @@
|
||||
===Vertices (Split by UV/Normal): 14
|
||||
V 0: Pos(1.941315, 0.20689, 0.015108) Norm(-0.46303, -0.659218, -0.592482) UV(0.380616, 0.022463)
|
||||
V 1: Pos(1.938853, 0.203385, 0.120071) Norm(-0.475339, -0.676743, 0.562202) UV(0.619384, 0.022463)
|
||||
V 2: Pos(1.919735, 0.312747, 0.120071) Norm(-0.676743, 0.475339, 0.562202) UV(0.619384, 0.261232)
|
||||
V 3: Pos(1.92324, 0.310285, 0.015108) Norm(-0.659218, 0.463029, -0.592482) UV(0.380616, 0.261232)
|
||||
V 4: Pos(2.029097, 0.331865, 0.120071) Norm(0.475339, 0.676744, 0.562202) UV(0.619384, 0.5)
|
||||
V 5: Pos(2.026635, 0.32836, 0.015108) Norm(0.46303, 0.659218, -0.592482) UV(0.380616, 0.5)
|
||||
V 6: Pos(2.048215, 0.222503, 0.120071) Norm(0.676743, -0.475339, 0.562202) UV(0.619384, 0.738768)
|
||||
V 7: Pos(2.04471, 0.224965, 0.015108) Norm(0.659218, -0.46303, -0.592482) UV(0.380616, 0.738768)
|
||||
V 8: Pos(1.938853, 0.203385, 0.120071) Norm(-0.475339, -0.676743, 0.562202) UV(0.619384, 0.977537)
|
||||
V 9: Pos(1.941315, 0.20689, 0.015108) Norm(-0.46303, -0.659218, -0.592482) UV(0.380616, 0.977537)
|
||||
V 10: Pos(1.92324, 0.310285, 0.015108) Norm(-0.659218, 0.463029, -0.592482) UV(0.141847, 0.5)
|
||||
V 11: Pos(1.941315, 0.20689, 0.015108) Norm(-0.46303, -0.659218, -0.592482) UV(0.141847, 0.738768)
|
||||
V 12: Pos(1.919735, 0.312747, 0.120071) Norm(-0.676743, 0.475339, 0.562202) UV(0.858153, 0.5)
|
||||
V 13: Pos(1.938853, 0.203385, 0.120071) Norm(-0.475339, -0.676743, 0.562202) UV(0.858153, 0.738768)
|
||||
|
||||
===Triangles (Indices): 12
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 3 2 4
|
||||
Tri: 3 4 5
|
||||
Tri: 5 4 6
|
||||
Tri: 5 6 7
|
||||
Tri: 7 6 8
|
||||
Tri: 7 8 9
|
||||
Tri: 10 5 7
|
||||
Tri: 10 7 11
|
||||
Tri: 4 12 13
|
||||
Tri: 4 13 6
|
||||
@@ -0,0 +1,52 @@
|
||||
===Vertices (Split by UV/Normal): 29
|
||||
V 0: Pos(2.144698, 1.006366, 0.014196) Norm(0.403294, 0.189282, -0.89528) UV(0.038866, 0.499333)
|
||||
V 1: Pos(2.153896, 1.009608, 0.025287) Norm(0.876115, 0.355937, -0.325162) UV(0.0229, 0.474324)
|
||||
V 2: Pos(2.179514, 0.863888, 0.224368) Norm(0.938676, -0.127483, 0.320367) UV(0.232621, 0.022338)
|
||||
V 3: Pos(2.16982, 0.849805, 0.224272) Norm(0.452238, -0.834195, 0.315593) UV(0.266966, 0.022338)
|
||||
V 4: Pos(2.025819, 0.823535, 0.02403) Norm(-0.017565, -0.942434, -0.333931) UV(0.476687, 0.474324)
|
||||
V 5: Pos(2.025633, 0.833386, 0.013027) Norm(-0.02715, -0.436065, -0.899505) UV(0.460721, 0.499333)
|
||||
V 6: Pos(2.145396, 1.015422, 0.030637) Norm(-0.022224, 0.970458, 0.240245) UV(0.930474, 0.535066)
|
||||
V 7: Pos(2.171014, 0.869703, 0.229717) Norm(0.054319, 0.477468, 0.876969) UV(0.930474, 0.036794)
|
||||
V 8: Pos(2.179514, 0.863888, 0.224368) Norm(0.938676, -0.127483, 0.320367) UV(0.953787, 0.036794)
|
||||
V 9: Pos(2.153896, 1.009608, 0.025287) Norm(0.876115, 0.355937, -0.325162) UV(0.953787, 0.535066)
|
||||
V 10: Pos(2.017133, 0.839201, 0.018377) Norm(-0.925489, 0.178454, -0.334101) UV(0.492653, 0.499333)
|
||||
V 11: Pos(2.017319, 0.829349, 0.02938) Norm(-0.915912, -0.327912, 0.231473) UV(0.476687, 0.474324)
|
||||
V 12: Pos(2.16132, 0.855619, 0.229622) Norm(-0.432118, -0.229245, 0.872193) UV(0.686408, 0.022338)
|
||||
V 13: Pos(2.171014, 0.869703, 0.229717) Norm(0.054319, 0.477468, 0.876969) UV(0.720754, 0.022338)
|
||||
V 14: Pos(2.145396, 1.015422, 0.030637) Norm(-0.022224, 0.970458, 0.240245) UV(0.930474, 0.474324)
|
||||
V 15: Pos(2.136198, 1.01218, 0.019545) Norm(-0.495043, 0.803811, -0.329878) UV(0.914509, 0.499333)
|
||||
V 16: Pos(2.025819, 0.823535, 0.02403) Norm(-0.017565, -0.942434, -0.333931) UV(0.953787, 0.520609)
|
||||
V 17: Pos(2.16982, 0.849805, 0.224272) Norm(0.452238, -0.834195, 0.315593) UV(0.953787, 0.022338)
|
||||
V 18: Pos(2.16132, 0.855619, 0.229622) Norm(-0.432118, -0.229245, 0.872193) UV(0.9771, 0.022338)
|
||||
V 19: Pos(2.017319, 0.829349, 0.02938) Norm(-0.915912, -0.327912, 0.231473) UV(0.9771, 0.520609)
|
||||
V 20: Pos(2.16132, 0.855619, 0.229622) Norm(-0.432118, -0.229245, 0.872193) UV(0.930474, 0.022338)
|
||||
V 21: Pos(2.017133, 0.839201, 0.018377) Norm(-0.925489, 0.178454, -0.334101) UV(0.046213, 0.515299)
|
||||
V 22: Pos(2.025633, 0.833386, 0.013027) Norm(-0.02715, -0.436065, -0.899505) UV(0.0229, 0.515299)
|
||||
V 23: Pos(2.025819, 0.823535, 0.02403) Norm(-0.017565, -0.942434, -0.333931) UV(0.0229, 0.499333)
|
||||
V 24: Pos(2.017319, 0.829349, 0.02938) Norm(-0.915912, -0.327912, 0.231473) UV(0.046213, 0.499333)
|
||||
V 25: Pos(2.144698, 1.006366, 0.014196) Norm(0.403294, 0.189282, -0.89528) UV(0.0229, 0.937155)
|
||||
V 26: Pos(2.136198, 1.01218, 0.019545) Norm(-0.495043, 0.803811, -0.329878) UV(0.046213, 0.937155)
|
||||
V 27: Pos(2.145396, 1.015422, 0.030637) Norm(-0.022224, 0.970458, 0.240245) UV(0.046213, 0.95312)
|
||||
V 28: Pos(2.153896, 1.009608, 0.025287) Norm(0.876115, 0.355937, -0.325162) UV(0.0229, 0.95312)
|
||||
|
||||
===Triangles (Indices): 20
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 0 3 4
|
||||
Tri: 0 4 5
|
||||
Tri: 6 7 8
|
||||
Tri: 6 8 9
|
||||
Tri: 10 11 12
|
||||
Tri: 10 12 13
|
||||
Tri: 10 13 14
|
||||
Tri: 10 14 15
|
||||
Tri: 16 17 18
|
||||
Tri: 16 18 19
|
||||
Tri: 7 20 17
|
||||
Tri: 7 17 8
|
||||
Tri: 21 22 23
|
||||
Tri: 21 23 24
|
||||
Tri: 25 26 27
|
||||
Tri: 25 27 28
|
||||
Tri: 21 26 25
|
||||
Tri: 21 25 22
|
||||
@@ -0,0 +1,423 @@
|
||||
===Vertices (Split by UV/Normal): 226
|
||||
V 0: Pos(-2.162589, -1.0, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.531013, 0.882126)
|
||||
V 1: Pos(-2.162589, -1.0, 1.296742) Norm(0.026636, -0.402662, -0.914961) UV(0.491477, 0.882126)
|
||||
V 2: Pos(-2.162589, 1.963949, 1.296742) Norm(0.026637, 0.402662, -0.914961) UV(0.491477, 0.666563)
|
||||
V 3: Pos(-2.162589, 1.963949, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.531013, 0.666563)
|
||||
V 4: Pos(2.283504, 1.963949, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.624115, 0.882126)
|
||||
V 5: Pos(2.283504, 1.963949, 1.296742) Norm(-0.005023, 0.39988, -0.916554) UV(0.584579, 0.882126)
|
||||
V 6: Pos(2.283504, -1.0, 1.296742) Norm(-0.005023, -0.39988, -0.916554) UV(0.584579, 0.666563)
|
||||
V 7: Pos(2.283504, -1.0, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.624115, 0.666563)
|
||||
V 8: Pos(-0.787029, -0.902648, 0.016616) Norm(0.408248, 0.408248, 0.816497) UV(0.254904, 0.57906)
|
||||
V 9: Pos(0.907943, -0.902648, 0.016616) Norm(-0.408248, 0.408248, 0.816497) UV(0.254904, 0.455791)
|
||||
V 10: Pos(0.907943, 1.866597, 0.016616) Norm(-0.408248, -0.408247, 0.816497) UV(0.456307, 0.455791)
|
||||
V 11: Pos(-0.787029, 1.866597, 0.016616) Norm(0.408248, -0.408249, 0.816496) UV(0.456307, 0.57906)
|
||||
V 12: Pos(-0.787029, 1.866597, 0.753136) Norm(0.301511, -0.904534, -0.301511) UV(0.737052, 0.235723)
|
||||
V 13: Pos(-0.787029, 1.866597, 1.820116) Norm(0.353009, -0.756776, -0.550158) UV(0.814652, 0.235723)
|
||||
V 14: Pos(-2.060281, 1.866597, 1.337887) Norm(0.65901, -0.598824, -0.4551) UV(0.77958, 0.328324)
|
||||
V 15: Pos(-2.060281, 1.866597, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.737052, 0.328324)
|
||||
V 16: Pos(-0.787029, -0.902648, 0.753136) Norm(0.301511, 0.904534, -0.301511) UV(0.737052, 0.420926)
|
||||
V 17: Pos(-0.787029, -0.902648, 0.016616) Norm(0.408248, 0.408248, 0.816497) UV(0.683486, 0.420926)
|
||||
V 18: Pos(-2.060281, -0.902648, 0.016616) Norm(0.57735, 0.57735, 0.57735) UV(0.683486, 0.328324)
|
||||
V 19: Pos(-2.060281, -0.902648, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.737052, 0.328324)
|
||||
V 20: Pos(2.181196, 1.866597, 0.016616) Norm(-0.57735, -0.57735, 0.57735) UV(0.456307, 0.363191)
|
||||
V 21: Pos(2.181196, -0.902648, 0.016616) Norm(-0.57735, 0.57735, 0.57735) UV(0.254904, 0.363191)
|
||||
V 22: Pos(0.907943, 1.866597, 0.016616) Norm(-0.408248, -0.408247, 0.816497) UV(0.243391, 0.416758)
|
||||
V 23: Pos(0.907943, 1.866597, 0.753136) Norm(-0.301512, -0.904534, -0.301512) UV(0.243391, 0.363192)
|
||||
V 24: Pos(0.907944, 1.963949, 0.753136) Norm(-0.301511, 0.904534, -0.301511) UV(0.250471, 0.363192)
|
||||
V 25: Pos(0.907944, 1.963949, 0.016616) Norm(-0.707106, 0.707108, -0.0) UV(0.250471, 0.416758)
|
||||
V 26: Pos(0.907944, 1.963949, 0.753136) Norm(-0.301511, 0.904534, -0.301511) UV(0.456307, 0.363191)
|
||||
V 27: Pos(0.907943, 1.866597, 0.753136) Norm(-0.301512, -0.904534, -0.301512) UV(0.463388, 0.363191)
|
||||
V 28: Pos(0.060457, 1.866597, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.463388, 0.424826)
|
||||
V 29: Pos(0.060458, 1.963949, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.456307, 0.424826)
|
||||
V 30: Pos(-2.060281, 1.866597, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.06086, 0.873065)
|
||||
V 31: Pos(-2.060281, 1.866597, 1.337887) Norm(0.65901, -0.598824, -0.4551) UV(0.018331, 0.873065)
|
||||
V 32: Pos(-2.060281, -0.902648, 1.337887) Norm(0.65901, 0.598824, -0.4551) UV(0.018331, 0.671662)
|
||||
V 33: Pos(-2.060281, -0.902648, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.06086, 0.671662)
|
||||
V 34: Pos(2.181196, 1.866597, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.737052, 0.019848)
|
||||
V 35: Pos(2.181196, 1.866597, 1.337887) Norm(-0.65901, -0.598824, -0.4551) UV(0.77958, 0.019848)
|
||||
V 36: Pos(0.907943, 1.866597, 1.820116) Norm(-0.353009, -0.756776, -0.550158) UV(0.814652, 0.11245)
|
||||
V 37: Pos(0.907943, 1.866597, 0.753136) Norm(-0.301512, -0.904534, -0.301512) UV(0.737052, 0.11245)
|
||||
V 38: Pos(2.181196, 1.866597, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.297432, 0.67166)
|
||||
V 39: Pos(2.181196, 1.866597, 0.016616) Norm(-0.57735, -0.57735, 0.57735) UV(0.350998, 0.67166)
|
||||
V 40: Pos(2.181196, -0.902648, 0.016616) Norm(-0.57735, 0.57735, 0.57735) UV(0.350998, 0.873063)
|
||||
V 41: Pos(2.181196, -0.902648, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.297432, 0.873063)
|
||||
V 42: Pos(2.181196, -0.902648, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.737052, 0.6368)
|
||||
V 43: Pos(2.181196, -0.902648, 0.016616) Norm(-0.57735, 0.57735, 0.57735) UV(0.683486, 0.6368)
|
||||
V 44: Pos(0.907943, -0.902648, 0.016616) Norm(-0.408248, 0.408248, 0.816497) UV(0.683486, 0.544199)
|
||||
V 45: Pos(0.907943, -0.902648, 0.753136) Norm(-0.301511, 0.904534, -0.301512) UV(0.737052, 0.544199)
|
||||
V 46: Pos(-2.162589, 1.963949, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.545043, 0.019848)
|
||||
V 47: Pos(-2.162589, 1.963949, 1.296742) Norm(0.026637, 0.402662, -0.914961) UV(0.584579, 0.019848)
|
||||
V 48: Pos(-0.787029, 1.963949, 1.820116) Norm(0.353547, 0.756586, -0.550075) UV(0.622643, 0.119891)
|
||||
V 49: Pos(-0.787028, 1.963949, 0.753136) Norm(0.301511, 0.904534, -0.301512) UV(0.545043, 0.119891)
|
||||
V 50: Pos(2.283504, -1.0, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.545043, 0.343206)
|
||||
V 51: Pos(2.283504, -1.0, 1.296742) Norm(-0.005023, -0.39988, -0.916554) UV(0.584579, 0.343206)
|
||||
V 52: Pos(0.907944, -1.0, 1.820116) Norm(-0.353547, -0.756586, -0.550075) UV(0.622643, 0.443248)
|
||||
V 53: Pos(0.907944, -1.0, 0.753136) Norm(-0.301511, -0.904534, -0.301511) UV(0.545043, 0.443248)
|
||||
V 54: Pos(-0.787029, 1.866597, 0.753136) Norm(0.301511, -0.904534, -0.301511) UV(0.463388, 0.486461)
|
||||
V 55: Pos(-0.787028, 1.963949, 0.753136) Norm(0.301511, 0.904534, -0.301512) UV(0.456307, 0.486461)
|
||||
V 56: Pos(-0.787028, -1.0, 0.753136) Norm(0.301511, -0.904534, -0.301512) UV(0.545043, 0.566521)
|
||||
V 57: Pos(-0.787028, -1.0, 1.820116) Norm(0.353547, -0.756586, -0.550075) UV(0.622643, 0.566521)
|
||||
V 58: Pos(-2.162589, -1.0, 1.296742) Norm(0.026636, -0.402662, -0.914961) UV(0.584579, 0.666563)
|
||||
V 59: Pos(-2.162589, -1.0, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.545043, 0.666563)
|
||||
V 60: Pos(0.907944, 1.963949, 0.753136) Norm(-0.301511, 0.904534, -0.301511) UV(0.545043, 0.243163)
|
||||
V 61: Pos(0.907943, 1.963949, 1.820116) Norm(-0.353548, 0.756586, -0.550075) UV(0.622643, 0.243163)
|
||||
V 62: Pos(2.283504, 1.963949, 1.296742) Norm(-0.005023, 0.39988, -0.916554) UV(0.584579, 0.343206)
|
||||
V 63: Pos(2.283504, 1.963949, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.545043, 0.343206)
|
||||
V 64: Pos(-0.787029, 1.866597, 0.753136) Norm(0.301511, -0.904534, -0.301511) UV(0.250471, 0.416758)
|
||||
V 65: Pos(-0.787029, 1.866597, 0.016616) Norm(0.408248, -0.408249, 0.816496) UV(0.250471, 0.470324)
|
||||
V 66: Pos(-0.787028, 1.963949, 0.016616) Norm(0.707107, 0.707106, 0.0) UV(0.243391, 0.470324)
|
||||
V 67: Pos(-0.787028, 1.963949, 0.753136) Norm(0.301511, 0.904534, -0.301512) UV(0.243391, 0.416758)
|
||||
V 68: Pos(0.060457, 1.866597, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.737052, 0.174086)
|
||||
V 69: Pos(0.060457, 1.866597, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.875495, 0.174086)
|
||||
V 70: Pos(0.907943, -0.902648, 0.753136) Norm(-0.301511, 0.904534, -0.301512) UV(0.470468, 0.363191)
|
||||
V 71: Pos(0.907943, -0.902648, 0.016616) Norm(-0.408248, 0.408248, 0.816497) UV(0.470468, 0.416757)
|
||||
V 72: Pos(0.907943, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.463388, 0.416757)
|
||||
V 73: Pos(0.907944, -1.0, 0.753136) Norm(-0.301511, -0.904534, -0.301511) UV(0.463388, 0.363191)
|
||||
V 74: Pos(2.283504, -1.146237, 1.410701) Norm(0.241334, -0.710196, 0.661346) UV(0.782394, 0.969614)
|
||||
V 75: Pos(2.283504, -1.146237, 1.296742) Norm(-0.243443, -0.704043, -0.667127) UV(0.790682, 0.969614)
|
||||
V 76: Pos(2.428398, -1.146237, 1.246102) Norm(0.478888, -0.561687, -0.674666) UV(0.794365, 0.980152)
|
||||
V 77: Pos(2.428398, -1.146237, 1.36006) Norm(0.654111, -0.597131, 0.464299) UV(0.786077, 0.980152)
|
||||
V 78: Pos(-2.162589, -1.0, 1.296742) Norm(0.026636, -0.402662, -0.914961) UV(0.244269, 0.353713)
|
||||
V 79: Pos(-0.787028, -1.0, 1.820116) Norm(0.353547, -0.756586, -0.550075) UV(0.244269, 0.253425)
|
||||
V 80: Pos(-0.787028, -1.146237, 1.820116) Norm(0.404514, -0.663521, -0.629372) UV(0.254904, 0.253425)
|
||||
V 81: Pos(-2.162589, -1.146237, 1.296742) Norm(0.253725, -0.70799, -0.65907) UV(0.254904, 0.353713)
|
||||
V 82: Pos(2.283504, -1.0, 1.410701) Norm(0.342802, 0.0, 0.939408) UV(0.26554, 0.03036)
|
||||
V 83: Pos(0.907944, -1.0, 1.934074) Norm(0.54068, 0.0, 0.841229) UV(0.26554, 0.130136)
|
||||
V 84: Pos(0.907944, -1.146237, 1.934074) Norm(0.353547, -0.756586, 0.550075) UV(0.254904, 0.130136)
|
||||
V 85: Pos(2.283504, -1.146237, 1.410701) Norm(0.241334, -0.710196, 0.661346) UV(0.254904, 0.03036)
|
||||
V 86: Pos(-0.787029, -0.902648, 0.016616) Norm(0.408248, 0.408248, 0.816497) UV(0.463388, 0.470323)
|
||||
V 87: Pos(-0.787029, -0.902648, 0.753136) Norm(0.301511, 0.904534, -0.301511) UV(0.463388, 0.416757)
|
||||
V 88: Pos(-0.787028, -1.0, 0.753136) Norm(0.301511, -0.904534, -0.301512) UV(0.470468, 0.416757)
|
||||
V 89: Pos(-0.787029, -1.0, 0.016616) Norm(0.707107, -0.707107, -0.0) UV(0.470468, 0.470323)
|
||||
V 90: Pos(0.060458, -0.902648, 2.656697) Norm(-0.0, 0.578056, -0.815997) UV(0.875495, 0.482562)
|
||||
V 91: Pos(0.060458, -0.902648, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.737052, 0.482562)
|
||||
V 92: Pos(-0.787029, -0.902648, 1.820116) Norm(0.353009, 0.756776, -0.550158) UV(0.814652, 0.420926)
|
||||
V 93: Pos(0.907944, -1.0, 0.753136) Norm(-0.301511, -0.904534, -0.301511) UV(0.243391, 0.363192)
|
||||
V 94: Pos(0.060458, -1.0, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.243391, 0.424828)
|
||||
V 95: Pos(0.060458, -0.902648, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.23631, 0.424828)
|
||||
V 96: Pos(0.907943, -0.902648, 0.753136) Norm(-0.301511, 0.904534, -0.301512) UV(0.23631, 0.363192)
|
||||
V 97: Pos(0.907944, -1.0, 1.820116) Norm(-0.353547, -0.756586, -0.550075) UV(0.244269, 0.130155)
|
||||
V 98: Pos(2.283504, -1.0, 1.296742) Norm(-0.005023, -0.39988, -0.916554) UV(0.244269, 0.030362)
|
||||
V 99: Pos(2.283504, -1.146237, 1.296742) Norm(-0.243443, -0.704043, -0.667127) UV(0.254904, 0.030362)
|
||||
V 100: Pos(0.907944, -1.146237, 1.820116) Norm(-0.404514, -0.663521, -0.629372) UV(0.254904, 0.130155)
|
||||
V 101: Pos(-2.292608, -1.146237, 1.246102) Norm(-0.467487, -0.560279, -0.683772) UV(0.254904, 0.363192)
|
||||
V 102: Pos(-2.292608, -1.0, 1.246102) Norm(-0.564391, 0.0, -0.825508) UV(0.244269, 0.363192)
|
||||
V 103: Pos(0.060458, -1.0, 2.770656) Norm(0.0, 0.0, 1.0) UV(0.26554, 0.191347)
|
||||
V 104: Pos(0.060458, -1.146237, 2.770656) Norm(0.0, -0.578056, 0.815997) UV(0.254904, 0.191347)
|
||||
V 105: Pos(-0.787028, -1.0, 1.934074) Norm(-0.54068, -0.0, 0.841229) UV(0.26554, 0.253405)
|
||||
V 106: Pos(-0.787028, -1.146237, 1.934074) Norm(-0.353547, -0.756586, 0.550075) UV(0.254904, 0.253405)
|
||||
V 107: Pos(-2.162589, 1.963949, 1.410701) Norm(-0.359271, -0.0, 0.933233) UV(0.481103, 0.35371)
|
||||
V 108: Pos(-0.787029, 1.963949, 1.934074) Norm(-0.54068, -0.0, 0.841228) UV(0.481103, 0.253405)
|
||||
V 109: Pos(-0.787029, 2.10659, 1.934074) Norm(-0.353547, 0.756586, 0.550075) UV(0.491477, 0.253405)
|
||||
V 110: Pos(-2.162589, 2.10659, 1.410701) Norm(-0.254359, 0.706225, 0.660717) UV(0.491477, 0.35371)
|
||||
V 111: Pos(-2.162589, -1.0, 1.410701) Norm(-0.359271, -0.0, 0.933233) UV(0.26554, 0.35371)
|
||||
V 112: Pos(-2.162589, -1.146237, 1.410701) Norm(-0.254359, -0.706225, 0.660717) UV(0.254904, 0.35371)
|
||||
V 113: Pos(2.283504, 1.963949, 1.410701) Norm(0.342802, 0.0, 0.939408) UV(0.481103, 0.03036)
|
||||
V 114: Pos(2.428398, -1.0, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.26554, 0.019848)
|
||||
V 115: Pos(2.428398, 1.963949, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.481103, 0.019848)
|
||||
V 116: Pos(-2.162589, 1.963949, 1.296742) Norm(0.026637, 0.402662, -0.914961) UV(0.028706, 0.353713)
|
||||
V 117: Pos(-2.292608, 1.963949, 1.246102) Norm(-0.56439, 0.0, -0.825508) UV(0.028706, 0.363192)
|
||||
V 118: Pos(0.060458, -1.0, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.244269, 0.191395)
|
||||
V 119: Pos(0.060458, -1.146237, 2.656697) Norm(0.0, -0.90301, -0.429619) UV(0.254904, 0.191395)
|
||||
V 120: Pos(0.060457, 1.963949, 2.656697) Norm(0.0, 0.578056, -0.815997) UV(0.028706, 0.191395)
|
||||
V 121: Pos(-0.787029, 1.963949, 1.820116) Norm(0.353547, 0.756586, -0.550075) UV(0.028706, 0.253425)
|
||||
V 122: Pos(-0.787029, 2.10659, 1.820116) Norm(0.404514, 0.663521, -0.629372) UV(0.018331, 0.253425)
|
||||
V 123: Pos(0.060457, 2.10659, 2.656697) Norm(0.0, 0.90301, -0.429619) UV(0.018331, 0.191395)
|
||||
V 124: Pos(0.907943, 1.963949, 1.820116) Norm(-0.353548, 0.756586, -0.550075) UV(0.028706, 0.130155)
|
||||
V 125: Pos(0.907943, 2.10659, 1.820116) Norm(-0.404514, 0.663521, -0.629372) UV(0.018331, 0.130155)
|
||||
V 126: Pos(0.907944, -1.146237, 1.820116) Norm(-0.404514, -0.663521, -0.629372) UV(0.752617, 0.869571)
|
||||
V 127: Pos(0.907944, -1.146237, 1.934074) Norm(0.353547, -0.756586, 0.550075) UV(0.74433, 0.869571)
|
||||
V 128: Pos(0.060457, 1.963949, 2.770656) Norm(-0.0, -0.0, 1.0) UV(0.481103, 0.191347)
|
||||
V 129: Pos(0.907943, 1.963949, 1.934074) Norm(0.54068, 0.0, 0.841228) UV(0.481103, 0.130136)
|
||||
V 130: Pos(-2.162589, -1.146237, 1.296742) Norm(0.253725, -0.70799, -0.65907) UV(0.790682, 0.646256)
|
||||
V 131: Pos(-0.787028, -1.146237, 1.820116) Norm(0.404514, -0.663521, -0.629372) UV(0.752618, 0.746299)
|
||||
V 132: Pos(-0.787028, -1.146237, 1.934074) Norm(-0.353547, -0.756586, 0.550075) UV(0.74433, 0.746299)
|
||||
V 133: Pos(-2.162589, -1.146237, 1.410701) Norm(-0.254359, -0.706225, 0.660717) UV(0.782394, 0.646256)
|
||||
V 134: Pos(2.181196, 1.866597, 1.337887) Norm(-0.65901, -0.598824, -0.4551) UV(0.018331, 0.363192)
|
||||
V 135: Pos(2.181196, -0.902648, 1.337887) Norm(-0.65901, 0.598824, -0.4551) UV(0.219734, 0.363192)
|
||||
V 136: Pos(0.907943, -0.902648, 1.820116) Norm(-0.353009, 0.756776, -0.550158) UV(0.219734, 0.455564)
|
||||
V 137: Pos(0.907943, 1.866597, 1.820116) Norm(-0.353009, -0.756776, -0.550158) UV(0.018331, 0.455564)
|
||||
V 138: Pos(-2.292608, 2.10659, 1.36006) Norm(-0.66075, 0.599445, 0.451747) UV(0.491477, 0.363191)
|
||||
V 139: Pos(-2.292608, 1.963949, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.481103, 0.363191)
|
||||
V 140: Pos(0.060458, -1.146237, 2.656697) Norm(0.0, -0.90301, -0.429619) UV(0.691774, 0.807935)
|
||||
V 141: Pos(0.060458, -1.146237, 2.770656) Norm(0.0, -0.578056, 0.815997) UV(0.683486, 0.807935)
|
||||
V 142: Pos(0.060458, -0.902648, 2.656697) Norm(-0.0, 0.578056, -0.815997) UV(0.219734, 0.516804)
|
||||
V 143: Pos(0.060457, 1.866597, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.018331, 0.516804)
|
||||
V 144: Pos(-0.787029, -0.902648, 1.820116) Norm(0.353009, 0.756776, -0.550158) UV(0.219734, 0.578834)
|
||||
V 145: Pos(-0.787029, 1.866597, 1.820116) Norm(0.353009, -0.756776, -0.550158) UV(0.018331, 0.578834)
|
||||
V 146: Pos(-2.060281, 1.866597, 1.337887) Norm(0.65901, -0.598824, -0.4551) UV(0.018331, 0.671662)
|
||||
V 147: Pos(-2.060281, -0.902648, 1.337887) Norm(0.65901, 0.598824, -0.4551) UV(0.219734, 0.671662)
|
||||
V 148: Pos(2.283504, 2.10659, 1.296742) Norm(-0.243443, 0.704043, -0.667127) UV(0.90156, 0.647338)
|
||||
V 149: Pos(0.907943, 2.10659, 1.820116) Norm(-0.404514, 0.663521, -0.629372) UV(0.863496, 0.74738)
|
||||
V 150: Pos(0.907943, 2.10659, 1.934074) Norm(0.353547, 0.756586, 0.550075) UV(0.855208, 0.74738)
|
||||
V 151: Pos(2.283504, 2.10659, 1.410701) Norm(0.241334, 0.710196, 0.661346) UV(0.893272, 0.647338)
|
||||
V 152: Pos(-0.787029, 2.10659, 1.820116) Norm(0.404514, 0.663521, -0.629372) UV(0.863496, 0.870653)
|
||||
V 153: Pos(-2.162589, 2.10659, 1.296742) Norm(0.253725, 0.70799, -0.65907) UV(0.90156, 0.970695)
|
||||
V 154: Pos(-2.162589, 2.10659, 1.410701) Norm(-0.254359, 0.706225, 0.660717) UV(0.893272, 0.970695)
|
||||
V 155: Pos(-0.787029, 2.10659, 1.934074) Norm(-0.353547, 0.756586, 0.550075) UV(0.855208, 0.870653)
|
||||
V 156: Pos(0.060457, 2.10659, 2.656697) Norm(0.0, 0.90301, -0.429619) UV(0.802653, 0.809017)
|
||||
V 157: Pos(0.060457, 2.10659, 2.770656) Norm(0.0, 0.578056, 0.815997) UV(0.794365, 0.809017)
|
||||
V 158: Pos(-2.162589, 2.10659, 1.296742) Norm(0.253725, 0.70799, -0.65907) UV(0.018331, 0.353713)
|
||||
V 159: Pos(0.907943, 2.10659, 1.934074) Norm(0.353547, 0.756586, 0.550075) UV(0.491477, 0.130136)
|
||||
V 160: Pos(0.060457, 2.10659, 2.770656) Norm(0.0, 0.578056, 0.815997) UV(0.491477, 0.191347)
|
||||
V 161: Pos(2.283504, 1.963949, 1.296742) Norm(-0.005023, 0.39988, -0.916554) UV(0.028706, 0.030362)
|
||||
V 162: Pos(2.283504, 2.10659, 1.296742) Norm(-0.243443, 0.704043, -0.667127) UV(0.018331, 0.030362)
|
||||
V 163: Pos(2.283504, 2.10659, 1.410701) Norm(0.241334, 0.710196, 0.661346) UV(0.491477, 0.03036)
|
||||
V 164: Pos(2.428398, 2.10659, 1.36006) Norm(0.654111, 0.597131, 0.464299) UV(0.896955, 0.6368)
|
||||
V 165: Pos(2.428398, 2.10659, 1.246102) Norm(0.478889, 0.561687, -0.674666) UV(0.905243, 0.6368)
|
||||
V 166: Pos(2.428398, -1.0, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.23631, 0.373828)
|
||||
V 167: Pos(2.428398, -1.0, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.228022, 0.373828)
|
||||
V 168: Pos(2.428398, -1.146237, 1.36006) Norm(0.654111, -0.597131, 0.464299) UV(0.228022, 0.363192)
|
||||
V 169: Pos(2.428398, -1.146237, 1.246102) Norm(0.478888, -0.561687, -0.674666) UV(0.23631, 0.363192)
|
||||
V 170: Pos(2.428398, 1.963949, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.23631, 0.589391)
|
||||
V 171: Pos(2.428398, 1.963949, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.228022, 0.589391)
|
||||
V 172: Pos(2.428398, 2.10659, 1.246102) Norm(0.478889, 0.561687, -0.674666) UV(0.23631, 0.599765)
|
||||
V 173: Pos(2.428398, 2.10659, 1.36006) Norm(0.654111, 0.597131, 0.464299) UV(0.228022, 0.599765)
|
||||
V 174: Pos(2.428398, -1.0, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.244269, 0.019848)
|
||||
V 175: Pos(2.428398, -1.146237, 1.246102) Norm(0.478888, -0.561687, -0.674666) UV(0.254904, 0.019848)
|
||||
V 176: Pos(2.428398, 2.10659, 1.246102) Norm(0.478889, 0.561687, -0.674666) UV(0.018331, 0.019848)
|
||||
V 177: Pos(2.428398, 1.963949, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.028706, 0.019848)
|
||||
V 178: Pos(2.428398, 2.10659, 1.36006) Norm(0.654111, 0.597131, 0.464299) UV(0.491477, 0.019848)
|
||||
V 179: Pos(2.428398, -1.146237, 1.36006) Norm(0.654111, -0.597131, 0.464299) UV(0.254904, 0.019848)
|
||||
V 180: Pos(-2.292608, 1.963949, 1.246102) Norm(-0.56439, 0.0, -0.825508) UV(0.228022, 0.373567)
|
||||
V 181: Pos(-2.292608, -1.0, 1.246102) Norm(-0.564391, 0.0, -0.825508) UV(0.228022, 0.58913)
|
||||
V 182: Pos(-2.292608, -1.0, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.219734, 0.58913)
|
||||
V 183: Pos(-2.292608, 1.963949, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.219734, 0.373567)
|
||||
V 184: Pos(-2.292608, 2.10659, 1.36006) Norm(-0.66075, 0.599445, 0.451747) UV(0.219734, 0.363192)
|
||||
V 185: Pos(-2.292608, 2.10659, 1.246102) Norm(-0.467487, 0.560279, -0.683772) UV(0.228022, 0.363192)
|
||||
V 186: Pos(-2.292608, -1.146237, 1.246102) Norm(-0.467487, -0.560279, -0.683772) UV(0.228022, 0.599765)
|
||||
V 187: Pos(-2.292608, -1.146237, 1.36006) Norm(-0.66075, -0.599445, 0.451747) UV(0.219734, 0.599765)
|
||||
V 188: Pos(-2.292608, 2.10659, 1.246102) Norm(-0.467487, 0.560279, -0.683772) UV(0.905243, 0.980152)
|
||||
V 189: Pos(-2.292608, 2.10659, 1.36006) Norm(-0.66075, 0.599445, 0.451747) UV(0.896955, 0.980152)
|
||||
V 190: Pos(-2.292608, -1.146237, 1.36006) Norm(-0.66075, -0.599445, 0.451747) UV(0.786077, 0.6368)
|
||||
V 191: Pos(-2.292608, -1.146237, 1.246102) Norm(-0.467487, -0.560279, -0.683772) UV(0.794365, 0.6368)
|
||||
V 192: Pos(-2.292608, 2.10659, 1.246102) Norm(-0.467487, 0.560279, -0.683772) UV(0.018331, 0.363192)
|
||||
V 193: Pos(-2.292608, -1.0, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.26554, 0.363191)
|
||||
V 194: Pos(-2.292608, -1.146237, 1.36006) Norm(-0.66075, -0.599445, 0.451747) UV(0.254904, 0.363191)
|
||||
V 195: Pos(2.181196, -0.902648, 1.337887) Norm(-0.65901, 0.598824, -0.4551) UV(0.77958, 0.6368)
|
||||
V 196: Pos(0.907943, -0.902648, 1.820116) Norm(-0.353009, 0.756776, -0.550158) UV(0.814652, 0.544199)
|
||||
V 197: Pos(2.181196, 1.866597, 1.337887) Norm(-0.65901, -0.598824, -0.4551) UV(0.254904, 0.67166)
|
||||
V 198: Pos(2.181196, -0.902648, 1.337887) Norm(-0.65901, 0.598824, -0.4551) UV(0.254904, 0.873063)
|
||||
V 199: Pos(2.181196, 1.866597, 0.016616) Norm(-0.57735, -0.57735, 0.57735) UV(0.683486, 0.019848)
|
||||
V 200: Pos(0.907943, 1.866597, 0.016616) Norm(-0.408248, -0.408247, 0.816497) UV(0.683486, 0.11245)
|
||||
V 201: Pos(0.060458, 1.963949, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.545043, 0.181527)
|
||||
V 202: Pos(0.060457, 1.963949, 2.656697) Norm(0.0, 0.578056, -0.815997) UV(0.683486, 0.181527)
|
||||
V 203: Pos(0.060458, -1.0, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.545043, 0.504884)
|
||||
V 204: Pos(0.060458, -1.0, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.683486, 0.504884)
|
||||
V 205: Pos(0.907944, 1.963949, 0.016616) Norm(-0.707106, 0.707108, -0.0) UV(0.491477, 0.243163)
|
||||
V 206: Pos(2.283504, 1.963949, 0.016616) Norm(0.707107, 0.707107, 0.0) UV(0.491477, 0.343206)
|
||||
V 207: Pos(-0.787029, -1.0, 0.016616) Norm(0.707107, -0.707107, -0.0) UV(0.491477, 0.566521)
|
||||
V 208: Pos(-2.162589, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.491477, 0.666563)
|
||||
V 209: Pos(2.283504, -1.0, 0.016616) Norm(0.707107, -0.707107, 0.0) UV(0.491477, 0.343206)
|
||||
V 210: Pos(0.907943, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.491477, 0.443248)
|
||||
V 211: Pos(-2.162589, 1.963949, 0.016616) Norm(-0.707107, 0.707107, 0.0) UV(0.491477, 0.019848)
|
||||
V 212: Pos(-0.787028, 1.963949, 0.016616) Norm(0.707107, 0.707106, 0.0) UV(0.491477, 0.119891)
|
||||
V 213: Pos(2.283504, 1.963949, 0.016616) Norm(0.707107, 0.707107, 0.0) UV(0.677681, 0.882126)
|
||||
V 214: Pos(2.283504, -1.0, 0.016616) Norm(0.707107, -0.707107, 0.0) UV(0.677681, 0.666563)
|
||||
V 215: Pos(-2.162589, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.584579, 0.882126)
|
||||
V 216: Pos(-2.162589, 1.963949, 0.016616) Norm(-0.707107, 0.707107, 0.0) UV(0.584579, 0.666563)
|
||||
V 217: Pos(-2.060281, 1.866597, 0.016616) Norm(0.57735, -0.57735, 0.57735) UV(0.114425, 0.873065)
|
||||
V 218: Pos(-2.060281, -0.902648, 0.016616) Norm(0.57735, 0.57735, 0.57735) UV(0.114426, 0.671662)
|
||||
V 219: Pos(-2.060281, -0.902648, 1.337887) Norm(0.65901, 0.598824, -0.4551) UV(0.77958, 0.328324)
|
||||
V 220: Pos(-0.787029, 1.866597, 0.016616) Norm(0.408248, -0.408249, 0.816496) UV(0.683486, 0.235723)
|
||||
V 221: Pos(-2.060281, 1.866597, 0.016616) Norm(0.57735, -0.57735, 0.57735) UV(0.683486, 0.328324)
|
||||
V 222: Pos(-0.787028, -1.0, 0.753136) Norm(0.301511, -0.904534, -0.301512) UV(0.243391, 0.486463)
|
||||
V 223: Pos(-0.787029, -0.902648, 0.753136) Norm(0.301511, 0.904534, -0.301511) UV(0.23631, 0.486463)
|
||||
V 224: Pos(-2.060281, -0.902648, 0.016616) Norm(0.57735, 0.57735, 0.57735) UV(0.254904, 0.67166)
|
||||
V 225: Pos(-2.060281, 1.866597, 0.016616) Norm(0.57735, -0.57735, 0.57735) UV(0.456307, 0.67166)
|
||||
|
||||
===Triangles (Indices): 194
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 8 9 10
|
||||
Tri: 8 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 12 14 15
|
||||
Tri: 16 17 18
|
||||
Tri: 16 18 19
|
||||
Tri: 20 10 9
|
||||
Tri: 20 9 21
|
||||
Tri: 22 23 24
|
||||
Tri: 22 24 25
|
||||
Tri: 26 27 28
|
||||
Tri: 26 28 29
|
||||
Tri: 30 31 32
|
||||
Tri: 30 32 33
|
||||
Tri: 34 35 36
|
||||
Tri: 34 36 37
|
||||
Tri: 38 39 40
|
||||
Tri: 38 40 41
|
||||
Tri: 42 43 44
|
||||
Tri: 42 44 45
|
||||
Tri: 46 47 48
|
||||
Tri: 46 48 49
|
||||
Tri: 50 51 52
|
||||
Tri: 50 52 53
|
||||
Tri: 29 28 54
|
||||
Tri: 29 54 55
|
||||
Tri: 56 57 58
|
||||
Tri: 56 58 59
|
||||
Tri: 60 61 62
|
||||
Tri: 60 62 63
|
||||
Tri: 64 65 66
|
||||
Tri: 64 66 67
|
||||
Tri: 13 12 68
|
||||
Tri: 13 68 69
|
||||
Tri: 69 68 37
|
||||
Tri: 69 37 36
|
||||
Tri: 70 71 72
|
||||
Tri: 70 72 73
|
||||
Tri: 74 75 76
|
||||
Tri: 74 76 77
|
||||
Tri: 78 79 80
|
||||
Tri: 78 80 81
|
||||
Tri: 82 83 84
|
||||
Tri: 82 84 85
|
||||
Tri: 86 87 88
|
||||
Tri: 86 88 89
|
||||
Tri: 90 91 16
|
||||
Tri: 90 16 92
|
||||
Tri: 93 94 95
|
||||
Tri: 93 95 96
|
||||
Tri: 97 98 99
|
||||
Tri: 97 99 100
|
||||
Tri: 78 81 101
|
||||
Tri: 78 101 102
|
||||
Tri: 83 103 104
|
||||
Tri: 83 104 84
|
||||
Tri: 103 105 106
|
||||
Tri: 103 106 104
|
||||
Tri: 107 108 109
|
||||
Tri: 107 109 110
|
||||
Tri: 105 111 112
|
||||
Tri: 105 112 106
|
||||
Tri: 113 82 114
|
||||
Tri: 113 114 115
|
||||
Tri: 116 78 102
|
||||
Tri: 116 102 117
|
||||
Tri: 118 97 100
|
||||
Tri: 118 100 119
|
||||
Tri: 120 121 122
|
||||
Tri: 120 122 123
|
||||
Tri: 124 120 123
|
||||
Tri: 124 123 125
|
||||
Tri: 126 75 74
|
||||
Tri: 126 74 127
|
||||
Tri: 103 128 108
|
||||
Tri: 103 108 105
|
||||
Tri: 128 103 83
|
||||
Tri: 128 83 129
|
||||
Tri: 130 131 132
|
||||
Tri: 130 132 133
|
||||
Tri: 111 105 108
|
||||
Tri: 111 108 107
|
||||
Tri: 134 135 136
|
||||
Tri: 134 136 137
|
||||
Tri: 79 118 119
|
||||
Tri: 79 119 80
|
||||
Tri: 107 110 138
|
||||
Tri: 107 138 139
|
||||
Tri: 140 126 127
|
||||
Tri: 140 127 141
|
||||
Tri: 131 140 141
|
||||
Tri: 131 141 132
|
||||
Tri: 142 143 137
|
||||
Tri: 142 137 136
|
||||
Tri: 143 142 144
|
||||
Tri: 143 144 145
|
||||
Tri: 146 145 144
|
||||
Tri: 146 144 147
|
||||
Tri: 113 129 83
|
||||
Tri: 113 83 82
|
||||
Tri: 148 149 150
|
||||
Tri: 148 150 151
|
||||
Tri: 152 153 154
|
||||
Tri: 152 154 155
|
||||
Tri: 156 152 155
|
||||
Tri: 156 155 157
|
||||
Tri: 149 156 157
|
||||
Tri: 149 157 150
|
||||
Tri: 121 116 158
|
||||
Tri: 121 158 122
|
||||
Tri: 128 129 159
|
||||
Tri: 128 159 160
|
||||
Tri: 161 124 125
|
||||
Tri: 161 125 162
|
||||
Tri: 129 113 163
|
||||
Tri: 129 163 159
|
||||
Tri: 148 151 164
|
||||
Tri: 148 164 165
|
||||
Tri: 108 128 160
|
||||
Tri: 108 160 109
|
||||
Tri: 166 167 168
|
||||
Tri: 166 168 169
|
||||
Tri: 166 170 171
|
||||
Tri: 166 171 167
|
||||
Tri: 171 170 172
|
||||
Tri: 171 172 173
|
||||
Tri: 99 98 174
|
||||
Tri: 99 174 175
|
||||
Tri: 161 162 176
|
||||
Tri: 161 176 177
|
||||
Tri: 98 161 177
|
||||
Tri: 98 177 174
|
||||
Tri: 163 113 115
|
||||
Tri: 163 115 178
|
||||
Tri: 82 85 179
|
||||
Tri: 82 179 114
|
||||
Tri: 180 181 182
|
||||
Tri: 180 182 183
|
||||
Tri: 180 183 184
|
||||
Tri: 180 184 185
|
||||
Tri: 182 181 186
|
||||
Tri: 182 186 187
|
||||
Tri: 154 153 188
|
||||
Tri: 154 188 189
|
||||
Tri: 130 133 190
|
||||
Tri: 130 190 191
|
||||
Tri: 158 116 117
|
||||
Tri: 158 117 192
|
||||
Tri: 112 111 193
|
||||
Tri: 112 193 194
|
||||
Tri: 111 107 139
|
||||
Tri: 111 139 193
|
||||
Tri: 195 42 45
|
||||
Tri: 195 45 196
|
||||
Tri: 197 38 41
|
||||
Tri: 197 41 198
|
||||
Tri: 199 34 37
|
||||
Tri: 199 37 200
|
||||
Tri: 61 60 201
|
||||
Tri: 61 201 202
|
||||
Tri: 57 56 203
|
||||
Tri: 57 203 204
|
||||
Tri: 202 201 49
|
||||
Tri: 202 49 48
|
||||
Tri: 204 203 53
|
||||
Tri: 204 53 52
|
||||
Tri: 205 60 63
|
||||
Tri: 205 63 206
|
||||
Tri: 207 56 59
|
||||
Tri: 207 59 208
|
||||
Tri: 209 50 53
|
||||
Tri: 209 53 210
|
||||
Tri: 211 46 49
|
||||
Tri: 211 49 212
|
||||
Tri: 213 4 7
|
||||
Tri: 213 7 214
|
||||
Tri: 215 0 3
|
||||
Tri: 215 3 216
|
||||
Tri: 217 30 33
|
||||
Tri: 217 33 218
|
||||
Tri: 92 16 19
|
||||
Tri: 92 19 219
|
||||
Tri: 220 12 15
|
||||
Tri: 220 15 221
|
||||
Tri: 45 91 90
|
||||
Tri: 45 90 196
|
||||
Tri: 222 223 95
|
||||
Tri: 222 95 94
|
||||
Tri: 224 8 11
|
||||
Tri: 224 11 225
|
||||
@@ -0,0 +1,587 @@
|
||||
===Vertices (Split by UV/Normal): 308
|
||||
V 0: Pos(1.869281, 0.711913, 0.20808) Norm(-0.761914, -0.471586, 0.443953) UV(0.940254, 0.275626)
|
||||
V 1: Pos(1.909114, 0.661025, 0.20808) Norm(-0.635819, -0.635639, 0.437832) UV(0.941821, 0.050079)
|
||||
V 2: Pos(1.911422, 0.662832, 0.209918) Norm(-0.411643, 0.156254, 0.89785) UV(0.952296, 0.050152)
|
||||
V 3: Pos(1.87159, 0.71372, 0.209918) Norm(0.05145, -0.345966, 0.936835) UV(0.950729, 0.275699)
|
||||
V 4: Pos(1.913993, 0.664845, 0.208771) Norm(0.18131, 0.807914, 0.560716) UV(0.963526, 0.05023)
|
||||
V 5: Pos(1.874161, 0.715733, 0.208771) Norm(0.765568, 0.102584, 0.635124) UV(0.961959, 0.275777)
|
||||
V 6: Pos(1.874161, 0.715733, 0.208771) Norm(0.765568, 0.102584, 0.635124) UV(0.570192, 0.276899)
|
||||
V 7: Pos(1.913993, 0.664845, 0.208771) Norm(0.18131, 0.807914, 0.560716) UV(0.570192, 0.054444)
|
||||
V 8: Pos(1.914891, 0.665547, 0.205504) Norm(0.67715, 0.693757, -0.245293) UV(0.582143, 0.054731)
|
||||
V 9: Pos(1.875058, 0.716435, 0.205504) Norm(0.834547, 0.507059, -0.21546) UV(0.582143, 0.277186)
|
||||
V 10: Pos(1.913438, 0.66441, 0.202576) Norm(0.516638, 0.080688, -0.852393) UV(0.590955, 0.053367)
|
||||
V 11: Pos(1.873606, 0.715298, 0.202576) Norm(0.21114, 0.469699, -0.857206) UV(0.590955, 0.275822)
|
||||
V 12: Pos(1.873606, 0.715298, 0.202576) Norm(0.21114, 0.469699, -0.857206) UV(0.534956, 0.846336)
|
||||
V 13: Pos(1.913438, 0.66441, 0.202576) Norm(0.516638, 0.080688, -0.852393) UV(0.534956, 0.620784)
|
||||
V 14: Pos(1.91073, 0.662291, 0.202192) Norm(-0.030373, -0.478808, -0.877394) UV(0.546978, 0.620784)
|
||||
V 15: Pos(1.870898, 0.713179, 0.202192) Norm(-0.465715, 0.071095, -0.882074) UV(0.546978, 0.846336)
|
||||
V 16: Pos(1.872261, 0.722571, 0.208011) Norm(0.161414, 0.736744, -0.656623) UV(0.574598, 0.300921)
|
||||
V 17: Pos(1.874394, 0.719846, 0.208011) Norm(0.875251, 0.274505, -0.398224) UV(0.574598, 0.28901)
|
||||
V 18: Pos(1.908806, 0.660784, 0.204642) Norm(-0.491082, -0.800738, -0.343011) UV(0.555378, 0.620784)
|
||||
V 19: Pos(1.868973, 0.711672, 0.204642) Norm(-0.888391, -0.298097, -0.349142) UV(0.555378, 0.846336)
|
||||
V 20: Pos(1.868973, 0.711672, 0.204642) Norm(-0.888391, -0.298097, -0.349142) UV(0.064087, 0.044995)
|
||||
V 21: Pos(1.908806, 0.660784, 0.204642) Norm(-0.491082, -0.800738, -0.343011) UV(0.288741, 0.035517)
|
||||
V 22: Pos(1.909114, 0.661025, 0.20808) Norm(-0.635819, -0.635639, 0.437832) UV(0.289208, 0.047573)
|
||||
V 23: Pos(1.869281, 0.711913, 0.20808) Norm(-0.761914, -0.471586, 0.443953) UV(0.064555, 0.057052)
|
||||
V 24: Pos(1.869128, 0.722859, 0.209453) Norm(-0.579803, 0.709466, -0.400608) UV(0.530635, 0.876748)
|
||||
V 25: Pos(1.872261, 0.722571, 0.208011) Norm(0.161414, 0.736744, -0.656623) UV(0.522723, 0.869218)
|
||||
V 26: Pos(1.869128, 0.722859, 0.209453) Norm(-0.579803, 0.709466, -0.400608) UV(0.881676, 0.224478)
|
||||
V 27: Pos(1.867355, 0.720493, 0.211251) Norm(-0.978626, 0.203528, 0.029447) UV(0.893616, 0.224476)
|
||||
V 28: Pos(1.905059, 0.750005, 0.287268) Norm(-0.726396, 0.413925, 0.548648) UV(0.893569, 0.538039)
|
||||
V 29: Pos(1.906832, 0.752371, 0.28547) Norm(-0.303191, 0.93752, 0.17068) UV(0.88163, 0.538041)
|
||||
V 30: Pos(1.912745, 0.662505, 0.211251) Norm(-0.768111, 0.333689, 0.546496) UV(0.292472, 0.059404)
|
||||
V 31: Pos(1.913666, 0.659267, 0.212051) Norm(-0.641122, -0.621838, 0.449755) UV(0.303777, 0.061406)
|
||||
V 32: Pos(1.95137, 0.688779, 0.288069) Norm(-0.567274, -0.561274, 0.602638) UV(0.303777, 0.337972)
|
||||
V 33: Pos(1.950449, 0.692017, 0.287268) Norm(-0.977416, 0.171509, 0.123465) UV(0.292472, 0.33597)
|
||||
V 34: Pos(1.871199, 0.715583, 0.211251) Norm(0.121887, -0.767984, 0.628763) UV(0.953797, 0.281682)
|
||||
V 35: Pos(1.873921, 0.716736, 0.209453) Norm(0.873273, -0.419594, 0.247661) UV(0.963526, 0.279062)
|
||||
V 36: Pos(1.873921, 0.716736, 0.209453) Norm(0.873273, -0.419594, 0.247661) UV(0.568141, 0.280446)
|
||||
V 37: Pos(1.870898, 0.713179, 0.202192) Norm(-0.465715, 0.071095, -0.882074) UV(0.883904, 0.194467)
|
||||
V 38: Pos(1.868973, 0.711672, 0.204642) Norm(-0.888391, -0.298097, -0.349142) UV(0.895647, 0.197158)
|
||||
V 39: Pos(1.868277, 0.717255, 0.212051) Norm(-0.764855, -0.469795, 0.440783) UV(0.047779, 0.072207)
|
||||
V 40: Pos(1.867355, 0.720493, 0.211251) Norm(-0.978626, 0.203528, 0.029447) UV(0.036474, 0.070205)
|
||||
V 41: Pos(1.868277, 0.717255, 0.212051) Norm(-0.764855, -0.469795, 0.440783) UV(0.9494, 0.292533)
|
||||
V 42: Pos(1.913993, 0.664845, 0.208771) Norm(0.18131, 0.807914, 0.560716) UV(0.928243, 0.043066)
|
||||
V 43: Pos(1.914518, 0.66487, 0.209453) Norm(-0.305786, 0.941985, 0.138418) UV(0.928315, 0.045875)
|
||||
V 44: Pos(1.917651, 0.664582, 0.208011) Norm(0.44499, 0.797325, -0.407746) UV(0.919448, 0.045841)
|
||||
V 45: Pos(1.914891, 0.665547, 0.205504) Norm(0.67715, 0.693757, -0.245293) UV(0.91884, 0.035517)
|
||||
V 46: Pos(1.917651, 0.664582, 0.208011) Norm(0.44499, 0.797325, -0.407746) UV(0.574598, 0.047428)
|
||||
V 47: Pos(1.919784, 0.661858, 0.208011) Norm(0.764109, -0.017582, -0.644848) UV(0.574598, 0.035517)
|
||||
V 48: Pos(1.919784, 0.661858, 0.208011) Norm(0.764109, -0.017582, -0.644848) UV(0.522723, 0.600118)
|
||||
V 49: Pos(1.919311, 0.658748, 0.209453) Norm(0.559032, -0.73323, -0.387114) UV(0.530635, 0.592588)
|
||||
V 50: Pos(1.91073, 0.662291, 0.202192) Norm(-0.030373, -0.478808, -0.877394) UV(0.907113, 0.412291)
|
||||
V 51: Pos(1.919311, 0.658748, 0.209453) Norm(0.559032, -0.73323, -0.387114) UV(0.90461, 0.38031)
|
||||
V 52: Pos(1.916588, 0.657595, 0.211251) Norm(-0.026527, -0.998769, 0.041913) UV(0.916575, 0.380425)
|
||||
V 53: Pos(1.908806, 0.660784, 0.204642) Norm(-0.491082, -0.800738, -0.343011) UV(0.91884, 0.409524)
|
||||
V 54: Pos(1.916588, 0.657595, 0.211251) Norm(-0.026527, -0.998769, 0.041913) UV(0.314148, 0.058489)
|
||||
V 55: Pos(1.913666, 0.659267, 0.212051) Norm(-0.641122, -0.621838, 0.449755) UV(0.951185, 0.035517)
|
||||
V 56: Pos(1.912745, 0.662505, 0.211251) Norm(-0.768111, 0.333689, 0.546496) UV(0.955431, 0.046428)
|
||||
V 57: Pos(1.911422, 0.662832, 0.209918) Norm(-0.411643, 0.156254, 0.89785) UV(0.939991, 0.040403)
|
||||
V 58: Pos(1.912745, 0.662505, 0.211251) Norm(-0.768111, 0.333689, 0.546496) UV(0.940254, 0.045895)
|
||||
V 59: Pos(1.950787, 0.694725, 0.293158) Norm(-0.542765, -0.570773, 0.616137) UV(0.286249, 0.35723)
|
||||
V 60: Pos(1.950479, 0.694484, 0.28972) Norm(-0.94827, -0.260746, -0.181095) UV(0.285781, 0.345173)
|
||||
V 61: Pos(1.950449, 0.692017, 0.287268) Norm(-0.977416, 0.171509, 0.123465) UV(0.939646, 0.359457)
|
||||
V 62: Pos(1.950479, 0.694484, 0.28972) Norm(-0.94827, -0.260746, -0.181095) UV(0.939135, 0.369584)
|
||||
V 63: Pos(1.952403, 0.695991, 0.287271) Norm(-0.504568, 0.268536, -0.820548) UV(0.927398, 0.366872)
|
||||
V 64: Pos(1.952222, 0.694382, 0.28547) Norm(-0.556023, 0.743669, -0.371209) UV(0.927707, 0.359437)
|
||||
V 65: Pos(1.872261, 0.722571, 0.208011) Norm(0.161414, 0.736744, -0.656623) UV(0.87281, 0.22446)
|
||||
V 66: Pos(1.909964, 0.752083, 0.284029) Norm(0.355973, 0.897102, -0.261709) UV(0.872763, 0.538023)
|
||||
V 67: Pos(1.955354, 0.694095, 0.284029) Norm(0.353521, 0.733298, -0.580772) UV(0.91884, 0.359403)
|
||||
V 68: Pos(1.909964, 0.752083, 0.284029) Norm(0.355973, 0.897102, -0.261709) UV(0.345823, 0.336334)
|
||||
V 69: Pos(1.912097, 0.749358, 0.284029) Norm(0.796741, 0.167077, -0.580765) UV(0.345823, 0.324423)
|
||||
V 70: Pos(1.955354, 0.694095, 0.284029) Norm(0.353521, 0.733298, -0.580772) UV(0.345823, 0.082841)
|
||||
V 71: Pos(1.957487, 0.69137, 0.284029) Norm(0.956337, 0.1301, -0.261713) UV(0.345823, 0.070929)
|
||||
V 72: Pos(1.873921, 0.716736, 0.209453) Norm(0.873273, -0.419594, 0.247661) UV(0.948579, 0.920279)
|
||||
V 73: Pos(1.874394, 0.719846, 0.208011) Norm(0.875251, 0.274505, -0.398224) UV(0.939834, 0.920881)
|
||||
V 74: Pos(1.912097, 0.749358, 0.284029) Norm(0.796741, 0.167077, -0.580765) UV(0.940802, 0.608213)
|
||||
V 75: Pos(1.911624, 0.746248, 0.28547) Norm(0.588348, -0.71835, -0.37124) UV(0.949547, 0.607611)
|
||||
V 76: Pos(1.919784, 0.661858, 0.208011) Norm(0.764109, -0.017582, -0.644848) UV(0.895867, 0.380945)
|
||||
V 77: Pos(1.957487, 0.69137, 0.284029) Norm(0.956337, 0.1301, -0.261713) UV(0.895647, 0.068276)
|
||||
V 78: Pos(1.957014, 0.68826, 0.28547) Norm(0.83726, -0.519484, 0.170681) UV(0.90439, 0.067641)
|
||||
V 79: Pos(1.871199, 0.715583, 0.211251) Norm(0.121887, -0.767984, 0.628763) UV(0.960544, 0.92044)
|
||||
V 80: Pos(1.908902, 0.745095, 0.287268) Norm(-0.068272, -0.989999, 0.12345) UV(0.961512, 0.607772)
|
||||
V 81: Pos(1.954292, 0.687107, 0.287268) Norm(0.22735, -0.804542, 0.548657) UV(0.916355, 0.067756)
|
||||
V 82: Pos(1.871199, 0.715583, 0.211251) Norm(0.121887, -0.767984, 0.628763) UV(0.058149, 0.069291)
|
||||
V 83: Pos(1.908902, 0.745095, 0.287268) Norm(-0.068272, -0.989999, 0.12345) UV(0.058149, 0.345857)
|
||||
V 84: Pos(1.90598, 0.746767, 0.288069) Norm(-0.681101, -0.415856, 0.602632) UV(0.047779, 0.348773)
|
||||
V 85: Pos(1.954292, 0.687107, 0.287268) Norm(0.22735, -0.804542, 0.548657) UV(0.314148, 0.335055)
|
||||
V 86: Pos(1.905059, 0.750005, 0.287268) Norm(-0.726396, 0.413925, 0.548648) UV(0.036474, 0.346771)
|
||||
V 87: Pos(1.953095, 0.696532, 0.294996) Norm(0.149457, -0.309654, 0.93903) UV(0.298425, 0.453344)
|
||||
V 88: Pos(1.955666, 0.698545, 0.29385) Norm(0.73947, 0.113211, 0.663602) UV(0.309097, 0.456842)
|
||||
V 89: Pos(1.952377, 0.702748, 0.29385) Norm(0.053881, 0.831106, 0.553499) UV(0.303295, 0.474544)
|
||||
V 90: Pos(1.949806, 0.700735, 0.294996) Norm(-0.451341, 0.435647, 0.778783) UV(0.292623, 0.471046)
|
||||
V 91: Pos(1.913921, 0.74658, 0.294996) Norm(-0.264714, 0.219449, 0.939025) UV(0.298479, 0.388008)
|
||||
V 92: Pos(1.911613, 0.744773, 0.293158) Norm(-0.684428, -0.389788, 0.616136) UV(0.288518, 0.391248)
|
||||
V 93: Pos(1.914952, 0.740506, 0.293159) Norm(-0.139196, -0.897892, 0.41763) UV(0.282669, 0.373265)
|
||||
V 94: Pos(1.917261, 0.742313, 0.294996) Norm(0.314498, -0.542755, 0.778787) UV(0.292631, 0.370025)
|
||||
V 95: Pos(1.954292, 0.687107, 0.287268) Norm(0.22735, -0.804542, 0.548657) UV(0.290165, 0.420667)
|
||||
V 96: Pos(1.950787, 0.694725, 0.293158) Norm(-0.542765, -0.570773, 0.616137) UV(0.288471, 0.450082)
|
||||
V 97: Pos(1.95137, 0.688779, 0.288069) Norm(-0.567274, -0.561274, 0.602638) UV(0.282669, 0.429661)
|
||||
V 98: Pos(1.955666, 0.698545, 0.29385) Norm(0.73947, 0.113211, 0.663602) UV(0.90711, 0.035517)
|
||||
V 99: Pos(1.953095, 0.696532, 0.294996) Norm(0.149457, -0.309654, 0.93903) UV(0.91884, 0.03811)
|
||||
V 100: Pos(1.956563, 0.699247, 0.290583) Norm(0.921035, 0.386812, -0.045518) UV(0.326099, 0.097295)
|
||||
V 101: Pos(1.955666, 0.698545, 0.29385) Norm(0.73947, 0.113211, 0.663602) UV(0.314148, 0.097008)
|
||||
V 102: Pos(1.957014, 0.68826, 0.28547) Norm(0.83726, -0.519484, 0.170681) UV(0.339366, 0.062365)
|
||||
V 103: Pos(1.955111, 0.69811, 0.287655) Norm(0.424828, 0.496372, -0.757057) UV(0.334911, 0.095931)
|
||||
V 104: Pos(1.952222, 0.694382, 0.28547) Norm(-0.556023, 0.743669, -0.371209) UV(0.298744, 0.536259)
|
||||
V 105: Pos(1.952403, 0.695991, 0.287271) Norm(-0.504568, 0.268536, -0.820548) UV(0.294691, 0.540289)
|
||||
V 106: Pos(1.955111, 0.69811, 0.287655) Norm(0.424828, 0.496372, -0.757057) UV(0.282669, 0.540289)
|
||||
V 107: Pos(1.955354, 0.694095, 0.284029) Norm(0.353521, 0.733298, -0.580772) UV(0.290831, 0.528729)
|
||||
V 108: Pos(1.913229, 0.746038, 0.287271) Norm(0.13947, -0.554268, -0.82057) UV(0.949217, 0.599902)
|
||||
V 109: Pos(1.911304, 0.744532, 0.28972) Norm(-0.480863, -0.857888, -0.181104) UV(0.960954, 0.59718)
|
||||
V 110: Pos(1.911304, 0.744532, 0.28972) Norm(-0.480863, -0.857888, -0.181104) UV(0.06484, 0.354495)
|
||||
V 111: Pos(1.911613, 0.744773, 0.293158) Norm(-0.684428, -0.389788, 0.616136) UV(0.065307, 0.366552)
|
||||
V 112: Pos(1.905059, 0.750005, 0.287268) Norm(-0.726396, 0.413925, 0.548648) UV(0.290144, 0.420667)
|
||||
V 113: Pos(1.90598, 0.746767, 0.288069) Norm(-0.681101, -0.415856, 0.602632) UV(0.282669, 0.411656)
|
||||
V 114: Pos(1.913921, 0.74658, 0.294996) Norm(-0.264714, 0.219449, 0.939025) UV(0.895647, 0.569857)
|
||||
V 115: Pos(1.916492, 0.748592, 0.29385) Norm(0.287488, 0.690641, 0.6636) UV(0.883904, 0.572541)
|
||||
V 116: Pos(1.906832, 0.752371, 0.28547) Norm(-0.303191, 0.93752, 0.17068) UV(0.339366, 0.342623)
|
||||
V 117: Pos(1.916492, 0.748592, 0.29385) Norm(0.287488, 0.690641, 0.6636) UV(0.314148, 0.315787)
|
||||
V 118: Pos(1.917389, 0.749294, 0.290583) Norm(0.596697, 0.801175, -0.04552) UV(0.326099, 0.316075)
|
||||
V 119: Pos(1.915937, 0.748158, 0.287655) Norm(0.583885, 0.293186, -0.757047) UV(0.33491, 0.314711)
|
||||
V 120: Pos(1.915937, 0.748158, 0.287655) Norm(0.583885, 0.293186, -0.757047) UV(0.939834, 0.592427)
|
||||
V 121: Pos(1.947189, 0.698687, 0.28972) Norm(-0.965573, 0.033132, -0.258014) UV(0.282669, 0.494161)
|
||||
V 122: Pos(1.947497, 0.698928, 0.293158) Norm(-0.905044, 0.080519, 0.417626) UV(0.282669, 0.482089)
|
||||
V 123: Pos(1.949806, 0.700735, 0.294996) Norm(-0.451341, 0.435647, 0.778783) UV(0.29209, 0.474544)
|
||||
V 124: Pos(1.952377, 0.702748, 0.29385) Norm(0.053881, 0.831106, 0.553499) UV(0.303839, 0.477207)
|
||||
V 125: Pos(1.953274, 0.70345, 0.290583) Norm(0.23019, 0.969106, -0.088577) UV(0.309067, 0.488073)
|
||||
V 126: Pos(1.951822, 0.702313, 0.287655) Norm(-0.055188, 0.74573, -0.663958) UV(0.303839, 0.498959)
|
||||
V 127: Pos(1.949113, 0.700194, 0.287271) Norm(-0.587358, 0.329173, -0.739362) UV(0.29209, 0.501669)
|
||||
V 128: Pos(1.951822, 0.702313, 0.287655) Norm(-0.055188, 0.74573, -0.663958) UV(0.334911, 0.114304)
|
||||
V 129: Pos(1.953274, 0.70345, 0.290583) Norm(0.23019, 0.969106, -0.088577) UV(0.326099, 0.115668)
|
||||
V 130: Pos(1.950479, 0.694484, 0.28972) Norm(-0.94827, -0.260746, -0.181095) UV(0.303091, 0.540289)
|
||||
V 131: Pos(1.947189, 0.698687, 0.28972) Norm(-0.965573, 0.033132, -0.258014) UV(0.303091, 0.558917)
|
||||
V 132: Pos(1.949113, 0.700194, 0.287271) Norm(-0.587358, 0.329173, -0.739362) UV(0.294691, 0.558917)
|
||||
V 133: Pos(1.947497, 0.698928, 0.293158) Norm(-0.905044, 0.080519, 0.417626) UV(0.282669, 0.467784)
|
||||
V 134: Pos(1.952377, 0.702748, 0.29385) Norm(0.053881, 0.831106, 0.553499) UV(0.314148, 0.11538)
|
||||
V 135: Pos(1.951822, 0.702313, 0.287655) Norm(-0.055188, 0.74573, -0.663958) UV(0.282669, 0.558917)
|
||||
V 136: Pos(1.947497, 0.698928, 0.293158) Norm(-0.905044, 0.080519, 0.417626) UV(0.267695, 0.358013)
|
||||
V 137: Pos(1.947189, 0.698687, 0.28972) Norm(-0.965573, 0.033132, -0.258014) UV(0.267227, 0.345956)
|
||||
V 138: Pos(1.914952, 0.740506, 0.293159) Norm(-0.139196, -0.897892, 0.41763) UV(0.309023, 0.515086)
|
||||
V 139: Pos(1.914644, 0.740265, 0.28972) Norm(-0.199734, -0.94527, -0.258013) UV(0.303804, 0.525971)
|
||||
V 140: Pos(1.916569, 0.741772, 0.287271) Norm(0.178483, -0.649214, -0.739368) UV(0.292075, 0.528729)
|
||||
V 141: Pos(1.919277, 0.743891, 0.287655) Norm(0.710654, -0.232666, -0.663956) UV(0.282669, 0.521283)
|
||||
V 142: Pos(1.920729, 0.745028, 0.290583) Norm(0.996025, -0.009297, -0.088583) UV(0.282669, 0.50924)
|
||||
V 143: Pos(1.919832, 0.744326, 0.29385) Norm(0.819725, -0.147289, 0.553496) UV(0.292075, 0.501669)
|
||||
V 144: Pos(1.917261, 0.742313, 0.294996) Norm(0.314498, -0.542755, 0.778787) UV(0.303804, 0.504271)
|
||||
V 145: Pos(1.911304, 0.744532, 0.28972) Norm(-0.480863, -0.857888, -0.181104) UV(0.301579, 0.558917)
|
||||
V 146: Pos(1.913229, 0.746038, 0.287271) Norm(0.13947, -0.554268, -0.82057) UV(0.301579, 0.567317)
|
||||
V 147: Pos(1.916569, 0.741772, 0.287271) Norm(0.178483, -0.649214, -0.739368) UV(0.282669, 0.567317)
|
||||
V 148: Pos(1.914644, 0.740265, 0.28972) Norm(-0.199734, -0.94527, -0.258013) UV(0.282669, 0.558917)
|
||||
V 149: Pos(1.920729, 0.745028, 0.290583) Norm(0.996025, -0.009297, -0.088583) UV(0.326099, 0.297425)
|
||||
V 150: Pos(1.919277, 0.743891, 0.287655) Norm(0.710654, -0.232666, -0.663956) UV(0.33491, 0.296061)
|
||||
V 151: Pos(1.916492, 0.748592, 0.29385) Norm(0.287488, 0.690641, 0.6636) UV(0.309159, 0.384535)
|
||||
V 152: Pos(1.919832, 0.744326, 0.29385) Norm(0.819725, -0.147289, 0.553496) UV(0.303311, 0.366552)
|
||||
V 153: Pos(1.914644, 0.740265, 0.28972) Norm(-0.199734, -0.94527, -0.258013) UV(0.083674, 0.353701)
|
||||
V 154: Pos(1.914952, 0.740506, 0.293159) Norm(-0.139196, -0.897892, 0.41763) UV(0.084142, 0.365757)
|
||||
V 155: Pos(1.915937, 0.748158, 0.287655) Norm(0.583885, 0.293186, -0.757047) UV(0.301579, 0.579339)
|
||||
V 156: Pos(1.919277, 0.743891, 0.287655) Norm(0.710654, -0.232666, -0.663956) UV(0.282669, 0.579339)
|
||||
V 157: Pos(1.919832, 0.744326, 0.29385) Norm(0.819725, -0.147289, 0.553496) UV(0.314148, 0.297137)
|
||||
V 158: Pos(1.913563, 0.745878, 0.214141) Norm(-0.375685, 0.479958, 0.792781) UV(0.108868, 0.838116)
|
||||
V 159: Pos(1.913563, 0.745878, 0.277353) Norm(-0.375683, 0.479959, -0.792781) UV(0.108868, 0.617641)
|
||||
V 160: Pos(1.934118, 0.752578, 0.277353) Norm(0.02072, 0.609155, -0.792781) UV(0.178008, 0.616517)
|
||||
V 161: Pos(1.934118, 0.752578, 0.214141) Norm(0.02072, 0.609155, 0.792781) UV(0.178008, 0.836992)
|
||||
V 162: Pos(1.934118, 0.752578, 0.214141) Norm(0.02072, 0.609155, 0.792781) UV(0.751409, 0.601446)
|
||||
V 163: Pos(1.934118, 0.752578, 0.277353) Norm(0.02072, 0.609155, -0.792781) UV(0.751409, 0.818173)
|
||||
V 164: Pos(1.954171, 0.744497, 0.277353) Norm(0.40743, 0.453321, -0.792781) UV(0.693332, 0.809153)
|
||||
V 165: Pos(1.954171, 0.744497, 0.214141) Norm(0.40743, 0.453321, 0.792781) UV(0.693332, 0.592427)
|
||||
V 166: Pos(1.964339, 0.725417, 0.277353) Norm(0.603497, 0.085372, -0.792782) UV(0.617875, 0.809233)
|
||||
V 167: Pos(1.964339, 0.725417, 0.214141) Norm(0.603497, 0.085372, 0.792782) UV(0.617875, 0.592506)
|
||||
V 168: Pos(1.959863, 0.704265, 0.277353) Norm(0.517182, -0.322522, -0.792781) UV(0.560343, 0.818374)
|
||||
V 169: Pos(1.959863, 0.704265, 0.214141) Norm(0.517182, -0.322522, 0.792781) UV(0.560343, 0.601647)
|
||||
V 170: Pos(1.959863, 0.704265, 0.214141) Norm(0.517182, -0.322522, 0.792781) UV(0.751409, 0.81317)
|
||||
V 171: Pos(1.959863, 0.704265, 0.277353) Norm(0.517182, -0.322522, -0.792781) UV(0.751409, 0.592575)
|
||||
V 172: Pos(1.942838, 0.690939, 0.277353) Norm(0.188873, -0.579505, -0.79278) UV(0.826372, 0.592427)
|
||||
V 173: Pos(1.942838, 0.690939, 0.214141) Norm(0.188872, -0.579505, 0.792781) UV(0.826372, 0.813022)
|
||||
V 174: Pos(1.92123, 0.691674, 0.277353) Norm(-0.227816, -0.56533, -0.792781) UV(0.88935, 0.593138)
|
||||
V 175: Pos(1.92123, 0.691674, 0.214141) Norm(-0.227815, -0.56533, 0.792781) UV(0.88935, 0.813733)
|
||||
V 176: Pos(1.92123, 0.691674, 0.214141) Norm(-0.227815, -0.56533, 0.792781) UV(0.314148, 0.813623)
|
||||
V 177: Pos(1.92123, 0.691674, 0.277353) Norm(-0.227816, -0.56533, -0.792781) UV(0.314148, 0.593528)
|
||||
V 178: Pos(1.905151, 0.706126, 0.277353) Norm(-0.537905, -0.286631, -0.79278) UV(0.387196, 0.594843)
|
||||
V 179: Pos(1.905151, 0.706126, 0.214141) Norm(-0.537906, -0.286631, 0.79278) UV(0.387196, 0.814937)
|
||||
V 180: Pos(1.905151, 0.706126, 0.214141) Norm(-0.537906, -0.286631, 0.79278) UV(0.668112, 0.35897)
|
||||
V 181: Pos(1.902122, 0.727533, 0.214141) Norm(-0.596304, 0.126185, 0.79278) UV(0.602763, 0.396671)
|
||||
V 182: Pos(1.897466, 0.728518, 0.214141) Norm(-0.753676, 0.159485, 0.637603) UV(0.590038, 0.386001)
|
||||
V 183: Pos(1.90095, 0.703888, 0.214141) Norm(-0.679865, -0.362278, 0.637603) UV(0.665228, 0.342623)
|
||||
V 184: Pos(1.902122, 0.727533, 0.277353) Norm(-0.5963, 0.126186, -0.792782) UV(0.455316, 0.592588)
|
||||
V 185: Pos(1.902122, 0.727533, 0.214141) Norm(-0.596304, 0.126185, 0.79278) UV(0.455316, 0.812682)
|
||||
V 186: Pos(1.902122, 0.727533, 0.214141) Norm(-0.596304, 0.126185, 0.79278) UV(0.036474, 0.837324)
|
||||
V 187: Pos(1.902122, 0.727533, 0.277353) Norm(-0.5963, 0.126186, -0.792782) UV(0.036474, 0.616849)
|
||||
V 188: Pos(1.902122, 0.727533, 0.297259) Norm(-0.348018, 0.073645, 0.934591) UV(0.794731, 0.230825)
|
||||
V 189: Pos(1.913563, 0.745878, 0.297259) Norm(-0.219259, 0.280117, 0.934591) UV(0.820539, 0.159971)
|
||||
V 190: Pos(1.910629, 0.749626, 0.293966) Norm(-0.557345, 0.712036, 0.42705) UV(0.83715, 0.160418)
|
||||
V 191: Pos(1.897466, 0.728518, 0.293966) Norm(-0.884639, 0.187199, 0.427049) UV(0.807456, 0.241942)
|
||||
V 192: Pos(1.934118, 0.752578, 0.297259) Norm(0.012094, 0.35552, 0.934591) UV(0.794731, 0.089116)
|
||||
V 193: Pos(1.905151, 0.706126, 0.297259) Norm(-0.313937, -0.167288, 0.93459) UV(0.729381, 0.268526)
|
||||
V 194: Pos(1.92123, 0.691674, 0.297259) Norm(-0.132961, -0.329945, 0.934589) UV(0.655069, 0.255433)
|
||||
V 195: Pos(1.942838, 0.690939, 0.297259) Norm(0.110232, -0.338218, 0.934589) UV(0.606564, 0.197672)
|
||||
V 196: Pos(1.959863, 0.704265, 0.297259) Norm(0.301846, -0.188236, 0.934589) UV(0.606565, 0.12227)
|
||||
V 197: Pos(1.964339, 0.725417, 0.297259) Norm(0.352223, 0.049827, 0.934589) UV(0.655069, 0.064509)
|
||||
V 198: Pos(1.954171, 0.744497, 0.297259) Norm(0.23779, 0.264574, 0.934589) UV(0.729381, 0.051416)
|
||||
V 199: Pos(1.964339, 0.725417, 0.277353) Norm(0.603497, 0.085372, -0.792782) UV(0.218556, 0.396011)
|
||||
V 200: Pos(1.954171, 0.744497, 0.277353) Norm(0.40743, 0.453321, -0.792781) UV(0.144243, 0.382909)
|
||||
V 201: Pos(1.957353, 0.748037, 0.277353) Norm(0.514956, 0.572959, -0.637603) UV(0.141358, 0.366552)
|
||||
V 202: Pos(1.969051, 0.726083, 0.277353) Norm(0.76277, 0.107906, -0.637604) UV(0.226861, 0.381627)
|
||||
V 203: Pos(1.959863, 0.704265, 0.200236) Norm(1e-06, -0.0, -1.0) UV(0.544734, 0.42988)
|
||||
V 204: Pos(1.964339, 0.725417, 0.200236) Norm(1e-06, -0.0, -1.0) UV(0.496229, 0.372082)
|
||||
V 205: Pos(1.969051, 0.726083, 0.200236) Norm(0.762771, 0.107903, -0.637603) UV(0.504535, 0.357698)
|
||||
V 206: Pos(1.963901, 0.701747, 0.200236) Norm(0.653676, -0.40764, -0.637603) UV(0.560343, 0.4242)
|
||||
V 207: Pos(1.90095, 0.703888, 0.293966) Norm(-0.798003, -0.425231, 0.427047) UV(0.732266, 0.28532)
|
||||
V 208: Pos(1.954171, 0.744497, 0.214141) Norm(0.40743, 0.453321, 0.792781) UV(0.668112, 0.57608)
|
||||
V 209: Pos(1.964339, 0.725417, 0.214141) Norm(0.603497, 0.085372, 0.792782) UV(0.742425, 0.562987)
|
||||
V 210: Pos(1.969051, 0.726083, 0.214141) Norm(0.76277, 0.107904, 0.637604) UV(0.750731, 0.577362)
|
||||
V 211: Pos(1.957353, 0.748037, 0.214141) Norm(0.514956, 0.572959, 0.637604) UV(0.665228, 0.592427)
|
||||
V 212: Pos(1.954171, 0.744497, 0.200236) Norm(0.0, 0.0, -1.0) UV(0.421917, 0.35898)
|
||||
V 213: Pos(1.957353, 0.748037, 0.200236) Norm(0.514957, 0.572959, -0.637603) UV(0.419032, 0.342623)
|
||||
V 214: Pos(1.905151, 0.706126, 0.277353) Norm(-0.537905, -0.286631, -0.79278) UV(0.144243, 0.60016)
|
||||
V 215: Pos(1.92123, 0.691674, 0.277353) Norm(-0.227816, -0.56533, -0.792781) UV(0.218556, 0.587058)
|
||||
V 216: Pos(1.919451, 0.68726, 0.277353) Norm(-0.287939, -0.71453, -0.637603) UV(0.226861, 0.601442)
|
||||
V 217: Pos(1.90095, 0.703888, 0.277353) Norm(-0.679865, -0.362279, -0.637603) UV(0.141358, 0.616517)
|
||||
V 218: Pos(1.919451, 0.68726, 0.293966) Norm(-0.337972, -0.838693, 0.427047) UV(0.646763, 0.270255)
|
||||
V 219: Pos(1.934118, 0.752578, 0.200236) Norm(1e-06, 1e-06, -1.0) UV(0.356567, 0.396705)
|
||||
V 220: Pos(1.93428, 0.757334, 0.200236) Norm(0.02619, 0.76992, -0.637603) UV(0.343842, 0.386029)
|
||||
V 221: Pos(1.913563, 0.745878, 0.200236) Norm(2e-06, 1e-06, -1.0) UV(0.330759, 0.467606)
|
||||
V 222: Pos(1.942838, 0.690939, 0.200236) Norm(0.0, -0.0, -1.0) UV(0.544734, 0.505331)
|
||||
V 223: Pos(1.92123, 0.691674, 0.200236) Norm(0.0, 0.0, -1.0) UV(0.496229, 0.563129)
|
||||
V 224: Pos(1.905151, 0.706126, 0.200236) Norm(-1e-06, 0.0, -1.0) UV(0.421917, 0.576231)
|
||||
V 225: Pos(1.902122, 0.727533, 0.200236) Norm(-0.0, 0.0, -1.0) UV(0.356567, 0.538506)
|
||||
V 226: Pos(1.92123, 0.691674, 0.214141) Norm(-0.227815, -0.56533, 0.792781) UV(0.742425, 0.372063)
|
||||
V 227: Pos(1.919451, 0.68726, 0.214141) Norm(-0.287939, -0.714529, 0.637605) UV(0.750731, 0.357688)
|
||||
V 228: Pos(1.944313, 0.686414, 0.293966) Norm(0.2802, -0.85972, 0.427047) UV(0.590955, 0.203796)
|
||||
V 229: Pos(1.934118, 0.752578, 0.277353) Norm(0.02072, 0.609155, -0.792781) UV(0.078894, 0.420634)
|
||||
V 230: Pos(1.93428, 0.757334, 0.277353) Norm(0.026189, 0.769919, -0.637603) UV(0.066169, 0.409958)
|
||||
V 231: Pos(1.910629, 0.749626, 0.200236) Norm(-0.474832, 0.606627, -0.637604) UV(0.314148, 0.467606)
|
||||
V 232: Pos(1.963901, 0.701747, 0.293966) Norm(0.767264, -0.478475, 0.427046) UV(0.590955, 0.117041)
|
||||
V 233: Pos(1.934118, 0.752578, 0.214141) Norm(0.02072, 0.609155, 0.792781) UV(0.602763, 0.538379)
|
||||
V 234: Pos(1.93428, 0.757334, 0.214141) Norm(0.026188, 0.76992, 0.637603) UV(0.590038, 0.549049)
|
||||
V 235: Pos(1.942838, 0.690939, 0.277353) Norm(0.188873, -0.579505, -0.79278) UV(0.26706, 0.52926)
|
||||
V 236: Pos(1.944313, 0.686414, 0.277353) Norm(0.238719, -0.732445, -0.637603) UV(0.282669, 0.53494)
|
||||
V 237: Pos(1.969051, 0.726083, 0.293966) Norm(0.895316, 0.126655, 0.427045) UV(0.646763, 0.050582)
|
||||
V 238: Pos(1.957353, 0.748037, 0.277353) Norm(0.514956, 0.572959, -0.637603) UV(0.522723, 0.745674)
|
||||
V 239: Pos(1.93428, 0.757334, 0.277353) Norm(0.026189, 0.769919, -0.637603) UV(0.512275, 0.812485)
|
||||
V 240: Pos(1.93428, 0.757334, 0.293966) Norm(0.030741, 0.903706, 0.427049) UV(0.455316, 0.812425)
|
||||
V 241: Pos(1.957353, 0.748037, 0.293966) Norm(0.604439, 0.672522, 0.427046) UV(0.465765, 0.745615)
|
||||
V 242: Pos(1.90095, 0.703888, 0.214141) Norm(-0.679865, -0.362278, 0.637603) UV(0.937762, 0.676785)
|
||||
V 243: Pos(1.897466, 0.728518, 0.214141) Norm(-0.753676, 0.159485, 0.637603) UV(0.939834, 0.75518)
|
||||
V 244: Pos(1.897466, 0.728518, 0.200236) Norm(-0.753675, 0.159486, -0.637603) UV(0.891422, 0.754857)
|
||||
V 245: Pos(1.90095, 0.703888, 0.200236) Norm(-0.679866, -0.362278, -0.637602) UV(0.88935, 0.676463)
|
||||
V 246: Pos(1.910629, 0.749626, 0.277353) Norm(-0.474833, 0.606626, -0.637604) UV(0.806539, 0.425916)
|
||||
V 247: Pos(1.897466, 0.728518, 0.277353) Norm(-0.753674, 0.159486, -0.637604) UV(0.807646, 0.342623)
|
||||
V 248: Pos(1.897466, 0.728518, 0.293966) Norm(-0.884639, 0.187199, 0.427049) UV(0.865589, 0.342759)
|
||||
V 249: Pos(1.910629, 0.749626, 0.293966) Norm(-0.557345, 0.712036, 0.42705) UV(0.864482, 0.426052)
|
||||
V 250: Pos(1.897466, 0.728518, 0.277353) Norm(-0.753674, 0.159486, -0.637604) UV(0.238201, 0.778947)
|
||||
V 251: Pos(1.90095, 0.703888, 0.277353) Norm(-0.679865, -0.362279, -0.637603) UV(0.236128, 0.700553)
|
||||
V 252: Pos(1.90095, 0.703888, 0.293966) Norm(-0.798003, -0.425231, 0.427047) UV(0.293971, 0.700938)
|
||||
V 253: Pos(1.897466, 0.728518, 0.293966) Norm(-0.884639, 0.187199, 0.427049) UV(0.296043, 0.779333)
|
||||
V 254: Pos(1.919451, 0.68726, 0.277353) Norm(-0.287939, -0.71453, -0.637603) UV(0.238201, 0.616517)
|
||||
V 255: Pos(1.919451, 0.68726, 0.293966) Norm(-0.337972, -0.838693, 0.427047) UV(0.296043, 0.616902)
|
||||
V 256: Pos(1.919451, 0.68726, 0.277353) Norm(-0.287939, -0.71453, -0.637603) UV(0.83715, 0.194467)
|
||||
V 257: Pos(1.944313, 0.686414, 0.277353) Norm(0.238719, -0.732445, -0.637603) UV(0.837672, 0.122003)
|
||||
V 258: Pos(1.944313, 0.686414, 0.293966) Norm(0.2802, -0.85972, 0.427047) UV(0.895647, 0.121766)
|
||||
V 259: Pos(1.919451, 0.68726, 0.293966) Norm(-0.337972, -0.838693, 0.427047) UV(0.895125, 0.194231)
|
||||
V 260: Pos(1.963901, 0.701747, 0.277353) Norm(0.653675, -0.407639, -0.637605) UV(0.83715, 0.035753)
|
||||
V 261: Pos(1.963901, 0.701747, 0.293966) Norm(0.767264, -0.478475, 0.427046) UV(0.895125, 0.035517)
|
||||
V 262: Pos(1.963901, 0.701747, 0.277353) Norm(0.653675, -0.407639, -0.637605) UV(0.512275, 0.592648)
|
||||
V 263: Pos(1.969051, 0.726083, 0.277353) Norm(0.76277, 0.107906, -0.637604) UV(0.522723, 0.658854)
|
||||
V 264: Pos(1.969051, 0.726083, 0.293966) Norm(0.895316, 0.126655, 0.427045) UV(0.465765, 0.658794)
|
||||
V 265: Pos(1.963901, 0.701747, 0.293966) Norm(0.767264, -0.478475, 0.427046) UV(0.455316, 0.592588)
|
||||
V 266: Pos(1.93428, 0.757334, 0.277353) Norm(0.026189, 0.769919, -0.637603) UV(0.807646, 0.505471)
|
||||
V 267: Pos(1.93428, 0.757334, 0.293966) Norm(0.030741, 0.903706, 0.427049) UV(0.865589, 0.505607)
|
||||
V 268: Pos(1.919451, 0.68726, 0.214141) Norm(-0.287939, -0.714529, 0.637605) UV(0.939834, 0.592749)
|
||||
V 269: Pos(1.919451, 0.68726, 0.200236) Norm(-0.287938, -0.71453, -0.637604) UV(0.891422, 0.592427)
|
||||
V 270: Pos(1.944313, 0.686414, 0.214141) Norm(0.238718, -0.732445, 0.637603) UV(0.285173, 0.865582)
|
||||
V 271: Pos(1.919451, 0.68726, 0.214141) Norm(-0.287939, -0.714529, 0.637605) UV(0.284651, 0.938046)
|
||||
V 272: Pos(1.919451, 0.68726, 0.200236) Norm(-0.287938, -0.71453, -0.637604) UV(0.236128, 0.938244)
|
||||
V 273: Pos(1.944313, 0.686414, 0.200236) Norm(0.238718, -0.732445, -0.637603) UV(0.23665, 0.86578)
|
||||
V 274: Pos(1.963901, 0.701747, 0.214141) Norm(0.653674, -0.407641, 0.637604) UV(0.284651, 0.779332)
|
||||
V 275: Pos(1.963901, 0.701747, 0.200236) Norm(0.653676, -0.40764, -0.637603) UV(0.236128, 0.779531)
|
||||
V 276: Pos(1.969051, 0.726083, 0.214141) Norm(0.76277, 0.107904, 0.637604) UV(0.188456, 0.682723)
|
||||
V 277: Pos(1.963901, 0.701747, 0.214141) Norm(0.653674, -0.407641, 0.637604) UV(0.178008, 0.616517)
|
||||
V 278: Pos(1.963901, 0.701747, 0.200236) Norm(0.653676, -0.40764, -0.637603) UV(0.22568, 0.616567)
|
||||
V 279: Pos(1.969051, 0.726083, 0.200236) Norm(0.762771, 0.107903, -0.637603) UV(0.236128, 0.682773)
|
||||
V 280: Pos(1.910629, 0.749626, 0.214141) Norm(-0.474834, 0.606625, 0.637604) UV(0.937846, 0.838586)
|
||||
V 281: Pos(1.93428, 0.757334, 0.214141) Norm(0.026188, 0.76992, 0.637603) UV(0.938953, 0.918141)
|
||||
V 282: Pos(1.93428, 0.757334, 0.200236) Norm(0.02619, 0.76992, -0.637603) UV(0.890457, 0.918027)
|
||||
V 283: Pos(1.910629, 0.749626, 0.200236) Norm(-0.474832, 0.606627, -0.637604) UV(0.88935, 0.838472)
|
||||
V 284: Pos(1.957353, 0.748037, 0.214141) Norm(0.514956, 0.572959, 0.637604) UV(0.188456, 0.769543)
|
||||
V 285: Pos(1.957353, 0.748037, 0.200236) Norm(0.514957, 0.572959, -0.637603) UV(0.236128, 0.769594)
|
||||
V 286: Pos(1.93428, 0.757334, 0.214141) Norm(0.026188, 0.76992, 0.637603) UV(0.178008, 0.836354)
|
||||
V 287: Pos(1.93428, 0.757334, 0.200236) Norm(0.02619, 0.76992, -0.637603) UV(0.22568, 0.836404)
|
||||
V 288: Pos(1.897466, 0.728518, 0.214141) Norm(-0.753676, 0.159485, 0.637603) UV(0.938953, 0.755293)
|
||||
V 289: Pos(1.897466, 0.728518, 0.200236) Norm(-0.753675, 0.159486, -0.637603) UV(0.890457, 0.75518)
|
||||
V 290: Pos(1.944313, 0.686414, 0.200236) Norm(0.238718, -0.732445, -0.637603) UV(0.560343, 0.511012)
|
||||
V 291: Pos(1.902122, 0.727533, 0.277353) Norm(-0.5963, 0.126186, -0.792782) UV(0.078894, 0.562435)
|
||||
V 292: Pos(1.897466, 0.728518, 0.277353) Norm(-0.753674, 0.159486, -0.637604) UV(0.066169, 0.573111)
|
||||
V 293: Pos(1.919451, 0.68726, 0.200236) Norm(-0.287938, -0.71453, -0.637604) UV(0.504535, 0.577513)
|
||||
V 294: Pos(1.959863, 0.704265, 0.214141) Norm(0.517182, -0.322522, 0.792781) UV(0.790929, 0.505226)
|
||||
V 295: Pos(1.963901, 0.701747, 0.214141) Norm(0.653674, -0.407641, 0.637604) UV(0.806539, 0.510903)
|
||||
V 296: Pos(1.90095, 0.703888, 0.200236) Norm(-0.679866, -0.362278, -0.637602) UV(0.419032, 0.592588)
|
||||
V 297: Pos(1.959863, 0.704265, 0.277353) Norm(0.517182, -0.322522, -0.792781) UV(0.26706, 0.453809)
|
||||
V 298: Pos(1.963901, 0.701747, 0.277353) Norm(0.653675, -0.407639, -0.637605) UV(0.282669, 0.448129)
|
||||
V 299: Pos(1.913563, 0.745878, 0.214141) Norm(-0.375685, 0.479958, 0.792781) UV(0.576954, 0.467525)
|
||||
V 300: Pos(1.910629, 0.749626, 0.214141) Norm(-0.474834, 0.606625, 0.637604) UV(0.560343, 0.467525)
|
||||
V 301: Pos(1.897466, 0.728518, 0.200236) Norm(-0.753675, 0.159486, -0.637603) UV(0.343842, 0.549182)
|
||||
V 302: Pos(1.913563, 0.745878, 0.277353) Norm(-0.375683, 0.479959, -0.792781) UV(0.053085, 0.491535)
|
||||
V 303: Pos(1.910629, 0.749626, 0.277353) Norm(-0.474833, 0.606626, -0.637604) UV(0.036474, 0.491535)
|
||||
V 304: Pos(1.942838, 0.690939, 0.214141) Norm(0.188872, -0.579505, 0.792781) UV(0.790929, 0.429824)
|
||||
V 305: Pos(1.944313, 0.686414, 0.214141) Norm(0.238718, -0.732445, 0.637603) UV(0.806539, 0.424147)
|
||||
V 306: Pos(1.93428, 0.757334, 0.293966) Norm(0.030741, 0.903706, 0.427049) UV(0.807456, 0.078895)
|
||||
V 307: Pos(1.957353, 0.748037, 0.293966) Norm(0.604439, 0.672522, 0.427046) UV(0.732266, 0.035517)
|
||||
|
||||
===Triangles (Indices): 276
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 3 2 4
|
||||
Tri: 3 4 5
|
||||
Tri: 6 7 8
|
||||
Tri: 6 8 9
|
||||
Tri: 9 8 10
|
||||
Tri: 9 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 12 14 15
|
||||
Tri: 9 11 16
|
||||
Tri: 9 16 17
|
||||
Tri: 15 14 18
|
||||
Tri: 15 18 19
|
||||
Tri: 20 21 22
|
||||
Tri: 20 22 23
|
||||
Tri: 12 15 24
|
||||
Tri: 12 24 25
|
||||
Tri: 26 27 28
|
||||
Tri: 26 28 29
|
||||
Tri: 30 31 32
|
||||
Tri: 30 32 33
|
||||
Tri: 34 3 5
|
||||
Tri: 34 5 35
|
||||
Tri: 9 17 36
|
||||
Tri: 9 36 6
|
||||
Tri: 37 38 27
|
||||
Tri: 37 27 26
|
||||
Tri: 20 23 39
|
||||
Tri: 20 39 40
|
||||
Tri: 0 3 34
|
||||
Tri: 0 34 41
|
||||
Tri: 42 43 44
|
||||
Tri: 42 44 45
|
||||
Tri: 8 46 47
|
||||
Tri: 8 47 10
|
||||
Tri: 13 48 49
|
||||
Tri: 13 49 14
|
||||
Tri: 50 51 52
|
||||
Tri: 50 52 53
|
||||
Tri: 21 54 31
|
||||
Tri: 21 31 22
|
||||
Tri: 1 55 56
|
||||
Tri: 1 56 2
|
||||
Tri: 57 58 43
|
||||
Tri: 57 43 42
|
||||
Tri: 32 59 60
|
||||
Tri: 32 60 33
|
||||
Tri: 61 62 63
|
||||
Tri: 61 63 64
|
||||
Tri: 43 58 61
|
||||
Tri: 43 61 64
|
||||
Tri: 65 26 29
|
||||
Tri: 65 29 66
|
||||
Tri: 44 43 64
|
||||
Tri: 44 64 67
|
||||
Tri: 17 16 68
|
||||
Tri: 17 68 69
|
||||
Tri: 47 46 70
|
||||
Tri: 47 70 71
|
||||
Tri: 72 73 74
|
||||
Tri: 72 74 75
|
||||
Tri: 51 76 77
|
||||
Tri: 51 77 78
|
||||
Tri: 79 72 75
|
||||
Tri: 79 75 80
|
||||
Tri: 52 51 78
|
||||
Tri: 52 78 81
|
||||
Tri: 39 82 83
|
||||
Tri: 39 83 84
|
||||
Tri: 31 54 85
|
||||
Tri: 31 85 32
|
||||
Tri: 40 39 84
|
||||
Tri: 40 84 86
|
||||
Tri: 87 88 89
|
||||
Tri: 87 89 90
|
||||
Tri: 91 92 93
|
||||
Tri: 91 93 94
|
||||
Tri: 95 87 96
|
||||
Tri: 95 96 97
|
||||
Tri: 78 98 99
|
||||
Tri: 78 99 81
|
||||
Tri: 71 100 101
|
||||
Tri: 71 101 102
|
||||
Tri: 70 103 100
|
||||
Tri: 70 100 71
|
||||
Tri: 104 105 106
|
||||
Tri: 104 106 107
|
||||
Tri: 80 75 108
|
||||
Tri: 80 108 109
|
||||
Tri: 84 83 110
|
||||
Tri: 84 110 111
|
||||
Tri: 112 113 92
|
||||
Tri: 112 92 91
|
||||
Tri: 29 28 114
|
||||
Tri: 29 114 115
|
||||
Tri: 68 116 117
|
||||
Tri: 68 117 118
|
||||
Tri: 69 68 118
|
||||
Tri: 69 118 119
|
||||
Tri: 75 74 120
|
||||
Tri: 75 120 108
|
||||
Tri: 121 122 123
|
||||
Tri: 121 123 124
|
||||
Tri: 121 124 125
|
||||
Tri: 121 125 126
|
||||
Tri: 121 126 127
|
||||
Tri: 100 103 128
|
||||
Tri: 100 128 129
|
||||
Tri: 105 130 131
|
||||
Tri: 105 131 132
|
||||
Tri: 96 87 90
|
||||
Tri: 96 90 133
|
||||
Tri: 101 100 129
|
||||
Tri: 101 129 134
|
||||
Tri: 106 105 132
|
||||
Tri: 106 132 135
|
||||
Tri: 60 59 136
|
||||
Tri: 60 136 137
|
||||
Tri: 138 139 140
|
||||
Tri: 138 140 141
|
||||
Tri: 138 141 142
|
||||
Tri: 138 142 143
|
||||
Tri: 138 143 144
|
||||
Tri: 145 146 147
|
||||
Tri: 145 147 148
|
||||
Tri: 119 118 149
|
||||
Tri: 119 149 150
|
||||
Tri: 151 91 94
|
||||
Tri: 151 94 152
|
||||
Tri: 111 110 153
|
||||
Tri: 111 153 154
|
||||
Tri: 146 155 156
|
||||
Tri: 146 156 147
|
||||
Tri: 118 117 157
|
||||
Tri: 118 157 149
|
||||
Tri: 158 159 160
|
||||
Tri: 158 160 161
|
||||
Tri: 162 163 164
|
||||
Tri: 162 164 165
|
||||
Tri: 165 164 166
|
||||
Tri: 165 166 167
|
||||
Tri: 167 166 168
|
||||
Tri: 167 168 169
|
||||
Tri: 170 171 172
|
||||
Tri: 170 172 173
|
||||
Tri: 173 172 174
|
||||
Tri: 173 174 175
|
||||
Tri: 176 177 178
|
||||
Tri: 176 178 179
|
||||
Tri: 180 181 182
|
||||
Tri: 180 182 183
|
||||
Tri: 179 178 184
|
||||
Tri: 179 184 185
|
||||
Tri: 186 187 159
|
||||
Tri: 186 159 158
|
||||
Tri: 188 189 190
|
||||
Tri: 188 190 191
|
||||
Tri: 192 189 188
|
||||
Tri: 192 188 193
|
||||
Tri: 192 193 194
|
||||
Tri: 192 194 195
|
||||
Tri: 192 195 196
|
||||
Tri: 192 196 197
|
||||
Tri: 192 197 198
|
||||
Tri: 199 200 201
|
||||
Tri: 199 201 202
|
||||
Tri: 203 204 205
|
||||
Tri: 203 205 206
|
||||
Tri: 193 188 191
|
||||
Tri: 193 191 207
|
||||
Tri: 208 209 210
|
||||
Tri: 208 210 211
|
||||
Tri: 204 212 213
|
||||
Tri: 204 213 205
|
||||
Tri: 214 215 216
|
||||
Tri: 214 216 217
|
||||
Tri: 194 193 207
|
||||
Tri: 194 207 218
|
||||
Tri: 212 219 220
|
||||
Tri: 212 220 213
|
||||
Tri: 221 219 212
|
||||
Tri: 221 212 204
|
||||
Tri: 221 204 203
|
||||
Tri: 221 203 222
|
||||
Tri: 221 222 223
|
||||
Tri: 221 223 224
|
||||
Tri: 221 224 225
|
||||
Tri: 226 180 183
|
||||
Tri: 226 183 227
|
||||
Tri: 195 194 218
|
||||
Tri: 195 218 228
|
||||
Tri: 200 229 230
|
||||
Tri: 200 230 201
|
||||
Tri: 219 221 231
|
||||
Tri: 219 231 220
|
||||
Tri: 196 195 228
|
||||
Tri: 196 228 232
|
||||
Tri: 233 208 211
|
||||
Tri: 233 211 234
|
||||
Tri: 215 235 236
|
||||
Tri: 215 236 216
|
||||
Tri: 197 196 232
|
||||
Tri: 197 232 237
|
||||
Tri: 238 239 240
|
||||
Tri: 238 240 241
|
||||
Tri: 242 243 244
|
||||
Tri: 242 244 245
|
||||
Tri: 246 247 248
|
||||
Tri: 246 248 249
|
||||
Tri: 250 251 252
|
||||
Tri: 250 252 253
|
||||
Tri: 251 254 255
|
||||
Tri: 251 255 252
|
||||
Tri: 256 257 258
|
||||
Tri: 256 258 259
|
||||
Tri: 257 260 261
|
||||
Tri: 257 261 258
|
||||
Tri: 262 263 264
|
||||
Tri: 262 264 265
|
||||
Tri: 266 246 249
|
||||
Tri: 266 249 267
|
||||
Tri: 263 238 241
|
||||
Tri: 263 241 264
|
||||
Tri: 268 242 245
|
||||
Tri: 268 245 269
|
||||
Tri: 270 271 272
|
||||
Tri: 270 272 273
|
||||
Tri: 274 270 273
|
||||
Tri: 274 273 275
|
||||
Tri: 276 277 278
|
||||
Tri: 276 278 279
|
||||
Tri: 280 281 282
|
||||
Tri: 280 282 283
|
||||
Tri: 284 276 279
|
||||
Tri: 284 279 285
|
||||
Tri: 286 284 285
|
||||
Tri: 286 285 287
|
||||
Tri: 288 280 283
|
||||
Tri: 288 283 289
|
||||
Tri: 222 203 206
|
||||
Tri: 222 206 290
|
||||
Tri: 291 214 217
|
||||
Tri: 291 217 292
|
||||
Tri: 223 222 290
|
||||
Tri: 223 290 293
|
||||
Tri: 209 294 295
|
||||
Tri: 209 295 210
|
||||
Tri: 224 223 293
|
||||
Tri: 224 293 296
|
||||
Tri: 297 199 202
|
||||
Tri: 297 202 298
|
||||
Tri: 181 299 300
|
||||
Tri: 181 300 182
|
||||
Tri: 225 224 296
|
||||
Tri: 225 296 301
|
||||
Tri: 302 291 292
|
||||
Tri: 302 292 303
|
||||
Tri: 221 225 301
|
||||
Tri: 221 301 231
|
||||
Tri: 294 304 305
|
||||
Tri: 294 305 295
|
||||
Tri: 299 233 234
|
||||
Tri: 299 234 300
|
||||
Tri: 189 192 306
|
||||
Tri: 189 306 190
|
||||
Tri: 235 297 298
|
||||
Tri: 235 298 236
|
||||
Tri: 229 302 303
|
||||
Tri: 229 303 230
|
||||
Tri: 192 198 307
|
||||
Tri: 192 307 306
|
||||
Tri: 198 197 237
|
||||
Tri: 198 237 307
|
||||
Tri: 304 226 227
|
||||
Tri: 304 227 305
|
||||
@@ -0,0 +1,21 @@
|
||||
===Vertices (Split by UV/Normal): 10
|
||||
V 0: Pos(2.170956, 0.587034, 0.365303) Norm(-0.995813, -0.029843, 0.0864) UV(0.015946, 0.257699)
|
||||
V 1: Pos(2.170184, 0.584088, 0.389394) Norm(-0.99931, -0.015281, -0.033867) UV(0.016101, 0.023489)
|
||||
V 2: Pos(2.168909, 0.649793, 0.397387) Norm(-0.99931, -0.015281, -0.033867) UV(0.65722, 0.023489)
|
||||
V 3: Pos(2.16968, 0.652739, 0.373296) Norm(-0.995813, -0.029843, 0.0864) UV(0.657065, 0.257699)
|
||||
V 4: Pos(2.169977, 0.592927, 0.316697) Norm(-0.99906, -0.037007, 0.022573) UV(0.015812, 0.731623)
|
||||
V 5: Pos(2.165724, 0.589887, 0.341009) Norm(-0.999626, -0.02147, 0.016955) UV(0.016215, 0.497147)
|
||||
V 6: Pos(2.164448, 0.655593, 0.349002) Norm(-0.999626, -0.02147, 0.016955) UV(0.657334, 0.497147)
|
||||
V 7: Pos(2.168701, 0.658633, 0.32469) Norm(-0.99906, -0.037007, 0.022573) UV(0.656931, 0.731623)
|
||||
V 8: Pos(2.166257, 0.595846, 0.292114) Norm(-0.973478, -0.074359, 0.216359) UV(0.015973, 0.973057)
|
||||
V 9: Pos(2.161023, 0.66149, 0.299973) Norm(-0.973478, -0.074359, 0.216359) UV(0.657373, 0.976511)
|
||||
|
||||
===Triangles (Indices): 8
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 5 0 3
|
||||
Tri: 5 3 6
|
||||
Tri: 8 4 7
|
||||
Tri: 8 7 9
|
||||
@@ -0,0 +1,21 @@
|
||||
===Vertices (Split by UV/Normal): 10
|
||||
V 0: Pos(2.169542, 0.215532, 0.473265) Norm(-0.991319, 0.000923, 0.131472) UV(0.015946, 0.257699)
|
||||
V 1: Pos(2.169844, 0.213758, 0.502617) Norm(-0.999907, 0.008286, 0.010808) UV(0.016101, 0.023489)
|
||||
V 2: Pos(2.170559, 0.293782, 0.507448) Norm(-0.999907, 0.008286, 0.010808) UV(0.65722, 0.023489)
|
||||
V 3: Pos(2.170257, 0.295557, 0.478097) Norm(-0.99132, 0.000923, 0.131472) UV(0.657065, 0.257699)
|
||||
V 4: Pos(2.165864, 0.219133, 0.414181) Norm(-0.997607, -0.009991, 0.068407) UV(0.015812, 0.731623)
|
||||
V 5: Pos(2.161964, 0.217368, 0.443992) Norm(-0.99807, 0.005185, 0.061876) UV(0.016215, 0.497147)
|
||||
V 6: Pos(2.16268, 0.297392, 0.448823) Norm(-0.99807, 0.005185, 0.061877) UV(0.657334, 0.497147)
|
||||
V 7: Pos(2.166579, 0.299157, 0.419013) Norm(-0.997607, -0.009991, 0.068407) UV(0.656931, 0.731623)
|
||||
V 8: Pos(2.160102, 0.220978, 0.384476) Norm(-0.964166, -0.036223, 0.262816) UV(0.015973, 0.973057)
|
||||
V 9: Pos(2.15602, 0.301042, 0.389359) Norm(-0.964166, -0.036223, 0.262816) UV(0.657373, 0.976511)
|
||||
|
||||
===Triangles (Indices): 8
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 5 0 3
|
||||
Tri: 5 3 6
|
||||
Tri: 8 4 7
|
||||
Tri: 8 7 9
|
||||
@@ -0,0 +1,21 @@
|
||||
===Vertices (Split by UV/Normal): 10
|
||||
V 0: Pos(2.16925, 0.237091, 0.341346) Norm(-0.995813, -0.002278, 0.09138) UV(0.015946, 0.257699)
|
||||
V 1: Pos(2.168413, 0.241963, 0.367235) Norm(-0.99931, -0.024824, -0.027645) UV(0.016101, 0.023489)
|
||||
V 2: Pos(2.167028, 0.312561, 0.353905) Norm(-0.99931, -0.024824, -0.027645) UV(0.65722, 0.023489)
|
||||
V 3: Pos(2.167865, 0.307689, 0.328015) Norm(-0.995813, -0.002278, 0.09138) UV(0.657065, 0.257699)
|
||||
V 4: Pos(2.168187, 0.22721, 0.289126) Norm(-0.99906, -0.028434, 0.03272) UV(0.015812, 0.731623)
|
||||
V 5: Pos(2.163571, 0.232057, 0.315276) Norm(-0.999626, -0.015326, 0.02266) UV(0.016215, 0.497147)
|
||||
V 6: Pos(2.162186, 0.302655, 0.301945) Norm(-0.999626, -0.015326, 0.022659) UV(0.657334, 0.497147)
|
||||
V 7: Pos(2.166803, 0.297808, 0.275796) Norm(-0.99906, -0.028434, 0.032721) UV(0.656931, 0.731623)
|
||||
V 8: Pos(2.16415, 0.222148, 0.262737) Norm(-0.973479, -0.005347, 0.228716) UV(0.015973, 0.973057)
|
||||
V 9: Pos(2.158468, 0.292639, 0.249288) Norm(-0.973479, -0.005347, 0.228716) UV(0.657373, 0.976511)
|
||||
|
||||
===Triangles (Indices): 8
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 5 0 3
|
||||
Tri: 5 3 6
|
||||
Tri: 8 4 7
|
||||
Tri: 8 7 9
|
||||
@@ -0,0 +1,21 @@
|
||||
===Vertices (Split by UV/Normal): 10
|
||||
V 0: Pos(2.015285, 0.219518, 0.200815) Norm(0.082364, -0.039648, 0.995813) UV(0.015946, 0.257699)
|
||||
V 1: Pos(2.040888, 0.213314, 0.201652) Norm(-0.035405, -0.011257, 0.99931) UV(0.016101, 0.023489)
|
||||
V 2: Pos(2.057765, 0.283149, 0.203037) Norm(-0.035405, -0.011257, 0.99931) UV(0.65722, 0.023489)
|
||||
V 3: Pos(2.032162, 0.289353, 0.2022) Norm(0.082364, -0.039648, 0.995813) UV(0.657065, 0.257699)
|
||||
V 4: Pos(1.963621, 0.231982, 0.201878) Norm(0.018136, -0.039371, 0.99906) UV(0.015812, 0.731623)
|
||||
V 5: Pos(1.989451, 0.225649, 0.206494) Norm(0.014356, -0.023288, 0.999626) UV(0.016215, 0.497147)
|
||||
V 6: Pos(2.006328, 0.295484, 0.207879) Norm(0.014355, -0.023288, 0.999626) UV(0.657334, 0.497147)
|
||||
V 7: Pos(1.980498, 0.301817, 0.203262) Norm(0.018136, -0.039371, 0.99906) UV(0.656931, 0.731623)
|
||||
V 8: Pos(1.937485, 0.238219, 0.205915) Norm(0.20629, -0.098914, 0.973479) UV(0.015973, 0.973057)
|
||||
V 9: Pos(1.954209, 0.308005, 0.211597) Norm(0.20629, -0.098914, 0.973479) UV(0.657373, 0.976511)
|
||||
|
||||
===Triangles (Indices): 8
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 5 0 3
|
||||
Tri: 5 3 6
|
||||
Tri: 8 4 7
|
||||
Tri: 8 7 9
|
||||
@@ -0,0 +1,103 @@
|
||||
===Vertices (Split by UV/Normal): 60
|
||||
V 0: Pos(2.04882, 0.593961, 0.247254) Norm(0.15799, -0.805469, -0.571191) UV(0.953767, 0.893055)
|
||||
V 1: Pos(2.048557, 0.595304, 0.329654) Norm(0.15631, -0.796892, 0.58355) UV(0.960925, 0.457499)
|
||||
V 2: Pos(2.047151, 0.596248, 0.329654) Norm(-0.796943, -0.156316, 0.583478) UV(0.968094, 0.457499)
|
||||
V 3: Pos(2.045809, 0.595985, 0.247254) Norm(-0.805479, -0.157991, -0.571177) UV(0.969128, 0.893055)
|
||||
V 4: Pos(2.045809, 0.595985, 0.247254) Norm(-0.805479, -0.157991, -0.571177) UV(0.953767, 0.457499)
|
||||
V 5: Pos(2.047151, 0.596248, 0.329654) Norm(-0.796943, -0.156316, 0.583478) UV(0.960925, 0.021942)
|
||||
V 6: Pos(2.048096, 0.597654, 0.329654) Norm(-0.156311, 0.796894, 0.583547) UV(0.968094, 0.021942)
|
||||
V 7: Pos(2.047833, 0.598996, 0.247254) Norm(-0.157991, 0.805467, -0.571193) UV(0.969128, 0.457499)
|
||||
V 8: Pos(2.047833, 0.598996, 0.247254) Norm(-0.157991, 0.805467, -0.571193) UV(0.228451, 0.978058)
|
||||
V 9: Pos(2.048096, 0.597654, 0.329654) Norm(-0.156311, 0.796894, 0.583547) UV(0.235609, 0.542501)
|
||||
V 10: Pos(2.049502, 0.596709, 0.329654) Norm(0.796941, 0.156322, 0.583479) UV(0.242779, 0.542501)
|
||||
V 11: Pos(2.050844, 0.596972, 0.247254) Norm(0.805478, 0.157997, -0.571176) UV(0.243812, 0.978058)
|
||||
V 12: Pos(2.048557, 0.595304, 0.329654) Norm(0.15631, -0.796892, 0.58355) UV(0.248138, 0.542501)
|
||||
V 13: Pos(2.04882, 0.593961, 0.247254) Norm(0.15799, -0.805469, -0.571191) UV(0.255297, 0.978058)
|
||||
V 14: Pos(2.045809, 0.595985, 0.247254) Norm(-0.805479, -0.157991, -0.571177) UV(0.274477, 0.542501)
|
||||
V 15: Pos(2.047833, 0.598996, 0.247254) Norm(-0.157991, 0.805467, -0.571193) UV(0.274477, 0.561681)
|
||||
V 16: Pos(2.050844, 0.596972, 0.247254) Norm(0.805478, 0.157997, -0.571176) UV(0.255297, 0.561681)
|
||||
V 17: Pos(2.04882, 0.593961, 0.247254) Norm(0.15799, -0.805469, -0.571191) UV(0.255297, 0.542501)
|
||||
V 18: Pos(2.048096, 0.597654, 0.329654) Norm(-0.156311, 0.796894, 0.583547) UV(0.281463, 0.54487)
|
||||
V 19: Pos(2.047151, 0.596248, 0.329654) Norm(-0.796943, -0.156316, 0.583478) UV(0.281463, 0.55298)
|
||||
V 20: Pos(2.048557, 0.595304, 0.329654) Norm(0.15631, -0.796892, 0.58355) UV(0.274477, 0.550611)
|
||||
V 21: Pos(2.049502, 0.596709, 0.329654) Norm(0.796941, 0.156322, 0.583479) UV(0.274477, 0.542501)
|
||||
V 22: Pos(2.059372, 0.591441, 0.201308) Norm(0.832044, -0.468435, -0.297105) UV(0.540349, 0.551801)
|
||||
V 23: Pos(2.059372, 0.591441, 0.244588) Norm(0.832044, -0.468435, 0.297105) UV(0.540349, 0.390033)
|
||||
V 24: Pos(2.022467, 0.578506, 0.244588) Norm(-0.357515, -0.885388, 0.297105) UV(0.747057, 0.390033)
|
||||
V 25: Pos(2.022467, 0.578506, 0.201308) Norm(-0.357515, -0.885388, -0.297105) UV(0.747057, 0.551801)
|
||||
V 26: Pos(2.027751, 0.681654, 0.19842) Norm(0.496495, 0.488863, -0.717291) UV(0.540349, 0.527237)
|
||||
V 27: Pos(2.026796, 0.684379, 0.201308) Norm(0.357517, 0.885386, -0.297108) UV(0.525084, 0.542501)
|
||||
V 28: Pos(2.026796, 0.684379, 0.244588) Norm(0.357517, 0.885386, 0.297108) UV(0.296309, 0.542501)
|
||||
V 29: Pos(2.027751, 0.681654, 0.247476) Norm(0.496495, 0.488863, 0.717291) UV(0.281045, 0.527237)
|
||||
V 30: Pos(2.058416, 0.594166, 0.247476) Norm(0.693054, -0.071917, 0.717289) UV(0.281045, 0.037206)
|
||||
V 31: Pos(2.059372, 0.591441, 0.244588) Norm(0.832044, -0.468435, 0.297105) UV(0.29631, 0.021942)
|
||||
V 32: Pos(2.059372, 0.591441, 0.201308) Norm(0.832044, -0.468435, -0.297105) UV(0.525084, 0.021942)
|
||||
V 33: Pos(2.058416, 0.594166, 0.19842) Norm(0.693054, -0.071917, -0.71729) UV(0.540349, 0.037206)
|
||||
V 34: Pos(2.027751, 0.681654, 0.247476) Norm(0.496495, 0.488863, 0.717291) UV(0.540349, 0.021942)
|
||||
V 35: Pos(1.990847, 0.668718, 0.247476) Norm(-0.693054, 0.07191, 0.717291) UV(0.747057, 0.021942)
|
||||
V 36: Pos(2.021512, 0.581231, 0.247476) Norm(-0.496494, -0.488866, 0.717289) UV(0.747057, 0.368446)
|
||||
V 37: Pos(2.058416, 0.594166, 0.247476) Norm(0.693054, -0.071917, 0.717289) UV(0.540349, 0.368446)
|
||||
V 38: Pos(2.021512, 0.581231, 0.19842) Norm(-0.496494, -0.488866, -0.717289) UV(0.281045, 0.527237)
|
||||
V 39: Pos(2.022467, 0.578506, 0.201308) Norm(-0.357515, -0.885388, -0.297105) UV(0.265781, 0.542501)
|
||||
V 40: Pos(2.022467, 0.578506, 0.244588) Norm(-0.357515, -0.885388, 0.297105) UV(0.037006, 0.542501)
|
||||
V 41: Pos(2.021512, 0.581231, 0.247476) Norm(-0.496494, -0.488866, 0.717289) UV(0.021742, 0.527237)
|
||||
V 42: Pos(1.990847, 0.668718, 0.247476) Norm(-0.693054, 0.07191, 0.717291) UV(0.021742, 0.037206)
|
||||
V 43: Pos(1.989891, 0.671443, 0.244588) Norm(-0.832042, 0.468434, 0.297111) UV(0.037006, 0.021942)
|
||||
V 44: Pos(1.989891, 0.671443, 0.201308) Norm(-0.832042, 0.468434, -0.297111) UV(0.265781, 0.021942)
|
||||
V 45: Pos(1.990847, 0.668718, 0.19842) Norm(-0.693054, 0.07191, -0.71729) UV(0.281045, 0.037206)
|
||||
V 46: Pos(1.989891, 0.671443, 0.201308) Norm(-0.832042, 0.468434, -0.297111) UV(0.021742, 0.78654)
|
||||
V 47: Pos(1.989891, 0.671443, 0.244588) Norm(-0.832042, 0.468434, 0.297111) UV(0.021742, 0.557766)
|
||||
V 48: Pos(2.026796, 0.684379, 0.244588) Norm(0.357517, 0.885386, 0.297108) UV(0.228451, 0.557766)
|
||||
V 49: Pos(2.026796, 0.684379, 0.201308) Norm(0.357517, 0.885386, -0.297108) UV(0.228451, 0.78654)
|
||||
V 50: Pos(2.027751, 0.681654, 0.19842) Norm(0.496495, 0.488863, -0.717291) UV(0.747058, 0.527237)
|
||||
V 51: Pos(1.990847, 0.668718, 0.19842) Norm(-0.693054, 0.07191, -0.71729) UV(0.953767, 0.527237)
|
||||
V 52: Pos(1.989891, 0.671443, 0.201308) Norm(-0.832042, 0.468434, -0.297111) UV(0.953767, 0.542501)
|
||||
V 53: Pos(2.026796, 0.684379, 0.201308) Norm(0.357517, 0.885386, -0.297108) UV(0.747058, 0.542501)
|
||||
V 54: Pos(1.990847, 0.668718, 0.247476) Norm(-0.693054, 0.07191, 0.717291) UV(0.021742, 0.542501)
|
||||
V 55: Pos(2.027751, 0.681654, 0.247476) Norm(0.496495, 0.488863, 0.717291) UV(0.228451, 0.542501)
|
||||
V 56: Pos(2.021512, 0.581231, 0.19842) Norm(-0.496494, -0.488866, -0.717289) UV(0.953767, 0.037206)
|
||||
V 57: Pos(2.058416, 0.594166, 0.19842) Norm(0.693054, -0.071917, -0.71729) UV(0.747058, 0.037206)
|
||||
V 58: Pos(2.059372, 0.591441, 0.201308) Norm(0.832044, -0.468435, -0.297105) UV(0.747058, 0.021942)
|
||||
V 59: Pos(2.022467, 0.578506, 0.201308) Norm(-0.357515, -0.885388, -0.297105) UV(0.953767, 0.021942)
|
||||
|
||||
===Triangles (Indices): 40
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 8 9 10
|
||||
Tri: 8 10 11
|
||||
Tri: 11 10 12
|
||||
Tri: 11 12 13
|
||||
Tri: 14 15 16
|
||||
Tri: 14 16 17
|
||||
Tri: 18 19 20
|
||||
Tri: 18 20 21
|
||||
Tri: 22 23 24
|
||||
Tri: 22 24 25
|
||||
Tri: 26 27 28
|
||||
Tri: 26 28 29
|
||||
Tri: 26 29 30
|
||||
Tri: 26 30 31
|
||||
Tri: 26 31 32
|
||||
Tri: 26 32 33
|
||||
Tri: 34 35 36
|
||||
Tri: 34 36 37
|
||||
Tri: 38 39 40
|
||||
Tri: 38 40 41
|
||||
Tri: 38 41 42
|
||||
Tri: 38 42 43
|
||||
Tri: 38 43 44
|
||||
Tri: 38 44 45
|
||||
Tri: 46 47 48
|
||||
Tri: 46 48 49
|
||||
Tri: 50 51 52
|
||||
Tri: 50 52 53
|
||||
Tri: 54 55 48
|
||||
Tri: 54 48 47
|
||||
Tri: 56 57 58
|
||||
Tri: 56 58 59
|
||||
Tri: 37 36 24
|
||||
Tri: 37 24 23
|
||||
Tri: 51 50 57
|
||||
Tri: 51 57 56
|
||||
@@ -0,0 +1,223 @@
|
||||
===Vertices (Split by UV/Normal): 120
|
||||
V 0: Pos(1.857603, 0.794636, 0.177222) Norm(-0.904534, -0.301511, -0.301511) UV(0.628715, 0.045661)
|
||||
V 1: Pos(1.857603, 0.794636, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.045661)
|
||||
V 2: Pos(1.857603, 0.816721, 0.200019) Norm(-0.57735, 0.57735, 0.57735) UV(0.60656, 0.024198)
|
||||
V 3: Pos(1.857603, 0.816721, 0.177222) Norm(-0.707107, 0.707107, 0.0) UV(0.628715, 0.024198)
|
||||
V 4: Pos(1.857603, 0.816721, 0.177222) Norm(-0.707107, 0.707107, 0.0) UV(0.046149, 0.975802)
|
||||
V 5: Pos(1.857603, 0.816721, 0.200019) Norm(-0.57735, 0.57735, 0.57735) UV(0.023994, 0.975802)
|
||||
V 6: Pos(2.157339, 0.816721, 0.200019) Norm(0.57735, 0.57735, 0.57735) UV(0.023994, 0.684519)
|
||||
V 7: Pos(2.157339, 0.816721, 0.177222) Norm(0.707107, 0.707107, 0.0) UV(0.046149, 0.684519)
|
||||
V 8: Pos(2.157339, 0.159323, 0.177222) Norm(0.904534, 0.301511, -0.301511) UV(0.809229, 0.045661)
|
||||
V 9: Pos(2.157339, 0.159323, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.045661)
|
||||
V 10: Pos(2.157339, 0.137237, 0.200019) Norm(0.57735, -0.57735, 0.57735) UV(0.787075, 0.024198)
|
||||
V 11: Pos(2.157339, 0.137237, 0.177222) Norm(0.707107, -0.707107, 0.0) UV(0.809229, 0.024198)
|
||||
V 12: Pos(2.157339, 0.137237, 0.177222) Norm(0.707107, -0.707107, 0.0) UV(0.337432, 0.932876)
|
||||
V 13: Pos(2.157339, 0.137237, 0.200019) Norm(0.57735, -0.57735, 0.57735) UV(0.315277, 0.932876)
|
||||
V 14: Pos(1.857603, 0.137237, 0.200019) Norm(-0.57735, -0.57735, 0.57735) UV(0.315277, 0.641593)
|
||||
V 15: Pos(1.857603, 0.137237, 0.177222) Norm(-0.707107, -0.707107, 0.0) UV(0.337432, 0.641593)
|
||||
V 16: Pos(1.857603, 0.159323, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.538718, 0.641593)
|
||||
V 17: Pos(2.157339, 0.159323, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.538718, 0.932876)
|
||||
V 18: Pos(2.157339, 0.137237, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.517255, 0.932876)
|
||||
V 19: Pos(1.857603, 0.137237, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.517255, 0.641593)
|
||||
V 20: Pos(2.157339, 0.159323, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.045661)
|
||||
V 21: Pos(1.857603, 0.159323, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.045661)
|
||||
V 22: Pos(1.857603, 0.137237, 0.200019) Norm(-0.57735, -0.57735, 0.57735) UV(0.023994, 0.024198)
|
||||
V 23: Pos(2.157339, 0.137237, 0.200019) Norm(0.57735, -0.57735, 0.57735) UV(0.315277, 0.024198)
|
||||
V 24: Pos(2.157339, 0.137237, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.495792, 0.932876)
|
||||
V 25: Pos(2.157339, 0.137237, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.405535, 0.932876)
|
||||
V 26: Pos(1.857603, 0.137237, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.405535, 0.641593)
|
||||
V 27: Pos(1.857603, 0.137237, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.495792, 0.641593)
|
||||
V 28: Pos(2.157339, 0.159323, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.96759, 0.045661)
|
||||
V 29: Pos(2.157339, 0.159323, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.877332, 0.045661)
|
||||
V 30: Pos(2.157339, 0.137237, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.877332, 0.024198)
|
||||
V 31: Pos(2.157339, 0.137237, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.96759, 0.024198)
|
||||
V 32: Pos(1.857603, 0.816721, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.204509, 0.975802)
|
||||
V 33: Pos(1.857603, 0.816721, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.114252, 0.975802)
|
||||
V 34: Pos(2.157339, 0.816721, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.114252, 0.684519)
|
||||
V 35: Pos(2.157339, 0.816721, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.204509, 0.684519)
|
||||
V 36: Pos(1.857603, 0.794636, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.787075, 0.045661)
|
||||
V 37: Pos(1.857603, 0.794636, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.696818, 0.045661)
|
||||
V 38: Pos(1.857603, 0.816721, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.696818, 0.024198)
|
||||
V 39: Pos(1.857603, 0.816721, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.787075, 0.024198)
|
||||
V 40: Pos(2.157339, 0.64685, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.519439)
|
||||
V 41: Pos(1.857603, 0.64685, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.519439)
|
||||
V 42: Pos(1.857603, 0.476979, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.354358)
|
||||
V 43: Pos(2.157339, 0.476979, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.354358)
|
||||
V 44: Pos(2.157339, 0.64685, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.519439)
|
||||
V 45: Pos(2.157339, 0.64685, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.519439)
|
||||
V 46: Pos(2.157339, 0.476979, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.354358)
|
||||
V 47: Pos(2.157339, 0.476979, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.354358)
|
||||
V 48: Pos(1.857603, 0.307108, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.519439)
|
||||
V 49: Pos(1.857603, 0.307108, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.519439)
|
||||
V 50: Pos(1.857603, 0.476979, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.354358)
|
||||
V 51: Pos(1.857603, 0.476979, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.354358)
|
||||
V 52: Pos(2.157339, 0.731786, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.601979)
|
||||
V 53: Pos(2.157339, 0.731786, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.601979)
|
||||
V 54: Pos(2.157339, 0.731786, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.601979)
|
||||
V 55: Pos(1.857603, 0.731786, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.601979)
|
||||
V 56: Pos(1.857603, 0.64685, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.189278)
|
||||
V 57: Pos(1.857603, 0.64685, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.189278)
|
||||
V 58: Pos(2.157339, 0.794636, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.663056)
|
||||
V 59: Pos(1.857603, 0.794636, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.663056)
|
||||
V 60: Pos(2.157339, 0.794636, 0.177222) Norm(0.904534, -0.301511, -0.301511) UV(0.809229, 0.663056)
|
||||
V 61: Pos(2.157339, 0.794636, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.663056)
|
||||
V 62: Pos(1.857603, 0.731786, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.106738)
|
||||
V 63: Pos(1.857603, 0.731786, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.106738)
|
||||
V 64: Pos(2.157339, 0.816721, 0.177222) Norm(0.707107, 0.707107, 0.0) UV(0.809229, 0.684519)
|
||||
V 65: Pos(2.157339, 0.816721, 0.200019) Norm(0.57735, 0.57735, 0.57735) UV(0.787075, 0.684519)
|
||||
V 66: Pos(1.857603, 0.816721, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.517255, 0.641593)
|
||||
V 67: Pos(2.157339, 0.816721, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.517255, 0.932876)
|
||||
V 68: Pos(2.157339, 0.794636, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.495792, 0.932876)
|
||||
V 69: Pos(1.857603, 0.794636, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.495792, 0.641593)
|
||||
V 70: Pos(2.157339, 0.816721, 0.200019) Norm(0.57735, 0.57735, 0.57735) UV(0.315277, 0.684519)
|
||||
V 71: Pos(1.857603, 0.816721, 0.200019) Norm(-0.57735, 0.57735, 0.57735) UV(0.023994, 0.684519)
|
||||
V 72: Pos(2.157339, 0.816721, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.96759, 0.684519)
|
||||
V 73: Pos(2.157339, 0.816721, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.877332, 0.684519)
|
||||
V 74: Pos(2.157339, 0.794636, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.877332, 0.663056)
|
||||
V 75: Pos(2.157339, 0.794636, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.96759, 0.663056)
|
||||
V 76: Pos(1.857603, 0.222172, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.601979)
|
||||
V 77: Pos(1.857603, 0.222172, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.601979)
|
||||
V 78: Pos(1.857603, 0.307108, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.189278)
|
||||
V 79: Pos(2.157339, 0.307108, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.189278)
|
||||
V 80: Pos(2.157339, 0.307108, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.189278)
|
||||
V 81: Pos(2.157339, 0.307108, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.189278)
|
||||
V 82: Pos(1.857603, 0.159323, 0.177222) Norm(-0.904534, 0.301511, -0.301511) UV(0.628715, 0.663056)
|
||||
V 83: Pos(1.857603, 0.159323, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.663056)
|
||||
V 84: Pos(1.857603, 0.222172, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.106738)
|
||||
V 85: Pos(2.157339, 0.222172, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.106738)
|
||||
V 86: Pos(2.157339, 0.222172, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.106738)
|
||||
V 87: Pos(2.157339, 0.222172, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.106738)
|
||||
V 88: Pos(1.857603, 0.137237, 0.177222) Norm(-0.707107, -0.707107, 0.0) UV(0.628715, 0.684519)
|
||||
V 89: Pos(1.857603, 0.137237, 0.200019) Norm(-0.57735, -0.57735, 0.57735) UV(0.60656, 0.684519)
|
||||
V 90: Pos(1.857603, 0.137237, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.787075, 0.684519)
|
||||
V 91: Pos(1.857603, 0.137237, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.696818, 0.684519)
|
||||
V 92: Pos(1.857603, 0.159323, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.696818, 0.663056)
|
||||
V 93: Pos(1.857603, 0.159323, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.787075, 0.663056)
|
||||
V 94: Pos(1.857603, 0.794636, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.923281, 0.684519)
|
||||
V 95: Pos(2.157339, 0.794636, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.923281, 0.975802)
|
||||
V 96: Pos(2.157339, 0.794636, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.833023, 0.975802)
|
||||
V 97: Pos(1.857603, 0.794636, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.833023, 0.684519)
|
||||
V 98: Pos(2.157339, 0.794636, 0.177222) Norm(0.904534, -0.301511, -0.301511) UV(0.76492, 0.975802)
|
||||
V 99: Pos(1.857603, 0.794636, 0.177222) Norm(-0.904534, -0.301511, -0.301511) UV(0.76492, 0.684519)
|
||||
V 100: Pos(2.157339, 0.794636, 0.177222) Norm(0.904534, -0.301511, -0.301511) UV(0.315277, 0.641593)
|
||||
V 101: Pos(2.157339, 0.731786, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.580516)
|
||||
V 102: Pos(1.857603, 0.731786, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.580516)
|
||||
V 103: Pos(1.857603, 0.794636, 0.177222) Norm(-0.904534, -0.301511, -0.301511) UV(0.60656, 0.641593)
|
||||
V 104: Pos(2.157339, 0.64685, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.497976)
|
||||
V 105: Pos(1.857603, 0.64685, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.497976)
|
||||
V 106: Pos(2.157339, 0.476979, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.332896)
|
||||
V 107: Pos(1.857603, 0.476979, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.332896)
|
||||
V 108: Pos(1.857603, 0.307108, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.167815)
|
||||
V 109: Pos(2.157339, 0.307108, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.167815)
|
||||
V 110: Pos(1.857603, 0.222172, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.085275)
|
||||
V 111: Pos(2.157339, 0.222172, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.085275)
|
||||
V 112: Pos(1.857603, 0.159323, 0.177222) Norm(-0.904534, 0.301511, -0.301511) UV(0.60656, 0.024198)
|
||||
V 113: Pos(2.157339, 0.159323, 0.177222) Norm(0.904534, 0.301511, -0.301511) UV(0.315277, 0.024198)
|
||||
V 114: Pos(1.857603, 0.159323, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.674663, 0.975802)
|
||||
V 115: Pos(1.857603, 0.159323, 0.177222) Norm(-0.904534, 0.301511, -0.301511) UV(0.60656, 0.975802)
|
||||
V 116: Pos(2.157339, 0.159323, 0.177222) Norm(0.904534, 0.301511, -0.301511) UV(0.60656, 0.684519)
|
||||
V 117: Pos(2.157339, 0.159323, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.674663, 0.684519)
|
||||
V 118: Pos(1.857603, 0.159323, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.76492, 0.975802)
|
||||
V 119: Pos(2.157339, 0.159323, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.76492, 0.684519)
|
||||
|
||||
===Triangles (Indices): 100
|
||||
Tri: 0 1 2
|
||||
Tri: 0 2 3
|
||||
Tri: 4 5 6
|
||||
Tri: 4 6 7
|
||||
Tri: 8 9 10
|
||||
Tri: 8 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 12 14 15
|
||||
Tri: 16 17 18
|
||||
Tri: 16 18 19
|
||||
Tri: 20 21 22
|
||||
Tri: 20 22 23
|
||||
Tri: 24 25 26
|
||||
Tri: 24 26 27
|
||||
Tri: 28 29 30
|
||||
Tri: 28 30 31
|
||||
Tri: 32 33 34
|
||||
Tri: 32 34 35
|
||||
Tri: 36 37 38
|
||||
Tri: 36 38 39
|
||||
Tri: 25 12 15
|
||||
Tri: 25 15 26
|
||||
Tri: 29 8 11
|
||||
Tri: 29 11 30
|
||||
Tri: 33 4 7
|
||||
Tri: 33 7 34
|
||||
Tri: 37 0 3
|
||||
Tri: 37 3 38
|
||||
Tri: 40 41 42
|
||||
Tri: 40 42 43
|
||||
Tri: 44 45 46
|
||||
Tri: 44 46 47
|
||||
Tri: 48 49 50
|
||||
Tri: 48 50 51
|
||||
Tri: 52 53 45
|
||||
Tri: 52 45 44
|
||||
Tri: 54 55 41
|
||||
Tri: 54 41 40
|
||||
Tri: 51 50 56
|
||||
Tri: 51 56 57
|
||||
Tri: 58 59 55
|
||||
Tri: 58 55 54
|
||||
Tri: 60 61 53
|
||||
Tri: 60 53 52
|
||||
Tri: 57 56 62
|
||||
Tri: 57 62 63
|
||||
Tri: 64 65 61
|
||||
Tri: 64 61 60
|
||||
Tri: 66 67 68
|
||||
Tri: 66 68 69
|
||||
Tri: 70 71 59
|
||||
Tri: 70 59 58
|
||||
Tri: 72 73 74
|
||||
Tri: 72 74 75
|
||||
Tri: 73 64 60
|
||||
Tri: 73 60 74
|
||||
Tri: 63 62 1
|
||||
Tri: 63 1 0
|
||||
Tri: 76 77 49
|
||||
Tri: 76 49 48
|
||||
Tri: 43 42 78
|
||||
Tri: 43 78 79
|
||||
Tri: 47 46 80
|
||||
Tri: 47 80 81
|
||||
Tri: 82 83 77
|
||||
Tri: 82 77 76
|
||||
Tri: 79 78 84
|
||||
Tri: 79 84 85
|
||||
Tri: 81 80 86
|
||||
Tri: 81 86 87
|
||||
Tri: 88 89 83
|
||||
Tri: 88 83 82
|
||||
Tri: 90 91 92
|
||||
Tri: 90 92 93
|
||||
Tri: 91 88 82
|
||||
Tri: 91 82 92
|
||||
Tri: 85 84 21
|
||||
Tri: 85 21 20
|
||||
Tri: 87 86 9
|
||||
Tri: 87 9 8
|
||||
Tri: 94 95 96
|
||||
Tri: 94 96 97
|
||||
Tri: 96 98 99
|
||||
Tri: 96 99 97
|
||||
Tri: 100 101 102
|
||||
Tri: 100 102 103
|
||||
Tri: 101 104 105
|
||||
Tri: 101 105 102
|
||||
Tri: 104 106 107
|
||||
Tri: 104 107 105
|
||||
Tri: 108 107 106
|
||||
Tri: 108 106 109
|
||||
Tri: 110 108 109
|
||||
Tri: 110 109 111
|
||||
Tri: 112 110 111
|
||||
Tri: 112 111 113
|
||||
Tri: 114 115 116
|
||||
Tri: 114 116 117
|
||||
Tri: 118 114 117
|
||||
Tri: 118 117 119
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 30,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "aboutImage",
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"texture": "resources/e/menu/about.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "backButton",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/button_back.png",
|
||||
"hover": "resources/e/menu/button_back.png",
|
||||
"pressed": "resources/e/menu/button_back.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"objects": [
|
||||
{
|
||||
"name": "firebox",
|
||||
"texturePath": "resources/w/Cube001.png",
|
||||
"meshPath": "resources/w/firebox.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "inai",
|
||||
"texturePath": "resources/w/inai001.png",
|
||||
"meshPath": "resources/w/inai001.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 2.5,
|
||||
"positionY": 1.4,
|
||||
"positionZ": -9.9,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "bench",
|
||||
"texturePath": "resources/w/bench001opt.png",
|
||||
"meshPath": "resources/w/bench002opt.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 3.141592653589793,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -2.1,
|
||||
"positionY": 0.5,
|
||||
"positionZ": -7.9,
|
||||
"scale": 3.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "medik",
|
||||
"texturePath": "resources/w/red.png",
|
||||
"meshPath": "resources/w/firebox.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 4.0,
|
||||
"scale": 1.0,
|
||||
"interactive": true,
|
||||
"item": {
|
||||
"id": "health_potion",
|
||||
"name": "Health Potion",
|
||||
"description": "Restores 50 HP",
|
||||
"icon": "resources/w/red.png",
|
||||
"radius": 2.0
|
||||
},
|
||||
"activateFunction" : "on_health_pickup"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"objects": [
|
||||
{
|
||||
"name": "firebox",
|
||||
"texturePath": "resources/w/Cube001.png",
|
||||
"meshPath": "resources/w/firebox.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "medik",
|
||||
"texturePath": "resources/w/red.png",
|
||||
"meshPath": "resources/w/firebox.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 4.0,
|
||||
"scale": 1.0,
|
||||
"interactive": true,
|
||||
"item": {
|
||||
"id": "health_potion",
|
||||
"name": "Health Potion",
|
||||
"description": "Restores 50 HP",
|
||||
"icon": "resources/w/red.png",
|
||||
"radius": 2.0
|
||||
},
|
||||
"activateFunction" : "on_health_pickup"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 30,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "helpImage",
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"texture": "resources/e/menu/help.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "backButton",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/button_back.png",
|
||||
"hover": "resources/e/menu/button_back.png",
|
||||
"pressed": "resources/e/menu/button_back.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 20,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "title",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"texture": "resources/e/menu/title.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "startButton",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/button_start.png",
|
||||
"hover": "resources/e/menu/button_start.png",
|
||||
"pressed": "resources/e/menu/button_start.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "helpButton",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/button_help.png",
|
||||
"hover": "resources/e/menu/button_help.png",
|
||||
"pressed": "resources/e/menu/button_help.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "aboutButton",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/button_about.png",
|
||||
"hover": "resources/e/menu/button_about.png",
|
||||
"pressed": "resources/e/menu/button_about.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 20,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "backButton",
|
||||
"width": 512,
|
||||
"height": 256,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/hint1.png",
|
||||
"hover": "resources/e/menu/hint1.png",
|
||||
"pressed": "resources/e/menu/hint1.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 20,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "title",
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"texture": "resources/e/menu/final_loose.png"
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "backButton",
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"textures": {
|
||||
"normal": "resources/e/menu/restart_button.png",
|
||||
"hover": "resources/e/menu/restart_button.png",
|
||||
"pressed": "resources/e/menu/restart_button.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "center",
|
||||
"spacing": 20,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "title",
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"texture": "resources/e/menu/final_win.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"cellSize": 0.4,
|
||||
"agentRadius": 0.45,
|
||||
"floorY": 0.0,
|
||||
"objectPadding": 0.25,
|
||||
"areas": [
|
||||
{
|
||||
"name": "main_corridor",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-2.2, 0.8],
|
||||
[2.2, 0.8],
|
||||
[2.2, -40.8],
|
||||
[-2.2, -40.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -4.2],
|
||||
[-2.0, -4.2],
|
||||
[-2.0, -5.8],
|
||||
[-3.4, -5.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -4.2],
|
||||
[3.4, -4.2],
|
||||
[3.4, -5.8],
|
||||
[2.0, -5.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -1.0],
|
||||
[-3.0, -1.0],
|
||||
[-3.0, -9.0],
|
||||
[-8.8, -9.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -1.0],
|
||||
[8.8, -1.0],
|
||||
[8.8, -9.0],
|
||||
[3.0, -9.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -14.2],
|
||||
[-2.0, -14.2],
|
||||
[-2.0, -15.8],
|
||||
[-3.4, -15.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -14.2],
|
||||
[3.4, -14.2],
|
||||
[3.4, -15.8],
|
||||
[2.0, -15.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -11.0],
|
||||
[-3.0, -11.0],
|
||||
[-3.0, -19.0],
|
||||
[-8.8, -19.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -11.0],
|
||||
[8.8, -11.0],
|
||||
[8.8, -19.0],
|
||||
[3.0, -19.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -24.2],
|
||||
[-2.0, -24.2],
|
||||
[-2.0, -25.8],
|
||||
[-3.4, -25.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -24.2],
|
||||
[3.4, -24.2],
|
||||
[3.4, -25.8],
|
||||
[2.0, -25.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -21.0],
|
||||
[-3.0, -21.0],
|
||||
[-3.0, -29.0],
|
||||
[-8.8, -29.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -21.0],
|
||||
[8.8, -21.0],
|
||||
[8.8, -29.0],
|
||||
[3.0, -29.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -34.2],
|
||||
[-2.0, -34.2],
|
||||
[-2.0, -35.8],
|
||||
[-3.4, -35.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -34.2],
|
||||
[3.4, -34.2],
|
||||
[3.4, -35.8],
|
||||
[2.0, -35.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -31.0],
|
||||
[-3.0, -31.0],
|
||||
[-3.0, -39.0],
|
||||
[-8.8, -39.0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -31.0],
|
||||
[8.8, -31.0],
|
||||
[8.8, -39.0],
|
||||
[3.0, -39.0]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
{
|
||||
"cellSize": 0.4,
|
||||
"agentRadius": 0.45,
|
||||
"floorY": 0.0,
|
||||
"objectPadding": 0.25,
|
||||
"areas": [
|
||||
{
|
||||
"name": "main_corridor",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-32, 15],
|
||||
[-2, 15],
|
||||
[-2, 2.35],
|
||||
[-32, 2.35]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor1",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-2, 3.2],
|
||||
[-14.1, 3.2],
|
||||
[-14.1, -25],
|
||||
[-2, -25]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor2",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-32, 5],
|
||||
[-26.9, 5],
|
||||
[-26.9, -25],
|
||||
[-32, -25]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor3",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-32, -5.5],
|
||||
[-7.3, -5.5],
|
||||
[-7.3, -8.6],
|
||||
[-32, -8.6]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor4",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-32, -25],
|
||||
[-25.7, -25],
|
||||
[-25.7, -8.6],
|
||||
[-32, -8.6]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor5",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-2, -25],
|
||||
[-14.89, -25],
|
||||
[-14.89, -8.6],
|
||||
[-2, -8.6]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor6",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-2, -25],
|
||||
[-32, -25],
|
||||
[-32, -13.5],
|
||||
[-2, -13.5]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor7",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-16.9, -25],
|
||||
[-24, -25],
|
||||
[-24, -8.5],
|
||||
[-16.9, -8.5]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor8",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-26.1, 1.5],
|
||||
[-14.9, 1.5],
|
||||
[-14.9, -4.8],
|
||||
[-26.1, -4.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor9",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-14, -1.2],
|
||||
[-16, -1.2],
|
||||
[-16, 0],
|
||||
[-14, 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_corridor10",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-17.2, -3],
|
||||
[-16, -3],
|
||||
[-16, -5.5],
|
||||
[-17.2, -5.5]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "extension_left",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-400, -400],
|
||||
[-32, -400],
|
||||
[-32, 400],
|
||||
[-400, 400]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "extension_right",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-2, -400],
|
||||
[400, -400],
|
||||
[400, 400],
|
||||
[-2, 400]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "extension_bottom",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-32, -400],
|
||||
[-2, -400],
|
||||
[-2, -25],
|
||||
[-32, -25]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "extension_top",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-32, 15],
|
||||
[-2, 15],
|
||||
[-2, 400],
|
||||
[-32, 400]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cor",
|
||||
"available": true,
|
||||
"forbidden": true,
|
||||
"polygon": [
|
||||
[-26.1, 1.5],
|
||||
[-17.2, 1.5],
|
||||
[-17.2, 0.5],
|
||||
[-26.1, 0.5]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_cor2",
|
||||
"available": true,
|
||||
"forbidden": true,
|
||||
"polygon": [
|
||||
[-22.18, -1.2],
|
||||
[-17.2, -1.2],
|
||||
[-17.2, -2.8],
|
||||
[-22.18, -2.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_cor3",
|
||||
"available": true,
|
||||
"forbidden": true,
|
||||
"polygon": [
|
||||
[-25.69, -2.6],
|
||||
[-22.5, -2.6],
|
||||
[-22.5, -3.8],
|
||||
[-25.69, -3.8]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main_cor4",
|
||||
"available": true,
|
||||
"forbidden": true,
|
||||
"polygon": [
|
||||
[-23.6, -2.9],
|
||||
[-22.2, -2.9],
|
||||
[-22.2, -4.8],
|
||||
[-23.6, -4.8]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"cellSize": 0.4,
|
||||
"agentRadius": 0.45,
|
||||
"floorY": 0.0,
|
||||
"objectPadding": 0.25,
|
||||
"areas": [
|
||||
{
|
||||
"name": "main_corridor",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[26, 9.5],
|
||||
[14, 9.5],
|
||||
[14, -8.7],
|
||||
[26, -8.7]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"npcs": [
|
||||
{
|
||||
"id": "npc_01_default",
|
||||
"name": "NPC Default Guard",
|
||||
"texturePath": "resources/w/default_skin001.png",
|
||||
"animationIdlePath": "resources/w/default_idle002.anim",
|
||||
"animationWalkPath": "resources/w/default_walk001.anim",
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -10.0,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0,
|
||||
"gift": {
|
||||
"id": "guard_token",
|
||||
"name": "Guard's Token",
|
||||
"description": "A token from the Guard - sign of respect",
|
||||
"icon": "resources/w/red.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "npc_03_ghost",
|
||||
"name": "NPC Floating Ghost",
|
||||
"texturePath": "resources/w/ghost_skin001.png",
|
||||
"animationIdlePath": "resources/w/default_float001.anim",
|
||||
"animationWalkPath": "resources/w/default_float001.anim",
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -5.0,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 0.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 1.0,
|
||||
"gift": {
|
||||
"id": "ghost_essence",
|
||||
"name": "Ghost's Essence",
|
||||
"description": "A mysterious essence from the Ghost realm",
|
||||
"icon": "resources/w/red.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"npcs": [
|
||||
{
|
||||
"id": "npc_03_ghost",
|
||||
"name": "NPC Floating Ghost",
|
||||
"texturePath": "resources/w/ghost_skin001.png",
|
||||
"animationIdlePath": "resources/w/default_float001.anim",
|
||||
"animationWalkPath": "resources/w/default_float001.anim",
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -5.0,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 0.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 1.0,
|
||||
"gift": {
|
||||
"id": "ghost_essence",
|
||||
"name": "Ghost's Essence",
|
||||
"description": "A mysterious essence from the Ghost realm",
|
||||
"icon": "resources/w/red.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"name": "inventory_root",
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "inventory_button",
|
||||
"x": 50.0,
|
||||
"y": 50.0,
|
||||
"width": 150.0,
|
||||
"height": 60.0,
|
||||
"text": "Inventory",
|
||||
"fontSize": 24,
|
||||
"fontPath": "resources/fonts/DroidSans.ttf",
|
||||
"textCentered": true,
|
||||
"color": [1.0, 1.0, 1.0, 1.0],
|
||||
"textures": {
|
||||
"normal": "resources/w/red.png",
|
||||
"hover": "resources/w/red.png",
|
||||
"pressed": "resources/w/red.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FrameLayout",
|
||||
"name": "inventory_items_panel",
|
||||
"x": 50.0,
|
||||
"y": 150.0,
|
||||
"width": 250.0,
|
||||
"height": 300.0,
|
||||
"children": [
|
||||
{
|
||||
"type": "StaticImage",
|
||||
"name": "panel_background",
|
||||
"width": 200,
|
||||
"height": 400,
|
||||
"texture": "resources/w/red.png"
|
||||
},
|
||||
{
|
||||
"type": "TextView",
|
||||
"name": "inventory_items_text",
|
||||
"x": -100.0,
|
||||
"y": -100.0,
|
||||
"width": 250.0,
|
||||
"height": 300.0,
|
||||
"text": "Inventory (Empty)",
|
||||
"fontSize": 18,
|
||||
"fontPath": "resources/fonts/DroidSans.ttf",
|
||||
"centered": false,
|
||||
"color": [1.0, 1.0, 1.0, 1.0]
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "close_inventory_button",
|
||||
"x": 165.0,
|
||||
"y": 0.0,
|
||||
"width": 40.0,
|
||||
"height": 40.0,
|
||||
"text": "X",
|
||||
"fontSize": 20,
|
||||
"fontPath": "resources/fonts/DroidSans.ttf",
|
||||
"textCentered": true,
|
||||
"color": [1.0, 1.0, 1.0, 1.0],
|
||||
"textures": {
|
||||
"normal": "resources/w/blue.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Loaded 100 of 2437 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user