Merge branch 'main' into headless-mode
This commit is contained in:
commit
ecbdb09eff
@ -16,6 +16,10 @@ Particles are a table, all fields of which are optional.
|
||||
| acceleration | Particles acceleration. | {0, -16, 0} |
|
||||
| explosion | Force of particles explosion on spawn. | {2, 2, 2} |
|
||||
| size | Size of particles. | {0.1, 0.1, 0.1} |
|
||||
| size_spread | Maximum particle size spread over time. | 0.2 |
|
||||
| angle_spread | Maximum initial rotation angle spread (0 to 1) | 0.0 |
|
||||
| min_angular_vel | Minimum angular velocity (radians per sec). Non-negative. | 0.0 |
|
||||
| max_angular_vel | Maximum angular velocity (radians per sec). Non-negative. | 0.0 |
|
||||
| spawn_shape | Shape of particle spawn area. (ball/sphere/box) | ball |
|
||||
| spawn_spread | Size of particle spawn area. | {0, 0, 0} |
|
||||
| random_sub_uv | Size of random texture subregion (1 - entire texture will be used). | 1.0 |
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} |
|
||||
| size | Размер частиц. | {0.1, 0.1, 0.1} |
|
||||
| size_spread | Максимальное отклонение времени размера частиц. | 0.2 |
|
||||
| angle_spread | Максимальное отклонение начального угла поворота (от 0 до 1) | 0.0 |
|
||||
| min_angular_vel | Минимальная угловая скорость (радианы в сек.). Неотрицательное. | 0.0 |
|
||||
| max_angular_vel | Максимальная угловая скорость (радианы в сек.). Неотрицательное. | 0.0 |
|
||||
| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball |
|
||||
| spawn_spread | Размер области спавна частиц. | {0, 0, 0} |
|
||||
| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 |
|
||||
|
||||
@ -3,5 +3,24 @@
|
||||
"material": "base:grass",
|
||||
"draw-group": 5,
|
||||
"culling": "optional",
|
||||
"particles": {
|
||||
"lifetime": 4.0,
|
||||
"spawn_interval": 1000.0,
|
||||
"acceleration": [0, -0.1, 0],
|
||||
"velocity": [0.2, -2.5, 0.3],
|
||||
"explosion": [0, 0, 0],
|
||||
"collision": false,
|
||||
"size": [0.3, 0.3, 0.3],
|
||||
"size_spread": 0.2,
|
||||
"spawn_shape": "box",
|
||||
"spawn_spread": [0.2, 0.2, 0.2],
|
||||
"angle_spread": 1.0,
|
||||
"min_angular_vel": 0.5,
|
||||
"max_angular_vel": 2.0,
|
||||
"lighting": true,
|
||||
"frames": [
|
||||
"particles:leaf_0"
|
||||
]
|
||||
},
|
||||
"base:durability": 0.7
|
||||
}
|
||||
|
||||
BIN
res/content/base/textures/particles/leaf_0.png
Normal file
BIN
res/content/base/textures/particles/leaf_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
@ -104,9 +104,8 @@ void Decorator::update(
|
||||
|
||||
void Decorator::update(float delta, const Camera& camera) {
|
||||
glm::ivec3 pos = camera.position;
|
||||
pos -= glm::ivec3(UPDATE_AREA_DIAMETER / 2);
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
update(delta, pos, camera.position);
|
||||
update(delta, pos - glm::ivec3(UPDATE_AREA_DIAMETER / 2), pos);
|
||||
}
|
||||
const auto& chunks = *player.chunks;
|
||||
const auto& indices = *level.content->getIndices();
|
||||
|
||||
@ -19,12 +19,13 @@ Emitter::Emitter(
|
||||
)
|
||||
: level(level),
|
||||
origin(std::move(origin)),
|
||||
prototype({this, 0, glm::vec3(), preset.velocity, preset.lifetime, region}),
|
||||
prototype({this, 0, {}, preset.velocity, preset.lifetime, region}),
|
||||
texture(texture),
|
||||
count(count),
|
||||
preset(std::move(preset)) {
|
||||
random.setSeed(reinterpret_cast<ptrdiff_t>(this));
|
||||
this->prototype.emitter = this;
|
||||
timer = preset.spawnInterval;
|
||||
timer = preset.spawnInterval * random.randFloat();
|
||||
}
|
||||
|
||||
const Texture* Emitter::getTexture() const {
|
||||
@ -76,6 +77,10 @@ void Emitter::update(
|
||||
count = std::max(0, count - skipped);
|
||||
timer -= skipped * spawnInterval;
|
||||
}
|
||||
if (count < 0) {
|
||||
int skipped = timer / spawnInterval;
|
||||
timer -= skipped * spawnInterval;
|
||||
}
|
||||
return;
|
||||
}
|
||||
while (count && timer > spawnInterval) {
|
||||
@ -83,6 +88,15 @@ void Emitter::update(
|
||||
Particle particle = prototype;
|
||||
particle.emitter = this;
|
||||
particle.random = random.rand32();
|
||||
if (glm::abs(preset.angleSpread) >= 0.005f) {
|
||||
particle.angle =
|
||||
random.randFloat() * preset.angleSpread * glm::pi<float>() * 2;
|
||||
}
|
||||
particle.angularVelocity =
|
||||
(preset.minAngularVelocity +
|
||||
random.randFloat() *
|
||||
(preset.maxAngularVelocity - preset.minAngularVelocity)) *
|
||||
((random.rand() % 2) * 2 - 1);
|
||||
|
||||
glm::vec3 spawnOffset = generate_coord(preset.spawnShape);
|
||||
spawnOffset *= preset.spawnSpread;
|
||||
@ -103,6 +117,7 @@ void Emitter::update(
|
||||
if (count > 0) {
|
||||
count--;
|
||||
}
|
||||
refCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,6 +129,10 @@ bool Emitter::isDead() const {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
bool Emitter::isReferred() const {
|
||||
return refCount > 0;
|
||||
}
|
||||
|
||||
const EmitterOrigin& Emitter::getOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
@ -27,6 +27,10 @@ struct Particle {
|
||||
float lifetime;
|
||||
/// @brief UV region
|
||||
UVRegion region;
|
||||
/// @brief Current rotation angle
|
||||
float angle;
|
||||
/// @brief Angular velocity
|
||||
float angularVelocity;
|
||||
};
|
||||
|
||||
class Texture;
|
||||
@ -39,7 +43,7 @@ class Emitter {
|
||||
EmitterOrigin origin;
|
||||
/// @brief Particle prototype
|
||||
Particle prototype;
|
||||
/// @brief Particle
|
||||
/// @brief Particle texture
|
||||
const Texture* texture;
|
||||
/// @brief Number of particles should be spawned before emitter deactivation.
|
||||
/// -1 is infinite.
|
||||
@ -50,6 +54,9 @@ class Emitter {
|
||||
|
||||
util::PseudoRandom random;
|
||||
public:
|
||||
/// @brief Number of references (alive particles)
|
||||
int refCount = 0;
|
||||
/// @brief Particle settings
|
||||
ParticlesPreset preset;
|
||||
|
||||
Emitter(
|
||||
@ -82,6 +89,9 @@ public:
|
||||
/// @return true if the emitter has spawned all particles
|
||||
bool isDead() const;
|
||||
|
||||
/// @return true if there is at least one alive referring particle left
|
||||
bool isReferred() const;
|
||||
|
||||
const EmitterOrigin& getOrigin() const;
|
||||
|
||||
void setOrigin(const EmitterOrigin& origin);
|
||||
|
||||
@ -36,12 +36,14 @@ static inline void update_particle(
|
||||
const auto& preset = particle.emitter->preset;
|
||||
auto& pos = particle.position;
|
||||
auto& vel = particle.velocity;
|
||||
auto& angle = particle.angle;
|
||||
|
||||
vel += delta * preset.acceleration;
|
||||
if (preset.collision && chunks.isObstacleAt(pos + vel * delta)) {
|
||||
vel *= 0.0f;
|
||||
}
|
||||
pos += vel * delta;
|
||||
angle += particle.angularVelocity * delta;
|
||||
particle.lifetime -= delta;
|
||||
}
|
||||
|
||||
@ -65,7 +67,8 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
auto iter = vec.begin();
|
||||
while (iter != vec.end()) {
|
||||
auto& particle = *iter;
|
||||
auto& preset = particle.emitter->preset;
|
||||
auto& emitter = *particle.emitter;
|
||||
auto& preset = emitter.preset;
|
||||
|
||||
if (!preset.frames.empty()) {
|
||||
float time = preset.lifetime - particle.lifetime;
|
||||
@ -86,19 +89,52 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
}
|
||||
update_particle(particle, delta, chunks);
|
||||
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
|
||||
glm::vec4 light(1, 1, 1, 0);
|
||||
if (preset.lighting) {
|
||||
light = MainBatch::sampleLight(
|
||||
particle.position, chunks, backlight
|
||||
particle.position,
|
||||
chunks,
|
||||
backlight
|
||||
);
|
||||
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
light = glm::max(
|
||||
light,
|
||||
MainBatch::sampleLight(
|
||||
particle.position -
|
||||
size * glm::vec3(x, y, z),
|
||||
chunks,
|
||||
backlight
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||
}
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
|
||||
|
||||
glm::vec3 localRight = right;
|
||||
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
|
||||
float angle = particle.angle;
|
||||
if (glm::abs(angle) >= 0.005f) {
|
||||
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
|
||||
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
|
||||
|
||||
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
|
||||
camera.front * rotatedRight.z;
|
||||
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
|
||||
camera.front * rotatedUp.z;
|
||||
}
|
||||
batch->quad(
|
||||
particle.position,
|
||||
right,
|
||||
preset.globalUpVector ? glm::vec3(0, 1, 0) : up,
|
||||
localRight,
|
||||
localUp,
|
||||
preset.size * scale,
|
||||
light,
|
||||
glm::vec3(1.0f),
|
||||
@ -106,6 +142,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
);
|
||||
if (particle.lifetime <= 0.0f) {
|
||||
iter = vec.erase(iter);
|
||||
emitter.refCount--;
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
@ -128,19 +165,15 @@ void ParticlesRenderer::render(const Camera& camera, float delta) {
|
||||
auto iter = emitters.begin();
|
||||
while (iter != emitters.end()) {
|
||||
auto& emitter = *iter->second;
|
||||
if (emitter.isDead() && !emitter.isReferred()) {
|
||||
// destruct Emitter only when there is no particles spawned by it
|
||||
iter = emitters.erase(iter);
|
||||
continue;
|
||||
}
|
||||
auto texture = emitter.getTexture();
|
||||
const auto& found = particles.find(texture);
|
||||
std::vector<Particle>* vec;
|
||||
if (found == particles.end()) {
|
||||
if (emitter.isDead()) {
|
||||
// destruct Emitter only when there is no particles spawned by it
|
||||
iter = emitters.erase(iter);
|
||||
continue;
|
||||
}
|
||||
vec = &particles[texture];
|
||||
} else {
|
||||
vec = &found->second;
|
||||
}
|
||||
vec = &particles[texture];
|
||||
emitter.update(delta, camera.position, *vec);
|
||||
iter++;
|
||||
}
|
||||
|
||||
@ -43,6 +43,9 @@ dv::value ParticlesPreset::serialize() const {
|
||||
root["explosion"] = dv::to_value(explosion);
|
||||
root["size"] = dv::to_value(size);
|
||||
root["size_spread"] = sizeSpread;
|
||||
root["angle_spread"] = angleSpread;
|
||||
root["min_angular_vel"] = minAngularVelocity;
|
||||
root["max_angular_vel"] = maxAngularVelocity;
|
||||
root["spawn_spread"] = dv::to_value(size);
|
||||
root["spawn_shape"] = to_string(spawnShape);
|
||||
root["random_sub_uv"] = randomSubUV;
|
||||
@ -58,6 +61,9 @@ void ParticlesPreset::deserialize(const dv::value& src) {
|
||||
src.at("spawn_interval").get(spawnInterval);
|
||||
src.at("lifetime").get(lifetime);
|
||||
src.at("lifetime_spread").get(lifetimeSpread);
|
||||
src.at("angle_spread").get(angleSpread);
|
||||
src.at("min_angular_vel").get(minAngularVelocity);
|
||||
src.at("max_angular_vel").get(maxAngularVelocity);
|
||||
src.at("random_sub_uv").get(randomSubUV);
|
||||
if (src.has("velocity")) {
|
||||
dv::get_vec(src["velocity"], velocity);
|
||||
|
||||
@ -27,7 +27,7 @@ struct ParticlesPreset : public Serializable {
|
||||
/// @brief Use global up vector instead of camera-dependent one
|
||||
bool globalUpVector = false;
|
||||
/// @brief Max distance of actually spawning particles.
|
||||
float maxDistance = 16.0f;
|
||||
float maxDistance = 32.0f;
|
||||
/// @brief Particles spawn interval
|
||||
float spawnInterval = 0.1f;
|
||||
/// @brief Particle life time
|
||||
@ -44,6 +44,12 @@ struct ParticlesPreset : public Serializable {
|
||||
glm::vec3 size {0.1f};
|
||||
/// @brief Particles size spread
|
||||
float sizeSpread = 0.2f;
|
||||
/// @brief Random initial angle spread
|
||||
float angleSpread = 0.0f;
|
||||
/// @brief Minimum angular velocity
|
||||
float minAngularVelocity = 0.0f;
|
||||
/// @brief Maximum angular velocity
|
||||
float maxAngularVelocity = 0.0f;
|
||||
/// @brief Spawn spread shape
|
||||
ParticleSpawnShape spawnShape = BALL;
|
||||
/// @brief Spawn spread
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user