New type of anim

This commit is contained in:
Vladislav Khorev 2026-04-18 16:41:17 +03:00
parent c198f39515
commit b3a5e36e1b
20 changed files with 1407002 additions and 244 deletions

View File

@ -30,6 +30,8 @@ add_executable(space-game001
../src/AudioPlayerAsync.h ../src/AudioPlayerAsync.h
../src/BoneAnimatedModel.cpp ../src/BoneAnimatedModel.cpp
../src/BoneAnimatedModel.h ../src/BoneAnimatedModel.h
../src/BoneAnimatedModelNew.cpp
../src/BoneAnimatedModelNew.h
../src/render/OpenGlExtensions.cpp ../src/render/OpenGlExtensions.cpp
../src/render/OpenGlExtensions.h ../src/render/OpenGlExtensions.h
../src/utils/Utils.cpp ../src/utils/Utils.cpp

BIN
resources/e/female_packed0_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/e/female_packed1_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/e/female_packed2_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/e/female_packed3_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
resources/e/male_packed0_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/e/male_packed1_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/e/male_packed2_diffuse.png (Stored with Git LFS) Normal file

Binary file not shown.

237188
resources/e/woman_run001.txt Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,684 @@
#include "BoneAnimatedModelNew.h"
#include <regex>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cstring>
namespace ZL
{
#ifdef EMSCRIPTEN
using std::min;
using std::max;
#endif
extern int getIndexByValue(const std::string& name, const std::vector<std::string>& words);
static std::string trimRight(const std::string& s)
{
size_t end = s.size();
while (end > 0 && (s[end - 1] == '\r' || s[end - 1] == '\n' || s[end - 1] == ' ' || s[end - 1] == '\t'))
end--;
return s.substr(0, end);
}
void BoneSystemNew::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);
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;
std::string tempLine;
// ---- Armature matrix (4 lines after the header) ----
std::getline(f, tempLine); // === Armature Matrix ===
armatureMatrix = Matrix4f::Identity();
for (int r = 0; r < 4; r++)
{
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;
}
if (floatValues.size() >= 4)
{
for (int c = 0; c < 4; c++)
armatureMatrix.data()[r + c * 4] = floatValues[c];
}
}
// ---- Bones ----
std::getline(f, tempLine); // === Armature Bones: N
int numberBones;
if (std::regex_search(tempLine, match, pattern_count)) {
numberBones = std::stoi(match.str());
}
else {
throw std::runtime_error("Armature bones count not found");
}
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); // Bone: name
std::string boneName = trimRight(tempLine.substr(6));
boneNames[i] = boneName;
std::getline(f, tempLine); // HEAD_LOCAL
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); // TAIL_LOCAL
std::getline(f, tempLine); // Length
if (std::regex_search(tempLine, match, pattern_float)) {
bones[i].boneLength = std::stof(match.str());
}
else {
throw std::runtime_error("Bone length not found");
}
// 3x3 matrix
for (int r = 0; r < 3; r++)
{
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.data()[r] = floatValues[0];
bones[i].boneMatrixWorld.data()[r + 1 * 3] = floatValues[1];
bones[i].boneMatrixWorld.data()[r + 2 * 3] = floatValues[2];
}
std::getline(f, tempLine); // Parent
std::string parentLine = trimRight(tempLine);
if (parentLine == " Parent: None")
{
bones[i].parent = -1;
}
else
{
boneParentNames[i] = parentLine.substr(10);
}
std::getline(f, tempLine); // Children
auto bc = tempLine.cbegin();
auto ec = tempLine.cend();
while (std::regex_search(bc, ec, match, pattern_boneChildren)) {
boneChildren[boneName].push_back(match.str(1));
bc = match.suffix().first;
}
}
// Resolve parent/child indices
for (int i = 0; i < numberBones; i++)
{
const std::string& boneName = boneNames[i];
const std::string& boneParent = boneParentNames[i];
if (boneParent == "" || boneParent == "None")
bones[i].parent = -1;
else
bones[i].parent = getIndexByValue(boneParent, boneNames);
for (size_t j = 0; j < boneChildren[boneName].size(); j++)
bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames));
}
startBones = bones;
currentBones = bones;
// ---- Multi-mesh header ----
std::getline(f, tempLine); // === TOTAL MESHES TO EXPORT: N ===
int numberMeshes;
if (std::regex_search(tempLine, match, pattern_count)) {
numberMeshes = std::stoi(match.str());
}
else {
throw std::runtime_error("Total meshes count not found");
}
for (int meshIdx = 0; meshIdx < numberMeshes; meshIdx++)
{
// === Mesh Object: Name ===
std::getline(f, tempLine);
std::string meshHeader = trimRight(tempLine);
// Extract mesh name: strip "=== Mesh Object: " prefix and " ===" suffix
const std::string prefix = "=== Mesh Object: ";
const std::string suffix = " ===";
std::string meshName;
if (meshHeader.size() >= prefix.size() + suffix.size() &&
meshHeader.compare(0, prefix.size(), prefix) == 0 &&
meshHeader.compare(meshHeader.size() - suffix.size(), suffix.size(), suffix) == 0)
{
meshName = meshHeader.substr(prefix.size(),
meshHeader.size() - prefix.size() - suffix.size());
}
else
{
throw std::runtime_error("Invalid mesh header: " + meshHeader);
}
MeshBoneData meshData;
// Vertices
std::getline(f, tempLine); // ===Vertices: N
int numberVertices;
if (std::regex_search(tempLine, match, pattern_count)) {
numberVertices = std::stoi(match.str());
}
else {
throw std::runtime_error("Vertex count not found for mesh " + meshName);
}
std::vector<Vector3f> vertices(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] };
}
// UV coordinates
std::getline(f, tempLine); // ===UV Coordinates:
std::getline(f, tempLine); // Face count: M
int numberFaces;
if (std::regex_search(tempLine, match, pattern_count)) {
numberFaces = std::stoi(match.str());
}
else {
throw std::runtime_error("Face count not found for mesh " + meshName);
}
std::vector<std::array<Vector2f, 3>> uvCoords(numberFaces);
for (int i = 0; i < numberFaces; i++)
{
std::getline(f, tempLine); // Face X
std::getline(f, tempLine); // UV Count: 3
int uvCount;
if (std::regex_search(tempLine, match, pattern_count)) {
uvCount = std::stoi(match.str());
}
else {
throw std::runtime_error("UV count not found");
}
if (uvCount != 3)
throw std::runtime_error("more than 3 uvs");
for (int j = 0; j < 3; j++)
{
std::getline(f, tempLine); // UV <Vector (u, v)>
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;
}
if (floatValues.size() != 2)
throw std::runtime_error("more than 2 uvs---");
uvCoords[i][j] = Vector2f{ floatValues[0], floatValues[1] };
}
}
// Normals
std::getline(f, tempLine); // ===Normals:
std::vector<Vector3f> normals(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] };
}
// Triangles
std::getline(f, tempLine); // ===Triangles: M
int numberTriangles;
if (std::regex_search(tempLine, match, pattern_count)) {
numberTriangles = std::stoi(match.str());
}
else {
throw std::runtime_error("Triangle count not found for mesh " + meshName);
}
std::vector<std::array<int, 3>> triangles(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] };
}
// Vertex weights
std::getline(f, tempLine); // === Vertex Weights (Max 5 bones per vertex) ===
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight(numberVertices);
for (int i = 0; i < numberVertices; i++)
{
std::getline(f, tempLine); // Vertex X:
std::getline(f, tempLine); // Vertex groups: K
int boneCount;
if (std::regex_search(tempLine, match, pattern_count)) {
boneCount = std::stoi(match.str());
}
else {
throw std::runtime_error("Vertex group count not found");
}
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);
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 = static_cast<float>(weight);
sumWeights += static_cast<float>(weight);
}
else {
throw std::runtime_error("No match found in bone weight line");
}
}
for (int j = 0; j < boneCount; j++)
localVerticesBoneWeight[i][j].weight /= sumWeights;
}
// Build per-triangle expanded mesh
for (int i = 0; i < numberTriangles; i++)
{
meshData.mesh.PositionData.push_back(vertices[triangles[i][0]]);
meshData.mesh.PositionData.push_back(vertices[triangles[i][1]]);
meshData.mesh.PositionData.push_back(vertices[triangles[i][2]]);
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);
meshData.mesh.TexCoordData.push_back(uvCoords[i][0]);
meshData.mesh.TexCoordData.push_back(uvCoords[i][1]);
meshData.mesh.TexCoordData.push_back(uvCoords[i][2]);
}
meshData.startMesh = meshData.mesh;
meshes[meshName] = std::move(meshData);
meshNamesOrdered.push_back(meshName);
}
std::cout << "Loaded " << numberMeshes << " meshes from " << fileName << std::endl;
// ---- Animation Keyframes ----
std::getline(f, tempLine); // === Animation Keyframes ===
std::getline(f, tempLine); // === Bone Transforms per Keyframe ===
std::getline(f, tempLine); // Keyframes: N
int numberKeyFrames;
if (std::regex_search(tempLine, match, pattern_count)) {
numberKeyFrames = std::stoi(match.str());
}
else {
throw std::runtime_error("Keyframe count not found");
}
animations.resize(1);
animations[0].keyFrames.resize(numberKeyFrames);
for (int i = 0; i < numberKeyFrames; i++)
{
std::getline(f, tempLine); // Frame: N
int numberFrame;
if (std::regex_search(tempLine, match, pattern_count)) {
numberFrame = std::stoi(match.str());
}
else {
throw std::runtime_error("Frame number not found");
}
animations[0].keyFrames[i].frame = numberFrame;
animations[0].keyFrames[i].bones.resize(numberBones);
for (int j = 0; j < numberBones; j++)
{
std::getline(f, tempLine); // Bone: name
std::string boneName = trimRight(tempLine.substr(8));
int boneNumber = getIndexByValue(boneName, boneNames);
animations[0].keyFrames[i].bones[boneNumber] = startBones[boneNumber];
std::getline(f, tempLine); // Location
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:
for (int r = 0; r < 4; r++)
{
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.data()[r] = floatValues[0];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 1 * 4] = floatValues[1];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 2 * 4] = floatValues[2];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 3 * 4] = floatValues[3];
}
}
}
if (startBones.size() > MAX_GPU_BONES)
{
std::cout << "Warning: model has " << startBones.size()
<< " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl;
}
}
void BoneSystemNew::Interpolate(int frame)
{
int startingKeyFrame = -1;
for (int i = 0; i < static_cast<int>(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)
{
startingKeyFrame = i;
break;
}
}
if (startingKeyFrame == -1)
{
std::cout << "Exception here: frame number is out of range of keyframes. Frame: " << frame << std::endl;
throw std::runtime_error("Exception here");
}
int modifiedFrameNumber = frame - animations[0].keyFrames[startingKeyFrame].frame;
int diffFrames = animations[0].keyFrames[startingKeyFrame + 1].frame - animations[0].keyFrames[startingKeyFrame].frame;
float t = (modifiedFrameNumber + 0.f) / diffFrames;
std::vector<Bone>& oneFrameBones = animations[0].keyFrames[startingKeyFrame].bones;
std::vector<Bone>& nextFrameBones = animations[0].keyFrames[startingKeyFrame + 1].bones;
std::vector<Matrix4f> skinningMatrixForEachBone(currentBones.size());
for (size_t i = 0; i < currentBones.size(); i++)
{
currentBones[i].boneStartWorld(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0));
currentBones[i].boneStartWorld(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1));
currentBones[i].boneStartWorld(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2));
Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized();
Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized();
Eigen::Quaternionf result = q1.slerp(t, q2);
Matrix3f boneMatrixWorld3 = result.toRotationMatrix();
currentBones[i].boneMatrixWorld = Eigen::Matrix4f::Identity();
currentBones[i].boneMatrixWorld.block<3, 3>(0, 0) = boneMatrixWorld3;
currentBones[i].boneMatrixWorld.block<3, 1>(0, 3) = currentBones[i].boneStartWorld;
Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
skinningMatrixForEachBone[i] = currentBones[i].boneMatrixWorld * startBoneMatrixWorld4.inverse();
}
for (const auto& name : meshNamesOrdered)
{
auto it = meshes.find(name);
if (it == meshes.end()) continue;
MeshBoneData& md = it->second;
for (size_t i = 0; i < md.mesh.PositionData.size(); i++)
{
Vector4f originalPos = {
md.startMesh.PositionData[i](0),
md.startMesh.PositionData[i](1),
md.startMesh.PositionData[i](2), 1.0f };
Vector4f finalPos = Vector4f{ 0.f, 0.f, 0.f, 0.f };
bool vMoved = false;
for (int j = 0; j < MAX_BONE_COUNT; j++)
{
if (md.verticesBoneWeight[i][j].weight != 0)
{
if (md.verticesBoneWeight[i][j].boneIndex == -1)
{
std::cout << "Exception here: bone index is -1 but weight is > 0" << std::endl;
throw std::runtime_error("Bones loaded incorrectly - bone index is -1 but weight is > 0");
}
vMoved = true;
finalPos = finalPos + (skinningMatrixForEachBone[md.verticesBoneWeight[i][j].boneIndex] * originalPos) * md.verticesBoneWeight[i][j].weight;
}
}
if (!vMoved) finalPos = originalPos;
md.mesh.PositionData[i](0) = finalPos(0);
md.mesh.PositionData[i](1) = finalPos(1);
md.mesh.PositionData[i](2) = finalPos(2);
}
}
}
void MeshGpuSkinningData::prepareGpuSkinningVBOs(MeshBoneData& meshData)
{
if (gpuSkinningPrepared) return;
gpuBoneData.PrepareGpuSkinningData(meshData.verticesBoneWeight);
bindPoseMutable.AssignFrom(meshData.startMesh);
auto& gpu = gpuBoneData;
boneIndices0VBO = std::make_shared<VBOHolder>();
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
glBufferData(GL_ARRAY_BUFFER, gpu.boneIndices0.size() * sizeof(Eigen::Vector4f),
gpu.boneIndices0.data(), GL_STATIC_DRAW);
boneIndices1VBO = std::make_shared<VBOHolder>();
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
glBufferData(GL_ARRAY_BUFFER, gpu.boneIndices1.size() * sizeof(Eigen::Vector2f),
gpu.boneIndices1.data(), GL_STATIC_DRAW);
boneWeights0VBO = std::make_shared<VBOHolder>();
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
glBufferData(GL_ARRAY_BUFFER, gpu.boneWeights0.size() * sizeof(Eigen::Vector4f),
gpu.boneWeights0.data(), GL_STATIC_DRAW);
boneWeights1VBO = std::make_shared<VBOHolder>();
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
glBufferData(GL_ARRAY_BUFFER, gpu.boneWeights1.size() * sizeof(Eigen::Vector2f),
gpu.boneWeights1.data(), GL_STATIC_DRAW);
gpuSkinningPrepared = true;
}
void MeshGpuSkinningData::RenderVBO(Renderer& renderer)
{
CheckGlError(__FILE__, __LINE__);
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer());
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
CheckGlError(__FILE__, __LINE__);
if (bindPoseMutable.texCoordVBO) {
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer());
renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL);
CheckGlError(__FILE__, __LINE__);
}
if (bindPoseMutable.normalVBO) {
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.normalVBO->getBuffer());
renderer.VertexAttribPointer3fv("vNormal", 0, NULL);
CheckGlError(__FILE__, __LINE__);
} else {
renderer.DisableVertexAttribArray("vNormal");
CheckGlError(__FILE__, __LINE__);
}
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
CheckGlError(__FILE__, __LINE__);
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
CheckGlError(__FILE__, __LINE__);
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
CheckGlError(__FILE__, __LINE__);
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
CheckGlError(__FILE__, __LINE__);
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(bindPoseMutable.data.PositionData.size()));
}
void GpuSkinningShaderDataNew::prepareGpuSkinningVBOs(BoneSystemNew& model)
{
for (const auto& name : model.meshNamesOrdered)
{
auto it = model.meshes.find(name);
if (it == model.meshes.end()) continue;
perMesh[name].prepareGpuSkinningVBOs(it->second);
}
}
void GpuSkinningShaderDataNew::RenderVBO(Renderer& renderer, const std::vector<std::string>& meshNamesOrdered)
{
for (const auto& name : meshNamesOrdered)
{
auto it = perMesh.find(name);
if (it == perMesh.end()) continue;
it->second.RenderVBO(renderer);
}
}
void GpuSkinningShaderDataNew::ComputeSkinningMatrices(const std::vector<Bone>& startBones, const std::vector<AnimationKeyFrame>& keyFrames, int frame)
{
int startingKeyFrame = -1;
for (size_t i = 0; i < keyFrames.size() - 1; i++)
{
int oldFrame = keyFrames[i].frame;
int nextFrame = keyFrames[i + 1].frame;
if (frame >= oldFrame && frame < nextFrame)
{
startingKeyFrame = static_cast<int>(i);
break;
}
}
skinningMatrices.resize(startBones.size());
if (startingKeyFrame == -1)
{
for (auto& m : skinningMatrices) m = Matrix4f::Identity();
return;
}
int modifiedFrameNumber = frame - keyFrames[startingKeyFrame].frame;
int diffFrames = keyFrames[startingKeyFrame + 1].frame - keyFrames[startingKeyFrame].frame;
float t = (modifiedFrameNumber + 0.f) / diffFrames;
const std::vector<Bone>& oneFrameBones = keyFrames[startingKeyFrame].bones;
const std::vector<Bone>& nextFrameBones = keyFrames[startingKeyFrame + 1].bones;
for (size_t i = 0; i < startBones.size(); i++)
{
Vector3f interpPos;
interpPos(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0));
interpPos(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1));
interpPos(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2));
Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized();
Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized();
Eigen::Quaternionf result = q1.slerp(t, q2);
Matrix3f boneMatrixWorld3 = result.toRotationMatrix();
Matrix4f currentBoneMatrixWorld4 = Eigen::Matrix4f::Identity();
currentBoneMatrixWorld4.block<3, 3>(0, 0) = boneMatrixWorld3;
currentBoneMatrixWorld4.block<3, 1>(0, 3) = interpPos;
Matrix4f startBoneMatrixWorld4 = keyFrames[0].bones[i].boneMatrixWorld;
skinningMatrices[i] = currentBoneMatrixWorld4 * startBoneMatrixWorld4.inverse();
}
}
}

