merge
This commit is contained in:
commit
4c837eff0a
@ -436,6 +436,8 @@ add_executable(space-game001
|
||||
OpenGlExtensions.h
|
||||
Utils.cpp
|
||||
Utils.h
|
||||
SparkEmitter.cpp
|
||||
SparkEmitter.h
|
||||
)
|
||||
|
||||
# Установка проекта по умолчанию для Visual Studio
|
||||
|
||||
@ -8,7 +8,7 @@ namespace ZL {
|
||||
int Environment::windowHeaderHeight = 0;
|
||||
int Environment::width = 0;
|
||||
int Environment::height = 0;
|
||||
float Environment::zoom = 6.f;
|
||||
float Environment::zoom = 14.f;
|
||||
|
||||
bool Environment::leftPressed = false;
|
||||
bool Environment::rightPressed = false;
|
||||
|
||||
85
Game.h
85
Game.h
@ -1,58 +1,48 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "OpenGlExtensions.h"
|
||||
#include "Renderer.h"
|
||||
#include "Environment.h"
|
||||
#include "TextureManager.h"
|
||||
#include "SparkEmitter.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
|
||||
struct BoxCoords
|
||||
{
|
||||
Vector3f pos;
|
||||
Matrix3f m;
|
||||
};
|
||||
struct BoxCoords
|
||||
{
|
||||
Vector3f pos;
|
||||
Matrix3f m;
|
||||
};
|
||||
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
class Game {
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
|
||||
void setup();
|
||||
void update();
|
||||
void render();
|
||||
void setup();
|
||||
void update();
|
||||
void render();
|
||||
|
||||
bool shouldExit() const { return Environment::exitGameLoop; }
|
||||
|
||||
private:
|
||||
void processTickCount();
|
||||
void drawScene();
|
||||
void drawCubemap();
|
||||
void drawShip();
|
||||
void drawBoxes();
|
||||
void drawUI();
|
||||
bool shouldExit() const { return Environment::exitGameLoop; }
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
Renderer renderer;
|
||||
private:
|
||||
void processTickCount();
|
||||
void drawScene();
|
||||
void drawCubemap();
|
||||
void drawShip();
|
||||
void drawBoxes();
|
||||
void drawUI();
|
||||
|
||||
size_t newTickCount;
|
||||
size_t lastTickCount;
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
Renderer renderer;
|
||||
|
||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||
size_t newTickCount;
|
||||
size_t lastTickCount;
|
||||
|
||||
std::shared_ptr<Texture> spaceshipTexture;
|
||||
std::shared_ptr<Texture> cubemapTexture;
|
||||
VertexDataStruct spaceshipBase;
|
||||
VertexRenderStruct spaceship;
|
||||
|
||||
VertexRenderStruct cubemap;
|
||||
|
||||
std::shared_ptr<Texture> boxTexture;
|
||||
VertexDataStruct boxBase;
|
||||
|
||||
std::vector<BoxCoords> boxCoordsArr;
|
||||
std::vector<VertexRenderStruct> boxRenderArr;
|
||||
@ -69,7 +59,7 @@ private:
|
||||
|
||||
|
||||
bool isDraggingVolume = false;
|
||||
float musicVolume = 1.0f;
|
||||
float musicVolume = 0.0f;
|
||||
float volumeBarMinX = 1190.0f;
|
||||
float volumeBarMaxX = 1200.0f;
|
||||
float volumeBarMinY = 100.0f;
|
||||
@ -78,6 +68,23 @@ private:
|
||||
float musicVolumeBarButtonButtonRadius = 25.0f;
|
||||
void UpdateVolumeFromMouse(int mouseX, int mouseY);
|
||||
void UpdateVolumeKnob();
|
||||
};
|
||||
|
||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||
|
||||
std::shared_ptr<Texture> sparkTexture;
|
||||
std::shared_ptr<Texture> spaceshipTexture;
|
||||
std::shared_ptr<Texture> cubemapTexture;
|
||||
VertexDataStruct spaceshipBase;
|
||||
VertexRenderStruct spaceship;
|
||||
|
||||
VertexRenderStruct cubemap;
|
||||
|
||||
std::shared_ptr<Texture> boxTexture;
|
||||
VertexDataStruct boxBase;
|
||||
|
||||
SparkEmitter sparkEmitter;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
download from https://cmake.org/download/
|
||||
|
||||
|
||||
Windows x64 Installer: cmake-4.2.0-windows-x86_64.msi
|
||||
|
||||
|
||||
|
||||
297
SparkEmitter.cpp
Normal file
297
SparkEmitter.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
#include "SparkEmitter.h"
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include "OpenGlExtensions.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
SparkEmitter::SparkEmitter()
|
||||
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
lastEmissionTime = std::chrono::steady_clock::now();
|
||||
|
||||
sparkQuad.data = VertexDataStruct();
|
||||
}
|
||||
|
||||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
|
||||
: emissionPoints(positions), emissionRate(rate), isActive(true),
|
||||
drawDataDirty(true), maxParticles(positions.size() * 100) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
lastEmissionTime = std::chrono::steady_clock::now();
|
||||
|
||||
sparkQuad.data = VertexDataStruct();
|
||||
}
|
||||
|
||||
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions,
|
||||
std::shared_ptr<Texture> tex,
|
||||
float rate)
|
||||
: emissionPoints(positions), texture(tex), emissionRate(rate),
|
||||
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100) {
|
||||
particles.resize(maxParticles);
|
||||
drawPositions.reserve(maxParticles * 6);
|
||||
drawTexCoords.reserve(maxParticles * 6);
|
||||
lastEmissionTime = std::chrono::steady_clock::now();
|
||||
|
||||
sparkQuad.data = VertexDataStruct();
|
||||
}
|
||||
|
||||
void SparkEmitter::setTexture(std::shared_ptr<Texture> tex) {
|
||||
texture = tex;
|
||||
}
|
||||
|
||||
void SparkEmitter::prepareDrawData() {
|
||||
if (!drawDataDirty) return;
|
||||
|
||||
drawPositions.clear();
|
||||
drawTexCoords.clear();
|
||||
|
||||
if (getActiveParticleCount() == 0) {
|
||||
drawDataDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<const SparkParticle*, float>> sortedParticles;
|
||||
sortedParticles.reserve(getActiveParticleCount());
|
||||
|
||||
for (const auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
sortedParticles.push_back({ &particle, particle.position.v[2] });
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(sortedParticles.begin(), sortedParticles.end(),
|
||||
[](const auto& a, const auto& b) {
|
||||
return a.second > b.second;
|
||||
});
|
||||
|
||||
for (const auto& [particlePtr, depth] : sortedParticles) {
|
||||
const auto& particle = *particlePtr;
|
||||
Vector3f pos = particle.position;
|
||||
float size = 0.04f * particle.scale;
|
||||
|
||||
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] - size, pos.v[1] + size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||
|
||||
drawPositions.push_back({ pos.v[0] + size, pos.v[1] - size, pos.v[2] });
|
||||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||
}
|
||||
|
||||
drawDataDirty = false;
|
||||
}
|
||||
|
||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
|
||||
if (getActiveParticleCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
prepareDrawData();
|
||||
|
||||
if (drawPositions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sparkQuad.data.PositionData = drawPositions;
|
||||
sparkQuad.data.TexCoordData = drawTexCoords;
|
||||
sparkQuad.RefreshVBO();
|
||||
|
||||
static const std::string defaultShaderName = "default";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float aspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, 1, 1000);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0, 0, -1.0f * zoom });
|
||||
|
||||
renderer.DrawVertexRenderStruct(sparkQuad);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
void SparkEmitter::update(float deltaTimeMs) {
|
||||
auto currentTime = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
currentTime - lastEmissionTime).count();
|
||||
|
||||
if (isActive && elapsed >= emissionRate) {
|
||||
emit();
|
||||
lastEmissionTime = currentTime;
|
||||
drawDataDirty = true;
|
||||
}
|
||||
|
||||
bool anyChanged = false;
|
||||
for (auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
Vector3f oldPosition = particle.position;
|
||||
float oldScale = particle.scale;
|
||||
|
||||
particle.position.v[0] += particle.velocity.v[0] * deltaTimeMs / 1000.0f;
|
||||
particle.position.v[1] += particle.velocity.v[1] * deltaTimeMs / 1000.0f;
|
||||
particle.position.v[2] += particle.velocity.v[2] * deltaTimeMs / 1000.0f;
|
||||
|
||||
particle.lifeTime += deltaTimeMs;
|
||||
|
||||
if (particle.lifeTime >= particle.maxLifeTime) {
|
||||
particle.active = false;
|
||||
anyChanged = true;
|
||||
}
|
||||
else {
|
||||
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
|
||||
particle.scale = 1.0f - lifeRatio * 0.8f;
|
||||
|
||||
if (oldPosition.v[0] != particle.position.v[0] ||
|
||||
oldPosition.v[1] != particle.position.v[1] ||
|
||||
oldPosition.v[2] != particle.position.v[2] ||
|
||||
oldScale != particle.scale) {
|
||||
anyChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyChanged) {
|
||||
drawDataDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SparkEmitter::emit() {
|
||||
if (emissionPoints.empty()) return;
|
||||
bool emitted = false;
|
||||
|
||||
for (int i = 0; i < emissionPoints.size(); ++i) {
|
||||
bool particleFound = false;
|
||||
|
||||
for (auto& particle : particles) {
|
||||
if (!particle.active) {
|
||||
initParticle(particle, i);
|
||||
particle.active = true;
|
||||
particle.lifeTime = 0;
|
||||
particle.position = emissionPoints[i];
|
||||
particle.emitterIndex = i;
|
||||
particleFound = true;
|
||||
emitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!particleFound && !particles.empty()) {
|
||||
size_t oldestIndex = 0;
|
||||
float maxLifeTime = 0;
|
||||
|
||||
for (size_t j = 0; j < particles.size(); ++j) {
|
||||
if (particles[j].lifeTime > maxLifeTime) {
|
||||
maxLifeTime = particles[j].lifeTime;
|
||||
oldestIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
initParticle(particles[oldestIndex], i);
|
||||
particles[oldestIndex].active = true;
|
||||
particles[oldestIndex].lifeTime = 0;
|
||||
particles[oldestIndex].position = emissionPoints[i];
|
||||
particles[oldestIndex].emitterIndex = i;
|
||||
emitted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (emitted) {
|
||||
drawDataDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SparkEmitter::setEmissionPoints(const std::vector<Vector3f>& positions) {
|
||||
emissionPoints = positions;
|
||||
maxParticles = positions.size() * 100;
|
||||
particles.resize(maxParticles);
|
||||
drawDataDirty = true;
|
||||
}
|
||||
|
||||
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
|
||||
particle.velocity = getRandomVelocity(emitterIndex);
|
||||
particle.scale = 1.0f;
|
||||
particle.maxLifeTime = 800.0f + (rand() % 400);
|
||||
particle.emitterIndex = emitterIndex;
|
||||
}
|
||||
|
||||
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
static std::uniform_real_distribution<> angleDist(0, 2 * M_PI);
|
||||
static std::uniform_real_distribution<> speedDist(0.5f, 2.0f);
|
||||
static std::uniform_real_distribution<> zSpeedDist(1.0f, 3.0f);
|
||||
|
||||
float angle = angleDist(gen);
|
||||
float speed = speedDist(gen);
|
||||
float zSpeed = zSpeedDist(gen);
|
||||
|
||||
if (emitterIndex == 0) {
|
||||
return Vector3f{
|
||||
cosf(angle) * speed - 0.3f,
|
||||
sinf(angle) * speed,
|
||||
zSpeed
|
||||
};
|
||||
}
|
||||
else {
|
||||
return Vector3f{
|
||||
cosf(angle) * speed + 0.3f,
|
||||
sinf(angle) * speed,
|
||||
zSpeed
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<SparkParticle>& SparkEmitter::getParticles() const {
|
||||
return particles;
|
||||
}
|
||||
|
||||
size_t SparkEmitter::getActiveParticleCount() const {
|
||||
size_t count = 0;
|
||||
for (const auto& particle : particles) {
|
||||
if (particle.active) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
66
SparkEmitter.h
Normal file
66
SparkEmitter.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "ZLMath.h"
|
||||
#include "Renderer.h"
|
||||
#include "TextureManager.h"
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct SparkParticle {
|
||||
Vector3f position;
|
||||
Vector3f velocity;
|
||||
float scale;
|
||||
float lifeTime;
|
||||
float maxLifeTime;
|
||||
bool active;
|
||||
int emitterIndex;
|
||||
|
||||
SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f),
|
||||
lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) {
|
||||
}
|
||||
};
|
||||
|
||||
class SparkEmitter {
|
||||
private:
|
||||
std::vector<SparkParticle> particles;
|
||||
std::vector<Vector3f> emissionPoints;
|
||||
std::chrono::steady_clock::time_point lastEmissionTime;
|
||||
float emissionRate;
|
||||
bool isActive;
|
||||
|
||||
std::vector<Vector3f> drawPositions;
|
||||
std::vector<Vector2f> drawTexCoords;
|
||||
bool drawDataDirty;
|
||||
VertexRenderStruct sparkQuad;
|
||||
std::shared_ptr<Texture> texture;
|
||||
|
||||
int maxParticles;
|
||||
|
||||
void prepareDrawData();
|
||||
|
||||
public:
|
||||
SparkEmitter();
|
||||
SparkEmitter(const std::vector<Vector3f>& positions, float rate = 100.0f);
|
||||
SparkEmitter(const std::vector<Vector3f>& positions,
|
||||
std::shared_ptr<Texture> tex,
|
||||
float rate = 100.0f);
|
||||
|
||||
void setEmissionPoints(const std::vector<Vector3f>& positions);
|
||||
void setTexture(std::shared_ptr<Texture> tex);
|
||||
|
||||
void update(float deltaTimeMs);
|
||||
void emit();
|
||||
|
||||
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
|
||||
|
||||
const std::vector<SparkParticle>& getParticles() const;
|
||||
size_t getActiveParticleCount() const;
|
||||
|
||||
private:
|
||||
void initParticle(SparkParticle& particle, int emitterIndex);
|
||||
Vector3f getRandomVelocity(int emitterIndex);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
BIN
resources/spark.png
(Stored with Git LFS)
Normal file
BIN
resources/spark.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -5,8 +5,8 @@ varying vec2 texCoord;
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture2D(Texture,texCoord).rgba;
|
||||
gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0);
|
||||
//gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0);
|
||||
|
||||
//gl_FragColor = color;
|
||||
gl_FragColor = color;
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user