633 lines
20 KiB
C++
633 lines
20 KiB
C++
#include "SparkEmitter.h"
|
||
#include <random>
|
||
#include <cmath>
|
||
//#include "renderer/OpenGlExtensions.h"
|
||
#include <fstream>
|
||
#include "external/nlohmann/json.hpp"
|
||
#include <iostream>
|
||
#include "Environment.h"
|
||
#include <stdexcept>
|
||
#include "utils/Utils.h"
|
||
#include "GameConstants.h"
|
||
|
||
namespace ZL {
|
||
|
||
using json = nlohmann::json;
|
||
|
||
SparkEmitter::SparkEmitter()
|
||
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200),
|
||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false),
|
||
useWorldSpace(false) {
|
||
particles.resize(maxParticles);
|
||
drawPositions.reserve(maxParticles * 6);
|
||
drawTexCoords.reserve(maxParticles * 6);
|
||
lastEmissionTime = std::chrono::steady_clock::now();
|
||
|
||
sparkQuad.data = VertexDataStruct();
|
||
}
|
||
|
||
SparkEmitter::SparkEmitter(const SparkEmitter& copyFrom)
|
||
: particles(copyFrom.particles), emissionPoints(copyFrom.emissionPoints),
|
||
lastEmissionTime(copyFrom.lastEmissionTime), emissionRate(copyFrom.emissionRate),
|
||
isActive(copyFrom.isActive), drawPositions(copyFrom.drawPositions),
|
||
drawTexCoords(copyFrom.drawTexCoords), drawDataDirty(copyFrom.drawDataDirty),
|
||
sparkQuad(copyFrom.sparkQuad), texture(copyFrom.texture),
|
||
maxParticles(copyFrom.maxParticles), particleSize(copyFrom.particleSize),
|
||
biasX(copyFrom.biasX), speedRange(copyFrom.speedRange),
|
||
zSpeedRange(copyFrom.zSpeedRange),
|
||
scaleRange(copyFrom.scaleRange),
|
||
lifeTimeRange(copyFrom.lifeTimeRange),
|
||
shaderProgramName(copyFrom.shaderProgramName),
|
||
configured(copyFrom.configured), useWorldSpace(copyFrom.useWorldSpace)
|
||
{
|
||
}
|
||
|
||
|
||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
|
||
: emissionPoints(positions), emissionRate(rate), isActive(true),
|
||
drawDataDirty(true), maxParticles(positions.size() * 100),
|
||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false),
|
||
useWorldSpace(false) {
|
||
particles.resize(maxParticles);
|
||
drawPositions.reserve(maxParticles * 6);
|
||
drawTexCoords.reserve(maxParticles * 6);
|
||
lastEmissionTime = std::chrono::steady_clock::now();
|
||
|
||
sparkQuad.data = VertexDataStruct();
|
||
}
|
||
|
||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions,
|
||
std::shared_ptr<Texture> tex,
|
||
float rate)
|
||
: emissionPoints(positions), texture(tex), emissionRate(rate),
|
||
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100),
|
||
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false),
|
||
useWorldSpace(false) {
|
||
particles.resize(maxParticles);
|
||
drawPositions.reserve(maxParticles * 6);
|
||
drawTexCoords.reserve(maxParticles * 6);
|
||
lastEmissionTime = std::chrono::steady_clock::now();
|
||
|
||
sparkQuad.data = VertexDataStruct();
|
||
}
|
||
|
||
void SparkEmitter::setTexture(std::shared_ptr<Texture> tex) {
|
||
texture = tex;
|
||
}
|
||
|
||
void SparkEmitter::prepareDrawData(bool withRotation) {
|
||
if (!drawDataDirty) return;
|
||
|
||
drawPositions.clear();
|
||
drawTexCoords.clear();
|
||
|
||
if (getActiveParticleCount() == 0) {
|
||
drawDataDirty = false;
|
||
return;
|
||
}
|
||
|
||
std::vector<std::pair<const SparkParticle*, float>> sortedParticles;
|
||
sortedParticles.reserve(getActiveParticleCount());
|
||
|
||
for (const auto& particle : particles) {
|
||
if (particle.active) {
|
||
Vector3f posCam;
|
||
if (withRotation) {
|
||
Vector3f rel = particle.position - Environment::shipState.position;
|
||
posCam = Environment::inverseShipMatrix * rel;
|
||
}
|
||
else {
|
||
posCam = particle.position;
|
||
}
|
||
sortedParticles.push_back({ &particle, posCam(2) });
|
||
}
|
||
}
|
||
|
||
std::sort(sortedParticles.begin(), sortedParticles.end(),
|
||
[](const auto& a, const auto& b) {
|
||
return a.second < b.second;
|
||
});
|
||
|
||
for (const auto& [particlePtr, depth] : sortedParticles) {
|
||
const auto& particle = *particlePtr;
|
||
Vector3f posCam;
|
||
if (useWorldSpace) {
|
||
Vector3f rel = particle.position - Environment::shipState.position;
|
||
posCam = Environment::inverseShipMatrix * rel;
|
||
}
|
||
else {
|
||
posCam = particle.position;
|
||
}
|
||
|
||
float size = particleSize * particle.scale;
|
||
|
||
if (withRotation)
|
||
{
|
||
|
||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, -size, 0 } + posCam);
|
||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||
|
||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, size,0 } + posCam);
|
||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||
|
||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size,size, 0 } + posCam);
|
||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||
|
||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, -size, 0 } + posCam);
|
||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||
|
||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size, size,0 } + posCam);
|
||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||
|
||
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size, -size, 0 } + posCam);
|
||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||
}
|
||
else
|
||
{
|
||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||
|
||
drawPositions.push_back({ posCam(0) - size, posCam(1) + size, posCam(2) });
|
||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||
|
||
drawPositions.push_back({ posCam(0) + size, posCam(1) + size, posCam(2) });
|
||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||
|
||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||
|
||
drawPositions.push_back({ posCam(0) + size, posCam(1) + size, posCam(2) });
|
||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||
|
||
drawPositions.push_back({ posCam(0) + size, posCam(1) - size, posCam(2) });
|
||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||
|
||
}
|
||
}
|
||
|
||
drawDataDirty = false;
|
||
}
|
||
|
||
void SparkEmitter::prepareForDraw(bool withRotation) {
|
||
if (!configured) return;
|
||
|
||
prepareDrawData(withRotation);
|
||
|
||
if (!drawPositions.empty()) {
|
||
sparkQuad.data.PositionData = drawPositions;
|
||
sparkQuad.data.TexCoordData = drawTexCoords;
|
||
sparkQuad.RefreshVBO();
|
||
}
|
||
}
|
||
|
||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight)
|
||
{
|
||
draw(renderer, zoom, screenWidth, screenHeight, true);
|
||
}
|
||
|
||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight, bool withRotation) {
|
||
if (!configured) {
|
||
throw std::runtime_error("Failed to load spark emitter config file 1!");
|
||
}
|
||
|
||
if (getActiveParticleCount() == 0) {
|
||
return;
|
||
}
|
||
|
||
if (!texture) {
|
||
throw std::runtime_error("Failed to load spark emitter config file 2!");
|
||
}
|
||
|
||
//prepareDrawData(withRotation);
|
||
|
||
if (drawPositions.empty()) {
|
||
return;
|
||
}
|
||
/*
|
||
sparkQuad.data.PositionData = drawPositions;
|
||
sparkQuad.data.TexCoordData = drawTexCoords;
|
||
sparkQuad.RefreshVBO();
|
||
*/
|
||
renderer.shaderManager.PushShader(shaderProgramName);
|
||
renderer.RenderUniform1i(textureUniformName, 0);
|
||
renderer.SetMatrix();
|
||
|
||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||
|
||
glEnable(GL_BLEND);
|
||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||
|
||
//renderer.PushMatrix();
|
||
renderer.DrawVertexRenderStruct(sparkQuad);
|
||
//renderer.PopMatrix();
|
||
|
||
glDisable(GL_BLEND);
|
||
renderer.shaderManager.PopShader();
|
||
}
|
||
|
||
void SparkEmitter::update(float deltaTimeMs) {
|
||
if (!configured) {
|
||
throw std::runtime_error("Failed to load spark emitter config file 3!");
|
||
}
|
||
|
||
auto currentTime = std::chrono::steady_clock::now();
|
||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||
currentTime - lastEmissionTime).count();
|
||
|
||
if (isActive && elapsed >= static_cast<long long>(emissionRate)) {
|
||
int emitCount = static_cast<int>(elapsed / emissionRate);
|
||
float elapsedF = static_cast<float>(elapsed);
|
||
for (int e = 0; e < emitCount; ++e) {
|
||
// e=0 — самая старая эмиссия, e=emitCount-1 — самая свежая
|
||
float ageMs = static_cast<float>(emitCount - 1 - e) * emissionRate;
|
||
// lerpT=0 → текущая позиция (новейшая), lerpT=1 → предыдущая (самая старая)
|
||
float lerpT = (elapsedF > 0.0f) ? min(ageMs / elapsedF, 1.0f) : 0.0f;
|
||
emit(ageMs, lerpT);
|
||
}
|
||
lastEmissionTime += std::chrono::milliseconds(
|
||
static_cast<long long>(emitCount * emissionRate));
|
||
|
||
drawDataDirty = true;
|
||
}
|
||
|
||
bool anyChanged = false;
|
||
for (auto& particle : particles) {
|
||
if (particle.active) {
|
||
Vector3f oldPosition = particle.position;
|
||
float oldScale = particle.scale;
|
||
|
||
particle.position(0) += particle.velocity(0) * deltaTimeMs / 1000.0f;
|
||
particle.position(1) += particle.velocity(1) * deltaTimeMs / 1000.0f;
|
||
particle.position(2) += particle.velocity(2) * deltaTimeMs / 1000.0f;
|
||
|
||
particle.lifeTime += deltaTimeMs;
|
||
|
||
if (particle.lifeTime >= particle.maxLifeTime) {
|
||
particle.active = false;
|
||
anyChanged = true;
|
||
}
|
||
else {
|
||
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
|
||
particle.scale = 1.0f - lifeRatio * 0.8f;
|
||
|
||
if (oldPosition(0) != particle.position(0) ||
|
||
oldPosition(1) != particle.position(1) ||
|
||
oldPosition(2) != particle.position(2) ||
|
||
oldScale != particle.scale) {
|
||
anyChanged = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (anyChanged) {
|
||
drawDataDirty = true;
|
||
}
|
||
}
|
||
|
||
void SparkEmitter::emit(float ageMs, float lerpT) {
|
||
if (!configured) {
|
||
throw std::runtime_error("Failed to load spark emitter config file 4!");
|
||
}
|
||
|
||
if (emissionPoints.empty()) return;
|
||
bool emitted = false;
|
||
|
||
auto applyAge = [](SparkParticle& particle, float age) {
|
||
if (age <= 0.0f) return;
|
||
particle.position(0) += particle.velocity(0) * age / 1000.0f;
|
||
particle.position(1) += particle.velocity(1) * age / 1000.0f;
|
||
particle.position(2) += particle.velocity(2) * age / 1000.0f;
|
||
particle.lifeTime = age;
|
||
if (particle.lifeTime >= particle.maxLifeTime) {
|
||
particle.active = false;
|
||
} else {
|
||
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
|
||
particle.scale = 1.0f - lifeRatio * 0.8f;
|
||
}
|
||
};
|
||
|
||
// Вычисляем стартовую позицию с интерполяцией между prev и current
|
||
// lerpT=0 → текущая позиция (новейшая эмиссия), lerpT=1 → предыдущая (самая старая)
|
||
bool canInterp = (prevEmissionPoints.size() == emissionPoints.size());
|
||
|
||
for (int i = 0; i < (int)emissionPoints.size(); ++i) {
|
||
Vector3f birthPos = emissionPoints[i];
|
||
if (canInterp && lerpT > 0.0f) {
|
||
birthPos = emissionPoints[i] * (1.0f - lerpT) + prevEmissionPoints[i] * lerpT;
|
||
}
|
||
|
||
bool particleFound = false;
|
||
|
||
for (auto& particle : particles) {
|
||
if (!particle.active) {
|
||
initParticle(particle, i);
|
||
particle.active = true;
|
||
particle.lifeTime = 0;
|
||
particle.position = birthPos;
|
||
particle.emitterIndex = i;
|
||
applyAge(particle, ageMs);
|
||
particleFound = true;
|
||
emitted = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!particleFound && !particles.empty()) {
|
||
size_t oldestIndex = 0;
|
||
float maxLifeRatio = 0;
|
||
|
||
for (size_t j = 0; j < particles.size(); ++j) {
|
||
if (particles[j].lifeTime > maxLifeRatio) {
|
||
maxLifeRatio = particles[j].lifeTime;
|
||
oldestIndex = j;
|
||
}
|
||
}
|
||
|
||
initParticle(particles[oldestIndex], i);
|
||
particles[oldestIndex].active = true;
|
||
particles[oldestIndex].lifeTime = 0;
|
||
particles[oldestIndex].position = birthPos;
|
||
particles[oldestIndex].emitterIndex = i;
|
||
applyAge(particles[oldestIndex], ageMs);
|
||
emitted = true;
|
||
}
|
||
}
|
||
|
||
if (emitted) {
|
||
drawDataDirty = true;
|
||
}
|
||
}
|
||
|
||
void SparkEmitter::setEmissionPoints(const std::vector<Vector3f>& positions) {
|
||
prevEmissionPoints = emissionPoints;
|
||
emissionPoints = positions;
|
||
drawDataDirty = true;
|
||
}
|
||
|
||
void SparkEmitter::resetEmissionPoints(const std::vector<Vector3f>& positions)
|
||
{
|
||
prevEmissionPoints.clear();
|
||
emissionPoints = positions;
|
||
drawDataDirty = true;
|
||
}
|
||
|
||
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
|
||
if (!configured) {
|
||
throw std::runtime_error("Failed to load spark emitter config file 5!");
|
||
}
|
||
|
||
particle.velocity = getRandomVelocity(emitterIndex);
|
||
static std::random_device rd;
|
||
static std::mt19937 gen(rd());
|
||
std::uniform_real_distribution<float> scaleDist(scaleRange.min, scaleRange.max);
|
||
particle.scale = scaleDist(gen);
|
||
|
||
std::uniform_real_distribution<float> lifeDist(lifeTimeRange.min, lifeTimeRange.max);
|
||
particle.maxLifeTime = lifeDist(gen);
|
||
particle.emitterIndex = emitterIndex;
|
||
}
|
||
|
||
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
|
||
if (!configured) {
|
||
throw std::runtime_error("Failed to load spark emitter config file 6!");
|
||
}
|
||
|
||
static std::random_device rd;
|
||
static std::mt19937 gen(rd());
|
||
std::uniform_real_distribution<float> angleDist(0.0f, 2.0f * static_cast<float>(M_PI));
|
||
std::uniform_real_distribution<float> speedDist(speedRange.min, speedRange.max);
|
||
std::uniform_real_distribution<float> zSpeedDist(zSpeedRange.min, zSpeedRange.max);
|
||
|
||
float angle = angleDist(gen);
|
||
float speed = speedDist(gen);
|
||
float zSpeed = zSpeedDist(gen);
|
||
|
||
// Теперь biasX берется из JSON
|
||
if (emitterIndex == 0) {
|
||
return Vector3f{
|
||
cosf(angle) * speed - biasX,
|
||
sinf(angle) * speed,
|
||
zSpeed
|
||
};
|
||
}
|
||
else {
|
||
return Vector3f{
|
||
cosf(angle) * speed + biasX,
|
||
sinf(angle) * speed,
|
||
zSpeed
|
||
};
|
||
}
|
||
}
|
||
|
||
const std::vector<SparkParticle>& SparkEmitter::getParticles() const {
|
||
return particles;
|
||
}
|
||
|
||
size_t SparkEmitter::getActiveParticleCount() const {
|
||
size_t count = 0;
|
||
for (const auto& particle : particles) {
|
||
if (particle.active) {
|
||
count++;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
|
||
bool SparkEmitter::loadFromJsonFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||
std::cout << "Loading spark config from: " << path << std::endl;
|
||
|
||
std::string content;
|
||
try {
|
||
if (zipFile.empty()) {
|
||
content = readTextFile(path);
|
||
}
|
||
else {
|
||
auto buf = readFileFromZIP(path, zipFile);
|
||
if (buf.empty()) {
|
||
std::cerr << "Failed to read JSON from zip: " << path << " in " << zipFile << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 7!");
|
||
}
|
||
content.assign(buf.begin(), buf.end());
|
||
}
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cerr << "Failed to open JSON file: " << path << " : " << e.what() << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 8!");
|
||
}
|
||
|
||
json j;
|
||
try {
|
||
j = json::parse(content);
|
||
std::cout << "JSON parsed successfully" << std::endl;
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 9!");
|
||
}
|
||
std::cout << "JSON content: " << j.dump(2) << std::endl;
|
||
|
||
auto requireKey = [&](const std::string& key) {
|
||
if (!j.contains(key)) {
|
||
std::cerr << "Missing required key in spark JSON: " << key << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 9!");
|
||
}
|
||
};
|
||
|
||
requireKey("emissionPoints");
|
||
requireKey("texture");
|
||
requireKey("speedRange");
|
||
requireKey("zSpeedRange");
|
||
requireKey("scaleRange");
|
||
requireKey("lifeTimeRange");
|
||
requireKey("emissionRate");
|
||
requireKey("maxParticles");
|
||
requireKey("particleSize");
|
||
requireKey("biasX");
|
||
requireKey("shaderProgramName");
|
||
|
||
emissionRate = j["emissionRate"].get<float>();
|
||
|
||
maxParticles = j["maxParticles"].get<int>();
|
||
particles.resize(maxParticles);
|
||
drawPositions.reserve(maxParticles * 6);
|
||
drawTexCoords.reserve(maxParticles * 6);
|
||
std::cout << "Max particles: " << maxParticles << std::endl;
|
||
|
||
particleSize = j["particleSize"].get<float>();
|
||
std::cout << "Particle size: " << particleSize << std::endl;
|
||
|
||
biasX = j["biasX"].get<float>();
|
||
std::cout << "Bias X: " << biasX << std::endl;
|
||
|
||
// emissionPoints
|
||
std::vector<Vector3f> points;
|
||
if (j.contains("emissionPoints") && j["emissionPoints"].is_array()) {
|
||
for (const auto& el : j["emissionPoints"]) {
|
||
if (el.contains("position") && el["position"].is_array()) {
|
||
auto arr = el["position"];
|
||
points.push_back(Vector3f{ arr[0].get<float>(), arr[1].get<float>(), arr[2].get<float>() });
|
||
std::cout << "Fixed point: [" << arr[0] << ", " << arr[1] << ", " << arr[2] << "]" << std::endl;
|
||
}
|
||
else if (el.contains("positionRange") && el["positionRange"].is_object()) {
|
||
auto pr = el["positionRange"];
|
||
auto minArr = pr["min"];
|
||
auto maxArr = pr["max"];
|
||
int count = 1;
|
||
if (el.contains("count")) count = el["count"].get<int>();
|
||
|
||
std::random_device rd;
|
||
std::mt19937 gen(rd());
|
||
std::uniform_real_distribution<float> dx(minArr[0].get<float>(), maxArr[0].get<float>());
|
||
std::uniform_real_distribution<float> dy(minArr[1].get<float>(), maxArr[1].get<float>());
|
||
std::uniform_real_distribution<float> dz(minArr[2].get<float>(), maxArr[2].get<float>());
|
||
|
||
for (int k = 0; k < count; ++k) {
|
||
Vector3f randomPoint{ dx(gen), dy(gen), dz(gen) };
|
||
points.push_back(randomPoint);
|
||
std::cout << "Random point " << k + 1 << ": [" << randomPoint(0)
|
||
<< ", " << randomPoint(1) << ", " << randomPoint(2) << "]" << std::endl;
|
||
}
|
||
}
|
||
}
|
||
if (!points.empty()) {
|
||
setEmissionPoints(points);
|
||
std::cout << "Total emission points: " << emissionPoints.size() << std::endl;
|
||
}
|
||
else {
|
||
setEmissionPoints({});
|
||
std::cout << "Emission points parsed but empty" << std::endl;
|
||
//throw std::runtime_error("Failed to load spark emitter config file 10!");
|
||
}
|
||
}
|
||
else {
|
||
std::cerr << "emissionPoints is not array" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 11!");
|
||
}
|
||
|
||
// Ranges
|
||
if (j.contains("speedRange") && j["speedRange"].is_array()) {
|
||
auto a = j["speedRange"];
|
||
speedRange.min = a[0].get<float>();
|
||
speedRange.max = a[1].get<float>();
|
||
std::cout << "Speed range: [" << speedRange.min << ", " << speedRange.max << "]" << std::endl;
|
||
}
|
||
else {
|
||
std::cerr << "speedRange missing or invalid" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 12!");
|
||
}
|
||
|
||
if (j.contains("zSpeedRange") && j["zSpeedRange"].is_array()) {
|
||
auto a = j["zSpeedRange"];
|
||
zSpeedRange.min = a[0].get<float>();
|
||
zSpeedRange.max = a[1].get<float>();
|
||
std::cout << "Z speed range: [" << zSpeedRange.min << ", " << zSpeedRange.max << "]" << std::endl;
|
||
}
|
||
else {
|
||
std::cerr << "zSpeedRange missing or invalid" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 13!");
|
||
}
|
||
|
||
if (j.contains("scaleRange") && j["scaleRange"].is_array()) {
|
||
auto a = j["scaleRange"];
|
||
scaleRange.min = a[0].get<float>();
|
||
scaleRange.max = a[1].get<float>();
|
||
std::cout << "Scale range: [" << scaleRange.min << ", " << scaleRange.max << "]" << std::endl;
|
||
}
|
||
else {
|
||
std::cerr << "scaleRange missing or invalid" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 14!");
|
||
}
|
||
|
||
if (j.contains("lifeTimeRange") && j["lifeTimeRange"].is_array()) {
|
||
auto a = j["lifeTimeRange"];
|
||
lifeTimeRange.min = a[0].get<float>();
|
||
lifeTimeRange.max = a[1].get<float>();
|
||
std::cout << "Life time range: [" << lifeTimeRange.min << ", " << lifeTimeRange.max << "]" << std::endl;
|
||
}
|
||
else {
|
||
std::cerr << "lifeTimeRange missing or invalid" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 15!");
|
||
}
|
||
|
||
// texture
|
||
if (j.contains("texture") && j["texture"].is_string()) {
|
||
std::string texPath = j["texture"].get<std::string>();
|
||
std::cout << "Loading texture: " << texPath << " From zip file: " << zipFile << std::endl;
|
||
|
||
try {
|
||
std::cout << "Loading texture step 1" << std::endl;
|
||
auto texData = CreateTextureDataFromPng(texPath.c_str(), zipFile.c_str());
|
||
std::cout << "Loading texture step 2" << std::endl;
|
||
texture = std::make_shared<Texture>(texData);
|
||
std::cout << "Texture loaded successfully, ID: " << texture->getTexID() << std::endl;
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cout << "Texture load error: " << e.what() << std::endl;
|
||
std::cerr << "Texture load error: " << e.what() << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 16!");
|
||
}
|
||
}
|
||
else {
|
||
std::cerr << "No texture specified or invalid type in JSON" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 17!");
|
||
}
|
||
|
||
std::cout << "Working with shaders 1" << std::endl;
|
||
// shaders
|
||
if (j.contains("shaderProgramName") && j["shaderProgramName"].is_string()) {
|
||
shaderProgramName = j["shaderProgramName"].get<std::string>();
|
||
std::cout << "Shader program name: " << shaderProgramName << std::endl;
|
||
}
|
||
else {
|
||
std::cerr << "shaderProgramName missing or invalid" << std::endl;
|
||
throw std::runtime_error("Failed to load spark emitter config file 18!");
|
||
}
|
||
|
||
drawDataDirty = true;
|
||
configured = true;
|
||
std::cout << "SparkEmitter configuration loaded successfully!" << std::endl;
|
||
return true;
|
||
}
|
||
|
||
} // namespace ZL
|