New type of anim

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

No files matched your search

+684
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();
}
}
}
+66
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;
};
};
+44 -21
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) {
auto& data = animations[state];
throw std::runtime_error("Not supported");
/*auto& data = animations[state];
data.model.LoadFromBinaryFile(filename, zipFile);
if (!data.model.animations.empty() && !data.model.animations[0].keyFrames.empty()) {
data.totalFrames = data.model.animations[0].keyFrames.back().frame + 1;
}
else {
data.totalFrames = 1;
}
}*/
}
void Character::setTarget(const Eigen::Vector3f& target,
@@ -272,7 +275,7 @@ void Character::draw(Renderer& renderer) {
AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
if (it == animations.end() || !texture) return;
if (it == animations.end() || texturePerMesh.empty()) return;
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
@@ -288,10 +291,13 @@ void Character::draw(Renderer& renderer) {
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
auto& anim = it->second;
modelMutable.AssignFrom(anim.model.mesh);
modelMutable.RefreshVBO();
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
renderer.DrawVertexRenderStruct(modelMutable);
for (const auto& [meshName, texture] : texturePerMesh) {
modelMutableMap[meshName].AssignFrom(anim.model.meshes[meshName].mesh);
modelMutableMap[meshName].RefreshVBO();
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
renderer.DrawVertexRenderStruct(modelMutableMap[meshName]);
}
renderer.PopMatrix();
renderer.PopProjectionMatrix();
@@ -301,7 +307,7 @@ void Character::draw(Renderer& renderer) {
void Character::drawGpuSkinning(Renderer& renderer) {
AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
if (it == animations.end() || !texture)
if (it == animations.end() || texturePerMesh.empty())
{
return;
}
@@ -338,19 +344,26 @@ void Character::drawGpuSkinning(Renderer& renderer) {
static_cast<int>(anim.gpuSkinningShaderData.skinningMatrices.size()), false,
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 __ANDROID__
if (anim.gpuSkinningShaderData.bindPoseMutable.vao) {
glBindVertexArray(anim.gpuSkinningShaderData.bindPoseMutable.vao->getBuffer());
renderer.shaderManager.EnableVertexAttribArrays();
}
if (meshIt->second.bindPoseMutable.vao) {
glBindVertexArray(meshIt->second.bindPoseMutable.vao->getBuffer());
renderer.shaderManager.EnableVertexAttribArrays();
}
#endif
#endif
anim.gpuSkinningShaderData.RenderVBO(renderer);
meshIt->second.RenderVBO(renderer);
}
renderer.PopMatrix();
renderer.PopProjectionMatrix();
@@ -396,6 +409,7 @@ void Character::drawShadowDepth(Renderer& renderer) {
}
void Character::drawShadowDepthCpu(Renderer& renderer) {
/*
AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
if (it == animations.end()) return;
@@ -413,11 +427,14 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
modelMutable.RefreshVBO();
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) {
/*
CheckGlError(__FILE__, __LINE__);
AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
@@ -453,7 +470,9 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
renderer.PopMatrix();
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 ====================
@@ -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) {
AnimationState drawState = resolveActiveState();
/*AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
if (it == animations.end() || !texture) return;
@@ -501,11 +520,13 @@ void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lig
renderer.PopMatrix();
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) {
AnimationState drawState = resolveActiveState();
/*AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
if (it == animations.end() || !texture) return;
@@ -559,7 +580,9 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
renderer.PopProjectionMatrix();
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
+7 -5
View File
@@ -1,5 +1,7 @@
#pragma once
#include "BoneAnimatedModel.h"
#include "BoneAnimatedModelNew.h"
#include "render/Renderer.h"
#include "render/TextureManager.h"
#include "items/Item.h"
@@ -30,8 +32,8 @@ public:
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 setTexture(std::shared_ptr<Texture> texture) {
this->texture = texture;
void setTexture(const std::string& meshName, std::shared_ptr<Texture> texture) {
texturePerMesh[meshName] = texture;
}
void update(int64_t deltaMs);
// onArrived is called once when the character reaches this target.
@@ -78,9 +80,9 @@ public:
private:
std::map<AnimationState, BoneAnimationData> animations;
VertexRenderStruct modelMutable;
std::shared_ptr<Texture> texture;
std::map<AnimationState, BoneAnimationDataNew> animations;
std::map<std::string, VertexRenderStruct> modelMutableMap;
std::map<std::string, std::shared_ptr<Texture>> texturePerMesh;
Eigen::Vector3f walkTarget = Eigen::Vector3f(0.f, 0.f, 0.f);
Eigen::Vector3f requestedWalkTarget = Eigen::Vector3f(0.f, 0.f, 0.f);
+96 -217
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()
{
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")
{
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);
}
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->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
@@ -307,23 +151,26 @@ void Location::setup()
gameObjects = GameObjectLoader::loadAndCreateGameObjects(gameObjectsPath, renderer, CONST_ZIP_FILE);
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(interactiveObjectsPath, renderer, 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));
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->loadAnimation(AnimationState::STAND, "resources/e/woman_stand_idle003.txt", CONST_ZIP_FILE);
player->loadAnimation(AnimationState::WALK, "resources/e/woman_stand_idle003.txt", CONST_ZIP_FILE);
player->setTexture("Body", playerTexture1);
player->setTexture("Bottoms", playerTexture4);
player->setTexture("Eyelashes", playerTexture1);
player->setTexture("Eyes", playerTexture1);
player->setTexture("Hair", playerTexture3);
player->setTexture("Shoes", playerTexture1);
player->setTexture("Tops", playerTexture3);
player->walkSpeed = 3.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->canAttack = true;
player->isPlayer = true;
*/
/*
auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE));
auto npc02 = std::make_unique<Character>();
npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE);
@@ -342,7 +189,7 @@ void Location::setup()
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));
@@ -362,6 +209,20 @@ void Location::setup()
carWheelMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
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()
@@ -592,6 +453,12 @@ void Location::setup()
renderer.PopMatrix();
if (player && !inCar) player->draw(renderer);
if (girlfriend)
{
girlfriend->draw(renderer);
}
for (auto& npc : npcs) npc->draw(renderer);
//#ifdef SHOW_PATH
@@ -665,6 +532,12 @@ void Location::setup()
// Draw characters (they handle their own skinning shader switch internally)
if (player) player->drawShadowDepth(renderer);
if (girlfriend)
{
girlfriend->drawShadowDepth(renderer);
}
for (auto& npc : npcs) npc->drawShadowDepth(renderer);
renderer.PopMatrix(); // light view
@@ -783,6 +656,12 @@ void Location::setup()
player->update(delta);
dialogueSystem.update(static_cast<int>(delta), player->position);
}
if (girlfriend)
{
girlfriend->update(delta);
}
for (auto& npc : npcs) npc->update(delta);
if (inCar) {
+2
View File
@@ -28,6 +28,8 @@ namespace ZL
std::unique_ptr<Character> player;
std::unique_ptr<Character> girlfriend;
std::vector<std::unique_ptr<Character>> npcs;
float cameraAzimuth = 0.0f;
+3 -1
View File
@@ -382,6 +382,7 @@ namespace ZL {
const std::string& jsonPath,
const std::string& zipPath)
{
/*
std::vector<std::unique_ptr<Character>> npcs;
std::vector<NpcData> npcsData = loadNpcsFromJson(jsonPath, zipPath);
@@ -474,7 +475,8 @@ namespace ZL {
}
std::cout << "Total NPCs loaded: " << npcs.size() << std::endl;
return npcs;
return npcs;*/
return {};
}
} // namespace ZL