View File

@ -0,0 +1,66 @@
#pragma once
#include "BoneAnimatedModel.h"
#include <unordered_map>
namespace ZL
{
struct MeshBoneData
{
VertexDataStruct mesh;
VertexDataStruct startMesh;
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
};
struct BoneSystemNew
{
std::unordered_map<std::string, MeshBoneData> meshes;
std::vector<std::string> meshNamesOrdered;
Matrix4f armatureMatrix;
std::vector<Bone> startBones;
std::vector<Bone> currentBones;
std::vector<Animation> animations;
int startingFrame = 0;
void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
void Interpolate(int frame);
};
struct MeshGpuSkinningData
{
GpuBoneData gpuBoneData;
VertexRenderStruct bindPoseMutable;
std::shared_ptr<VBOHolder> boneIndices0VBO;
std::shared_ptr<VBOHolder> boneIndices1VBO;
std::shared_ptr<VBOHolder> boneWeights0VBO;
std::shared_ptr<VBOHolder> boneWeights1VBO;
bool gpuSkinningPrepared = false;
void prepareGpuSkinningVBOs(MeshBoneData& meshData);
void RenderVBO(Renderer& renderer);
};
struct GpuSkinningShaderDataNew
{
std::unordered_map<std::string, MeshGpuSkinningData> perMesh;
std::vector<Eigen::Matrix4f> skinningMatrices;
void prepareGpuSkinningVBOs(BoneSystemNew& model);
void RenderVBO(Renderer& renderer, const std::vector<std::string>& meshNamesOrdered);
void ComputeSkinningMatrices(const std::vector<Bone>& startBones, const std::vector<AnimationKeyFrame>& keyFrames, int frame);
};
struct BoneAnimationDataNew
{
BoneSystemNew model;
float currentFrame = 0.f;
int lastFrame = -1;
int totalFrames = 1;
GpuSkinningShaderDataNew gpuSkinningShaderData;
};
};

