working on adding particle shape types

This commit is contained in:
SamGondelman 2019-03-18 12:05:17 -07:00
parent 840f3a3a2e
commit ea50133146
7 changed files with 138 additions and 55 deletions

View file

@ -307,10 +307,6 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
}
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
// because the caching system only allows one Geometry per url, and because this url might also be used
// as a visual model, we need to change this url in some way. We add a "collision-hull" query-arg so it
// will end up in a different hash-key in ResourceCache. TODO: It would be better to use the same URL and
// parse it twice.
auto currentCompoundShapeURL = getCompoundShapeURL();
ModelEntityItem::setCompoundShapeURL(url);
if (getCompoundShapeURL() != currentCompoundShapeURL || !getModel()) {

View file

@ -9,13 +9,11 @@
//
#include "RenderableParticleEffectEntityItem.h"
#include <StencilMaskPass.h>
#include <GeometryCache.h>
#include <shaders/Shaders.h>
using namespace render;
using namespace render::entities;
@ -79,6 +77,14 @@ bool ParticleEffectEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedE
return true;
}
if (_shapeType != entity->getShapeType()) {
return true;
}
if (_compoundShapeURL != entity->getCompoundShapeURL()) {
return true;
}
return false;
}
@ -87,11 +93,17 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
if (!newParticleProperties.valid()) {
qCWarning(entitiesrenderer) << "Bad particle properties";
}
if (resultWithReadLock<bool>([&]{ return _particleProperties != newParticleProperties; })) {
if (resultWithReadLock<bool>([&] { return _particleProperties != newParticleProperties; })) {
_timeUntilNextEmit = 0;
withWriteLock([&]{
withWriteLock([&] {
_particleProperties = newParticleProperties;
_shapeType = entity->getShapeType();
QString compoundShapeURL = entity->getCompoundShapeURL();
if (_compoundShapeURL != compoundShapeURL) {
_compoundShapeURL = compoundShapeURL;
fetchGeometryResource();
}
if (!_prevEmitterShouldTrailInitialized) {
_prevEmitterShouldTrailInitialized = true;
_prevEmitterShouldTrail = _particleProperties.emission.shouldTrail;
@ -104,10 +116,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
});
_emitting = entity->getIsEmitting();
bool textureEmpty = resultWithReadLock<bool>([&]{ return _particleProperties.textures.isEmpty(); });
bool textureEmpty = resultWithReadLock<bool>([&] { return _particleProperties.textures.isEmpty(); });
if (textureEmpty) {
if (_networkTexture) {
withWriteLock([&] {
withWriteLock([&] {
_networkTexture.reset();
});
}
@ -116,11 +128,11 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
entity->setVisuallyReady(true);
});
} else {
bool textureNeedsUpdate = resultWithReadLock<bool>([&]{
bool textureNeedsUpdate = resultWithReadLock<bool>([&] {
return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
});
if (textureNeedsUpdate) {
withWriteLock([&] {
withWriteLock([&] {
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
});
}
@ -144,7 +156,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
// Fill in Uniforms structure
ParticleUniforms particleUniforms;
withReadLock([&]{
withReadLock([&] {
particleUniforms.radius.start = _particleProperties.radius.range.start;
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
@ -183,7 +195,8 @@ Item::Bound ParticleEffectEntityRenderer::getBound() {
static const size_t VERTEX_PER_PARTICLE = 4;
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties) {
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource) {
CpuParticle particle;
const auto& accelerationSpread = particleProperties.emission.acceleration.spread;
@ -221,33 +234,53 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
float azimuth;
if (azimuthFinish >= azimuthStart) {
azimuth = azimuthStart + (azimuthFinish - azimuthStart) * randFloat();
azimuth = azimuthStart + (azimuthFinish - azimuthStart) * randFloat();
} else {
azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat();
}
if (emitDimensions == Vectors::ZERO) {
if (emitDimensions == Vectors::ZERO || shapeType == ShapeType::SHAPE_TYPE_NONE) {
// Point
emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z;
} else {
// Ellipsoid
float radiusScale = 1.0f;
if (emitRadiusStart < 1.0f) {
float randRadius =
emitRadiusStart + randFloatInRange(0.0f, particle::MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
glm::vec3 emitPosition;
switch (shapeType) {
case ShapeType::SHAPE_TYPE_BOX:
case ShapeType::SHAPE_TYPE_CAPSULE_X:
case ShapeType::SHAPE_TYPE_CAPSULE_Y:
case ShapeType::SHAPE_TYPE_CAPSULE_Z:
case ShapeType::SHAPE_TYPE_CYLINDER_X:
case ShapeType::SHAPE_TYPE_CYLINDER_Y:
case ShapeType::SHAPE_TYPE_CYLINDER_Z:
case ShapeType::SHAPE_TYPE_CIRCLE:
case ShapeType::SHAPE_TYPE_PLANE:
case ShapeType::SHAPE_TYPE_COMPOUND:
case ShapeType::SHAPE_TYPE_SPHERE:
case ShapeType::SHAPE_TYPE_ELLIPSOID:
default: {
float radiusScale = 1.0f;
if (emitRadiusStart < 1.0f) {
float randRadius =
emitRadiusStart + randFloatInRange(0.0f, particle::MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
}
glm::vec3 radii = radiusScale * 0.5f * emitDimensions;
float x = radii.x * glm::cos(elevation) * glm::cos(azimuth);
float y = radii.y * glm::cos(elevation) * glm::sin(azimuth);
float z = radii.z * glm::sin(elevation);
emitPosition = glm::vec3(x, y, z);
emitDirection = glm::normalize(glm::vec3(radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f,
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f));
}
}
glm::vec3 radii = radiusScale * 0.5f * emitDimensions;
float x = radii.x * glm::cos(elevation) * glm::cos(azimuth);
float y = radii.y * glm::cos(elevation) * glm::sin(azimuth);
float z = radii.z * glm::sin(elevation);
glm::vec3 emitPosition = glm::vec3(x, y, z);
emitDirection = glm::normalize(glm::vec3(
radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f,
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
));
particle.relativePosition += emitOrientation * emitPosition;
}
}
@ -267,20 +300,25 @@ void ParticleEffectEntityRenderer::stepSimulation() {
const auto now = usecTimestampNow();
const auto interval = std::min<uint64_t>(USECS_PER_SECOND / 60, now - _lastSimulated);
_lastSimulated = now;
particle::Properties particleProperties;
withReadLock([&]{
ShapeType shapeType;
GeometryResource::Pointer geometryResource;
withReadLock([&] {
particleProperties = _particleProperties;
shapeType = _shapeType;
geometryResource = _geometryResource;
});
const auto& modelTransform = getModelTransform();
if (_emitting && particleProperties.emitting()) {
if (_emitting && particleProperties.emitting() &&
(_shapeType != ShapeType::SHAPE_TYPE_COMPOUND || (_geometryResource && _geometryResource->isLoaded()))) {
uint64_t emitInterval = particleProperties.emitIntervalUsecs();
if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
auto timeRemaining = interval;
while (timeRemaining > _timeUntilNextEmit) {
// emit particle
_cpuParticles.push_back(createParticle(now, modelTransform, particleProperties));
_cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource));
_timeUntilNextEmit = emitInterval;
if (emitInterval < timeRemaining) {
timeRemaining -= emitInterval;
@ -297,7 +335,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
}
const float deltaTime = (float)interval / (float)USECS_PER_SECOND;
// update the particles
// update the particles
for (auto& particle : _cpuParticles) {
if (_prevEmitterShouldTrail != particleProperties.emission.shouldTrail) {
if (_prevEmitterShouldTrail) {
@ -313,7 +351,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
static GpuParticles gpuParticles;
gpuParticles.clear();
gpuParticles.reserve(_cpuParticles.size()); // Reserve space
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform](const CpuParticle& particle) {
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform] (const CpuParticle& particle) {
glm::vec3 position = particle.relativePosition + (particleProperties.emission.shouldTrail ? particle.basePosition : modelTransform.getTranslation());
return GpuParticle(position, glm::vec2(particle.lifetime, particle.seed));
});
@ -358,3 +396,12 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
auto numParticles = _particleBuffer->getSize() / sizeof(GpuParticle);
batch.drawInstanced((gpu::uint32)numParticles, gpu::TRIANGLE_STRIP, (gpu::uint32)VERTEX_PER_PARTICLE);
}
void ParticleEffectEntityRenderer::fetchGeometryResource() {
QUrl hullURL(_compoundShapeURL);
if (hullURL.isEmpty()) {
_geometryResource.reset();
} else {
_geometryResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
}
}

View file

@ -81,7 +81,8 @@ private:
glm::vec2 spare;
};
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties);
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource);
void stepSimulation();
particle::Properties _particleProperties;
@ -90,11 +91,16 @@ private:
CpuParticles _cpuParticles;
bool _emitting { false };
uint64_t _timeUntilNextEmit { 0 };
BufferPointer _particleBuffer{ std::make_shared<Buffer>() };
BufferPointer _particleBuffer { std::make_shared<Buffer>() };
BufferView _uniformBuffer;
quint64 _lastSimulated { 0 };
PulsePropertyGroup _pulseProperties;
ShapeType _shapeType;
QString _compoundShapeURL;
void fetchGeometryResource();
GeometryResource::Pointer _geometryResource;
NetworkTexturePointer _networkTexture;
ScenePointer _scene;

View file

@ -1114,23 +1114,28 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* default, particles emit along the entity's local z-axis, and <code>azimuthStart</code> and <code>azimuthFinish</code>
* are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e.,
* the particles emit vertically.
* @property {Vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted.
* @property {number} emitRadiusStart=1 - The starting radius within the ellipsoid at which particles start being emitted;
* range <code>0.0</code> &ndash; <code>1.0</code> for the ellipsoid center to the ellipsoid surface, respectively.
* Particles are emitted from the portion of the ellipsoid that lies between <code>emitRadiusStart</code> and the
* ellipsoid's surface.
* @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. The shape is specified with
* <code>shapeType</code>.
* @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted;
* range <code>0.0</code> &ndash; <code>1.0</code> for the center to the surface, respectively.
* Particles are emitted from the portion of the shape that lies between <code>emitRadiusStart</code> and the
* shape's surface.
* @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted
* within the ellipsoid; range <code>0</code> &ndash; <code>Math.PI</code>. Particles are emitted from the portion of the
* ellipsoid that lies between <code>polarStart<code> and <code>polarFinish</code>.
* ellipsoid that lies between <code>polarStart<code> and <code>polarFinish</code>. Only used if <code>shapeType</code> is
* <code>ellipsoid</code>.
* @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted
* within the ellipsoid; range <code>0</code> &ndash; <code>Math.PI</code>. Particles are emitted from the portion of the
* ellipsoid that lies between <code>polarStart<code> and <code>polarFinish</code>.
* ellipsoid that lies between <code>polarStart<code> and <code>polarFinish</code>. Only used if <code>shapeType</code> is
* <code>ellipsoid</code>.
* @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local
* z-axis at which particles start being emitted; range <code>-Math.PI</code> &ndash; <code>Math.PI</code>. Particles are
* emitted from the portion of the ellipsoid that lies between <code>azimuthStart<code> and <code>azimuthFinish</code>.
* Only used if <code>shapeType</code> is <code>ellipsoid</code>.
* @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local
* z-axis at which particles stop being emitted; range <code>-Math.PI</code> &ndash; <code>Math.PI</code>. Particles are
* emitted from the portion of the ellipsoid that lies between <code>azimuthStart<code> and <code>azimuthFinish</code>.
* Only used if <code>shapeType</code> is <code>ellipsoid</code>.
*
* @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency,
* use PNG format.
@ -1170,7 +1175,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* up in the world. If true, they will point towards the entity's up vector, based on its orientation.
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
*
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
* @property {ShapeType} shapeType="ellipsoid" - The shape of the collision hull used if collisions are enabled.
* @property {string} compoundShapeURL="" - The model file to use for the compound shape if <code>shapeType</code> is
* <code>"compound"</code>.
*
* @example <caption>Create a ball of green smoke.</caption>
* particles = Entities.addEntity({
@ -1658,6 +1665,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
// Particles only
if (_type == EntityTypes::ParticleEffect) {
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
@ -3104,6 +3112,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
if (properties.getType() == EntityTypes::ParticleEffect) {
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType()));
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
_staticPulse.setProperties(properties);
@ -3584,6 +3593,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
if (properties.getType() == EntityTypes::ParticleEffect) {
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);

View file

@ -410,6 +410,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
withReadLock([&] {
@ -464,6 +465,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
withWriteLock([&] {
@ -540,6 +542,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
withWriteLock([&] {
@ -598,6 +601,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_SHAPE_TYPE;
requestedProperties += PROP_COMPOUND_SHAPE_URL;
requestedProperties += PROP_COLOR;
requestedProperties += PROP_ALPHA;
requestedProperties += _pulseProperties.getEntityProperties(params);
@ -656,6 +660,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
withReadLock([&] {
@ -726,6 +731,24 @@ void ParticleEffectEntityItem::setShapeType(ShapeType type) {
});
}
ShapeType ParticleEffectEntityItem::getShapeType() const {
return resultWithReadLock<ShapeType>([&] {
return _shapeType;
});
}
void ParticleEffectEntityItem::setCompoundShapeURL(const QString& compoundShapeURL) {
withWriteLock([&] {
_compoundShapeURL = compoundShapeURL;
});
}
QString ParticleEffectEntityItem::getCompoundShapeURL() const {
return resultWithReadLock<QString>([&] {
return _compoundShapeURL;
});
}
void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) {
withWriteLock([&] {
_particleProperties.maxParticles = glm::clamp(maxParticles, MINIMUM_MAX_PARTICLES, MAXIMUM_MAX_PARTICLES);

View file

@ -79,6 +79,7 @@ namespace particle {
static const QString DEFAULT_TEXTURES = "";
static const bool DEFAULT_EMITTER_SHOULD_TRAIL = false;
static const bool DEFAULT_ROTATE_WITH_ENTITY = false;
static const ShapeType DEFAULT_SHAPE_TYPE = ShapeType::SHAPE_TYPE_ELLIPSOID;
template <typename T>
struct Range {
@ -255,7 +256,10 @@ public:
float getAlphaSpread() const { return _particleProperties.alpha.gradient.spread; }
void setShapeType(ShapeType type) override;
virtual ShapeType getShapeType() const override { return _shapeType; }
virtual ShapeType getShapeType() const override;
QString getCompoundShapeURL() const;
virtual void setCompoundShapeURL(const QString& url);
virtual void debugDump() const override;
@ -349,7 +353,8 @@ protected:
PulsePropertyGroup _pulseProperties;
bool _isEmitting { true };
ShapeType _shapeType { SHAPE_TYPE_NONE };
ShapeType _shapeType{ particle::DEFAULT_SHAPE_TYPE };
QString _compoundShapeURL { "" };
};
#endif // hifi_ParticleEffectEntityItem_h

View file

@ -13,7 +13,6 @@
#include <glm/gtx/transform.hpp>
#include <QDebug>
#include <QUrlQuery>
#include <ByteCountCoding.h>
@ -463,9 +462,6 @@ void ZoneEntityItem::fetchCollisionGeometryResource() {
if (hullURL.isEmpty()) {
_shapeResource.reset();
} else {
QUrlQuery queryArgs(hullURL);
queryArgs.addQueryItem("collision-hull", "");
hullURL.setQuery(queryArgs);
_shapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
}
}