View File

@ -18,14 +18,17 @@ void Character::loadAnimation(AnimationState state, const std::string& filename,
} }
} }
void Character::loadBinaryAnimation(AnimationState state, const std::string& filename, const std::string& zipFile) { void Character::loadBinaryAnimation(AnimationState state, const std::string& filename, const std::string& zipFile) {
auto& data = animations[state];
throw std::runtime_error("Not supported");
/*auto& data = animations[state];
data.model.LoadFromBinaryFile(filename, zipFile); data.model.LoadFromBinaryFile(filename, zipFile);
if (!data.model.animations.empty() && !data.model.animations[0].keyFrames.empty()) { if (!data.model.animations.empty() && !data.model.animations[0].keyFrames.empty()) {
data.totalFrames = data.model.animations[0].keyFrames.back().frame + 1; data.totalFrames = data.model.animations[0].keyFrames.back().frame + 1;
} }
else { else {
data.totalFrames = 1; data.totalFrames = 1;
} }*/
} }
void Character::setTarget(const Eigen::Vector3f& target, void Character::setTarget(const Eigen::Vector3f& target,
@ -272,7 +275,7 @@ void Character::draw(Renderer& renderer) {
AnimationState drawState = resolveActiveState(); AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState); auto it = animations.find(drawState);
if (it == animations.end() || !texture) return; if (it == animations.end() || texturePerMesh.empty()) return;
renderer.shaderManager.PushShader(defaultShaderName); renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0); renderer.RenderUniform1i(textureUniformName, 0);
@ -288,10 +291,13 @@ void Character::draw(Renderer& renderer) {
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix()); renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second; auto& anim = it->second;
modelMutable.AssignFrom(anim.model.mesh);
modelMutable.RefreshVBO(); for (const auto& [meshName, texture] : texturePerMesh) {
glBindTexture(GL_TEXTURE_2D, texture->getTexID()); modelMutableMap[meshName].AssignFrom(anim.model.meshes[meshName].mesh);
renderer.DrawVertexRenderStruct(modelMutable); modelMutableMap[meshName].RefreshVBO();
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
renderer.DrawVertexRenderStruct(modelMutableMap[meshName]);
}
renderer.PopMatrix(); renderer.PopMatrix();
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
@ -301,7 +307,7 @@ void Character::draw(Renderer& renderer) {
void Character::drawGpuSkinning(Renderer& renderer) { void Character::drawGpuSkinning(Renderer& renderer) {
AnimationState drawState = resolveActiveState(); AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState); auto it = animations.find(drawState);
if (it == animations.end() || !texture) if (it == animations.end() || texturePerMesh.empty())
{ {
return; return;
} }
@ -338,19 +344,26 @@ void Character::drawGpuSkinning(Renderer& renderer) {
static_cast<int>(anim.gpuSkinningShaderData.skinningMatrices.size()), false, static_cast<int>(anim.gpuSkinningShaderData.skinningMatrices.size()), false,
anim.gpuSkinningShaderData.skinningMatrices[0].data()); anim.gpuSkinningShaderData.skinningMatrices[0].data());
glBindTexture(GL_TEXTURE_2D, texture->getTexID()); for (const auto& meshName : anim.model.meshNamesOrdered) {
auto texIt = texturePerMesh.find(meshName);
if (texIt == texturePerMesh.end()) continue;
auto meshIt = anim.gpuSkinningShaderData.perMesh.find(meshName);
if (meshIt == anim.gpuSkinningShaderData.perMesh.end()) continue;
glBindTexture(GL_TEXTURE_2D, texIt->second->getTexID());
// Bind VAO (desktop only)
#ifndef EMSCRIPTEN #ifndef EMSCRIPTEN
#ifndef __ANDROID__ #ifndef __ANDROID__
if (anim.gpuSkinningShaderData.bindPoseMutable.vao) { if (meshIt->second.bindPoseMutable.vao) {
glBindVertexArray(anim.gpuSkinningShaderData.bindPoseMutable.vao->getBuffer()); glBindVertexArray(meshIt->second.bindPoseMutable.vao->getBuffer());
renderer.shaderManager.EnableVertexAttribArrays(); renderer.shaderManager.EnableVertexAttribArrays();
} }
#endif #endif
#endif #endif
anim.gpuSkinningShaderData.RenderVBO(renderer); meshIt->second.RenderVBO(renderer);
}
renderer.PopMatrix(); renderer.PopMatrix();
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
@ -396,6 +409,7 @@ void Character::drawShadowDepth(Renderer& renderer) {
} }
void Character::drawShadowDepthCpu(Renderer& renderer) { void Character::drawShadowDepthCpu(Renderer& renderer) {
/*
AnimationState drawState = resolveActiveState(); AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState); auto it = animations.find(drawState);
if (it == animations.end()) return; if (it == animations.end()) return;
@ -413,11 +427,14 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
modelMutable.RefreshVBO(); modelMutable.RefreshVBO();
renderer.DrawVertexRenderStruct(modelMutable); renderer.DrawVertexRenderStruct(modelMutable);
renderer.PopMatrix(); renderer.PopMatrix();*/
std::cout << "CPU skinning shadow depth rendering is not implemented." << std::endl;
throw std::runtime_error("Not supported");
} }
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) { void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
/*
CheckGlError(__FILE__, __LINE__); CheckGlError(__FILE__, __LINE__);
AnimationState drawState = resolveActiveState(); AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState); auto it = animations.find(drawState);
@ -453,7 +470,9 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
renderer.PopMatrix(); renderer.PopMatrix();
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(__FILE__, __LINE__); CheckGlError(__FILE__, __LINE__);*/
std::cout << "CPU skinning shadow depth rendering is not implemented." << std::endl;
throw std::runtime_error("Not supported");
} }
// ==================== Main pass with shadows ==================== // ==================== Main pass with shadows ====================
@ -467,7 +486,7 @@ void Character::drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightF
} }
void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) { void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
AnimationState drawState = resolveActiveState(); /*AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState); auto it = animations.find(drawState);
if (it == animations.end() || !texture) return; if (it == animations.end() || !texture) return;
@ -501,11 +520,13 @@ void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lig
renderer.PopMatrix(); renderer.PopMatrix();
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();*/
std::cout << "CPU skinning shadow depth rendering is not implemented." << std::endl;
throw std::runtime_error("Not supported");
} }
void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) { void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera) {
AnimationState drawState = resolveActiveState(); /*AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState); auto it = animations.find(drawState);
if (it == animations.end() || !texture) return; if (it == animations.end() || !texture) return;
@ -559,7 +580,9 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(__FILE__, __LINE__); CheckGlError(__FILE__, __LINE__);*/
std::cout << "CPU skinning shadow depth rendering is not implemented." << std::endl;
throw std::runtime_error("Not supported");
} }
} // namespace ZL } // namespace ZL

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "BoneAnimatedModel.h" #include "BoneAnimatedModel.h"
#include "BoneAnimatedModelNew.h"
#include "render/Renderer.h" #include "render/Renderer.h"
#include "render/TextureManager.h" #include "render/TextureManager.h"
#include "items/Item.h" #include "items/Item.h"
@ -30,8 +32,8 @@ public:
void loadAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = ""); void loadAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = "");
void loadBinaryAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = ""); void loadBinaryAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = "");
void setTexture(std::shared_ptr<Texture> texture) { void setTexture(const std::string& meshName, std::shared_ptr<Texture> texture) {
this->texture = texture; texturePerMesh[meshName] = texture;
} }
void update(int64_t deltaMs); void update(int64_t deltaMs);
// onArrived is called once when the character reaches this target. // onArrived is called once when the character reaches this target.
@ -78,9 +80,9 @@ public:
private: private:
std::map<AnimationState, BoneAnimationData> animations; std::map<AnimationState, BoneAnimationDataNew> animations;
VertexRenderStruct modelMutable; std::map<std::string, VertexRenderStruct> modelMutableMap;
std::shared_ptr<Texture> texture; std::map<std::string, std::shared_ptr<Texture>> texturePerMesh;
Eigen::Vector3f walkTarget = Eigen::Vector3f(0.f, 0.f, 0.f); Eigen::Vector3f walkTarget = Eigen::Vector3f(0.f, 0.f, 0.f);
Eigen::Vector3f requestedWalkTarget = Eigen::Vector3f(0.f, 0.f, 0.f); Eigen::Vector3f requestedWalkTarget = Eigen::Vector3f(0.f, 0.f, 0.f);

View File

@ -29,199 +29,58 @@ namespace ZL
} }
/*void Location::setup()
{
roomTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/w/room005.png", CONST_ZIP_FILE));
roomMesh.data = LoadFromTextFile02("resources/w/room001.txt", CONST_ZIP_FILE);
roomMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
roomMesh.RefreshVBO();
carTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/e/car002.png", CONST_ZIP_FILE));
carMesh.data = LoadFromTextFile02("resources/e/car001.txt", CONST_ZIP_FILE);
//carMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
carMesh.RefreshVBO();
// Load static game objects
gameObjects = GameObjectLoader::loadAndCreateGameObjects("resources/config2/gameobjects.json", renderer, CONST_ZIP_FILE);
// Load interactive objects
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects("resources/config2/gameobjects.json", renderer, CONST_ZIP_FILE);
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
player = std::make_unique<Character>();
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE);
player->setTexture(playerTexture);
player->walkSpeed = 3.0f;
player->rotationSpeed = 8.0f;
player->modelScale = 1.f;
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
player->canAttack = true;
player->isPlayer = true;
std::cout << "Load resurces step 9" << std::endl;
// Load NPCs from JSON
npcs = GameObjectLoader::loadAndCreateNpcs("resources/config2/npcs.json", CONST_ZIP_FILE);
auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE));
std::cout << "Load resurces step 11" << std::endl;
auto npc02 = std::make_unique<Character>();
npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::WALK, "resources/w/default_float001.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/float_attack003_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/float_attack003.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->setTexture(ghostTexture);
npc02->walkSpeed = 1.5f;
npc02->rotationSpeed = 8.0f;
npc02->modelScale = 0.01f;
//npc02->modelScale = 0.1f;
npc02->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
npc02->position = Eigen::Vector3f(0.f, 0.f, -20.f);
npc02->setTarget(npc02->position);
npc02->canAttack = true;
npc02->attackTarget = player.get();
npcs.push_back(std::move(npc02));
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
std::cout << "Shadow map initialized" << std::endl;
setupNavigation();
scriptEngine.init(this, &inventory);
dialogueSystem.init(renderer, CONST_ZIP_FILE);
dialogueSystem.loadDatabase("resources/dialogue/sample_dialogues.json");
/*dialogueSystem.addTriggerZone({
"ghost_room_trigger",
"test_line_dialogue",
Eigen::Vector3f(0.0f, 0.0f, -8.5f),
2.0f,
true,
false
});
}*/
/*void Location::setup()
{
std::string roomTexPath;
std::string roomMeshPath;
std::string gameObjectsPath;
std::string interactiveObjectsPath;
std::string npcsPath;
if (locationId == "forest")
{
std::cout << "[LOCATION] Setting up FOREST location (using placeholder resources)" << std::endl;
roomTexPath = "resources/w/room005.png";
roomMeshPath = "resources/w/room001.txt";
gameObjectsPath = "resources/config2/gameobjects2.json";
interactiveObjectsPath = "resources/config2/gameobjects2.json";
npcsPath = "resources/config2/npcs2.json";
}
else // default
{
std::cout << "[LOCATION] Setting up DEFAULT location" << std::endl;
roomTexPath = "resources/w/room005.png";
roomMeshPath = "resources/w/room001.txt";
gameObjectsPath = "resources/config2/gameobjects.json";
interactiveObjectsPath = "resources/config2/gameobjects.json";
npcsPath = "resources/config2/npcs.json";
}
roomTexture = std::make_unique<Texture>(CreateTextureDataFromPng(roomTexPath.c_str(), CONST_ZIP_FILE));
roomMesh.data = LoadFromTextFile02(roomMeshPath.c_str(), CONST_ZIP_FILE);
roomMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
roomMesh.RefreshVBO();
carTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/e/car002.png", CONST_ZIP_FILE));
carMesh.data = LoadFromTextFile02("resources/e/car001.txt", CONST_ZIP_FILE);
carMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
carMesh.RefreshVBO();
carWheelTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/e/Wheel_base002.png", CONST_ZIP_FILE));
carWheelMesh.data = LoadFromTextFile02("resources/e/car_wheel001.txt", CONST_ZIP_FILE);
carWheelMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
carWheelMesh.RefreshVBO();
gameObjects = GameObjectLoader::loadAndCreateGameObjects(gameObjectsPath, renderer, CONST_ZIP_FILE);
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(interactiveObjectsPath, renderer, CONST_ZIP_FILE);
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
player = std::make_unique<Character>();
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE);
player->setTexture(playerTexture);
player->walkSpeed = 3.0f;
player->rotationSpeed = 8.0f;
player->modelScale = 1.f;
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
player->canAttack = true;
player->isPlayer = true;
std::cout << "Load resources step 9" << std::endl;
npcs = GameObjectLoader::loadAndCreateNpcs(npcsPath, CONST_ZIP_FILE);
auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE));
std::cout << "Load resources step 11" << std::endl;
auto npc02 = std::make_unique<Character>();
npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::WALK, "resources/w/default_float001.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/float_attack003_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/float_attack003.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->setTexture(ghostTexture);
npc02->walkSpeed = 1.5f;
npc02->rotationSpeed = 8.0f;
npc02->modelScale = 0.01f;
npc02->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
npc02->position = Eigen::Vector3f(0.f, 0.f, -20.f);
npc02->setTarget(npc02->position);
npc02->canAttack = true;
npc02->attackTarget = player.get();
npcs.push_back(std::move(npc02));
shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
std::cout << "Shadow map initialized" << std::endl;
setupNavigation();
scriptEngine.init(this, &inventory);
dialogueSystem.init(renderer, CONST_ZIP_FILE);
dialogueSystem.loadDatabase("resources/dialogue/sample_dialogues.json");
}*/
void Location::setup() void Location::setup()
{ {
auto playerTexture0 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/male_packed0_diffuse.png", CONST_ZIP_FILE));
auto playerTexture1 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/male_packed1_diffuse.png", CONST_ZIP_FILE));
auto playerTexture2 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/male_packed2_diffuse.png", CONST_ZIP_FILE));
player = std::make_unique<Character>();
player->loadAnimation(AnimationState::STAND, "resources/e/gg_stand_idle003.txt", CONST_ZIP_FILE);
player->loadAnimation(AnimationState::WALK, "resources/e/gg_stand_idle003.txt", CONST_ZIP_FILE);
player->setTexture("Body", playerTexture0);
player->setTexture("Bottoms", playerTexture1);
player->setTexture("Eyelashes", playerTexture0);
player->setTexture("Eyes", playerTexture0);
player->setTexture("Eyewear", playerTexture0);
player->setTexture("Gloves", playerTexture1);
player->setTexture("Hair", playerTexture0);
player->setTexture("Shoes", playerTexture1);
player->setTexture("Tops", playerTexture1);
player->walkSpeed = 3.0f;
player->rotationSpeed = 8.0f;
player->modelScale = 0.01f;
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
player->canAttack = true;
player->isPlayer = true;
auto girlfriendTexture0 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/female_packed0_diffuse.png", CONST_ZIP_FILE));
auto girlfriendTexture1 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/female_packed1_diffuse.png", CONST_ZIP_FILE));
auto girlfriendTexture2 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/female_packed2_diffuse.png", CONST_ZIP_FILE));
auto girlfriendTexture3 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/female_packed3_diffuse.png", CONST_ZIP_FILE));
girlfriend = std::make_unique<Character>();
girlfriend->loadAnimation(AnimationState::STAND, "resources/e/woman_stand_idle003.txt", CONST_ZIP_FILE);
girlfriend->loadAnimation(AnimationState::WALK, "resources/e/woman_stand_idle003.txt", CONST_ZIP_FILE);
girlfriend->setTexture("Body", girlfriendTexture0);
girlfriend->setTexture("Bottoms", girlfriendTexture3);
girlfriend->setTexture("Eyelashes", girlfriendTexture0);
girlfriend->setTexture("Eyes", girlfriendTexture0);
girlfriend->setTexture("Hair", girlfriendTexture2);
girlfriend->setTexture("Shoes", girlfriendTexture1);
girlfriend->setTexture("Tops", girlfriendTexture2);
girlfriend->walkSpeed = 3.0f;
girlfriend->rotationSpeed = 8.0f;
girlfriend->modelScale = 0.01f;
girlfriend->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
girlfriend->position = Vector3f{ -10, -0, -10 };
girlfriend->setTarget(girlfriend->position);
//girlfriend->canAttack = true;
//player->isPlayer = true;
if (locationId == "forest") if (locationId == "forest")
{ {
std::cout << "[LOCATION] Setting up FOREST location (custom models only)" << std::endl; std::cout << "[LOCATION] Setting up FOREST location (custom models only)" << std::endl;
@ -263,21 +122,6 @@ void Location::setup()
gameObjects["forest_model_" + std::to_string(i)] = std::move(obj); gameObjects["forest_model_" + std::to_string(i)] = std::move(obj);
} }
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
player = std::make_unique<Character>();
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE);
player->setTexture(playerTexture);
player->walkSpeed = 3.0f;
player->rotationSpeed = 8.0f;
player->modelScale = 1.f;
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
player->canAttack = true;
player->isPlayer = true;
shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f); shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f)); shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
@ -307,23 +151,26 @@ void Location::setup()
gameObjects = GameObjectLoader::loadAndCreateGameObjects(gameObjectsPath, renderer, CONST_ZIP_FILE); gameObjects = GameObjectLoader::loadAndCreateGameObjects(gameObjectsPath, renderer, CONST_ZIP_FILE);
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(interactiveObjectsPath, renderer, CONST_ZIP_FILE); interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(interactiveObjectsPath, renderer, CONST_ZIP_FILE);
npcs = GameObjectLoader::loadAndCreateNpcs(npcsPath, CONST_ZIP_FILE); npcs = GameObjectLoader::loadAndCreateNpcs(npcsPath, CONST_ZIP_FILE);
/*
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE)); auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
player = std::make_unique<Character>(); player = std::make_unique<Character>();
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE); player->loadAnimation(AnimationState::STAND, "resources/e/woman_stand_idle003.txt", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE); player->loadAnimation(AnimationState::WALK, "resources/e/woman_stand_idle003.txt", CONST_ZIP_FILE);
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE); player->setTexture("Body", playerTexture1);
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE); player->setTexture("Bottoms", playerTexture4);
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE); player->setTexture("Eyelashes", playerTexture1);
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE); player->setTexture("Eyes", playerTexture1);
player->setTexture(playerTexture); player->setTexture("Hair", playerTexture3);
player->setTexture("Shoes", playerTexture1);
player->setTexture("Tops", playerTexture3);
player->walkSpeed = 3.0f; player->walkSpeed = 3.0f;
player->rotationSpeed = 8.0f; player->rotationSpeed = 8.0f;
player->modelScale = 1.f; player->modelScale = 0.01f;
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())); player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
player->canAttack = true; player->canAttack = true;
player->isPlayer = true; player->isPlayer = true;
*/
/*
auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE)); auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE));
auto npc02 = std::make_unique<Character>(); auto npc02 = std::make_unique<Character>();
npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE); npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE);
@ -342,7 +189,7 @@ void Location::setup()
npc02->canAttack = true; npc02->canAttack = true;
npc02->attackTarget = player.get(); npc02->attackTarget = player.get();
npcs.push_back(std::move(npc02)); npcs.push_back(std::move(npc02));
*/
shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f); shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f)); shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
@ -362,6 +209,20 @@ void Location::setup()
carWheelMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix()); carWheelMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
carWheelMesh.RefreshVBO(); carWheelMesh.RefreshVBO();
/*
auto girlfriendTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/e/female_packed0_diffuse.png", CONST_ZIP_FILE));
girlfriend = std::make_unique<Character>();
girlfriend->loadAnimation(AnimationState::STAND, "resources/e/woman_stand_idle001.txt", CONST_ZIP_FILE);
girlfriend->loadAnimation(AnimationState::WALK, "resources/e/woman_run001.txt", CONST_ZIP_FILE);
girlfriend->setTexture(girlfriendTexture);
girlfriend->walkSpeed = 3.0f;
girlfriend->rotationSpeed = 8.0f;
girlfriend->modelScale = 0.01f;
girlfriend->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
girlfriend->position = Vector3f{ -10, -0, -10 };
girlfriend->setTarget(girlfriend->position);*/
} }
void Location::setupNavigation() void Location::setupNavigation()
@ -592,6 +453,12 @@ void Location::setup()
renderer.PopMatrix(); renderer.PopMatrix();
if (player && !inCar) player->draw(renderer); if (player && !inCar) player->draw(renderer);
if (girlfriend)
{
girlfriend->draw(renderer);
}
for (auto& npc : npcs) npc->draw(renderer); for (auto& npc : npcs) npc->draw(renderer);
//#ifdef SHOW_PATH //#ifdef SHOW_PATH
@ -665,6 +532,12 @@ void Location::setup()
// Draw characters (they handle their own skinning shader switch internally) // Draw characters (they handle their own skinning shader switch internally)
if (player) player->drawShadowDepth(renderer); if (player) player->drawShadowDepth(renderer);
if (girlfriend)
{
girlfriend->drawShadowDepth(renderer);
}
for (auto& npc : npcs) npc->drawShadowDepth(renderer); for (auto& npc : npcs) npc->drawShadowDepth(renderer);
renderer.PopMatrix(); // light view renderer.PopMatrix(); // light view
@ -783,6 +656,12 @@ void Location::setup()
player->update(delta); player->update(delta);
dialogueSystem.update(static_cast<int>(delta), player->position); dialogueSystem.update(static_cast<int>(delta), player->position);
} }
if (girlfriend)
{
girlfriend->update(delta);
}
for (auto& npc : npcs) npc->update(delta); for (auto& npc : npcs) npc->update(delta);
if (inCar) { if (inCar) {

View File

@ -28,6 +28,8 @@ namespace ZL
std::unique_ptr<Character> player; std::unique_ptr<Character> player;
std::unique_ptr<Character> girlfriend;
std::vector<std::unique_ptr<Character>> npcs; std::vector<std::unique_ptr<Character>> npcs;
float cameraAzimuth = 0.0f; float cameraAzimuth = 0.0f;

View File

@ -382,6 +382,7 @@ namespace ZL {
const std::string& jsonPath, const std::string& jsonPath,
const std::string& zipPath) const std::string& zipPath)
{ {
/*
std::vector<std::unique_ptr<Character>> npcs; std::vector<std::unique_ptr<Character>> npcs;
std::vector<NpcData> npcsData = loadNpcsFromJson(jsonPath, zipPath); std::vector<NpcData> npcsData = loadNpcsFromJson(jsonPath, zipPath);
@ -474,7 +475,8 @@ namespace ZL {
} }
std::cout << "Total NPCs loaded: " << npcs.size() << std::endl; std::cout << "Total NPCs loaded: " << npcs.size() << std::endl;
return npcs; return npcs;*/
return {};
} }
} // namespace ZL } // namespace ZL