particles don't spin with head roll, add particle spin properties, negative emitSpeed

This commit is contained in:
SamGondelman 2018-07-24 23:47:58 -07:00
parent db8846796f
commit 4750611af9
12 changed files with 255 additions and 21 deletions

View file

@ -144,6 +144,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
particleUniforms.color.middle = _particleProperties.getColorMiddle();
particleUniforms.color.finish = _particleProperties.getColorFinish();
particleUniforms.color.spread = _particleProperties.getColorSpread();
particleUniforms.spin.start = _particleProperties.spin.range.start;
particleUniforms.spin.middle = _particleProperties.spin.gradient.target;
particleUniforms.spin.finish = _particleProperties.spin.range.finish;
particleUniforms.spin.spread = _particleProperties.spin.gradient.spread;
particleUniforms.lifespan = _particleProperties.lifespan;
});
// Update particle uniforms

View file

@ -74,6 +74,7 @@ private:
struct ParticleUniforms {
InterpolationData<float> radius;
InterpolationData<glm::vec4> color; // rgba
InterpolationData<float> spin;
float lifespan;
glm::vec3 spare;
};

View file

@ -27,10 +27,17 @@ struct Colors {
vec4 finish;
vec4 spread;
};
struct Spin {
float start;
float middle;
float finish;
float spread;
};
struct ParticleUniforms {
Radii radius;
Colors color;
Spin spin;
vec4 lifespan; // x is lifespan, 3 spare floats
};
@ -44,15 +51,6 @@ layout(location=2) in vec2 inColor; // This is actual Lifetime + Seed
out vec4 varColor;
out vec2 varTexcoord;
const int NUM_VERTICES_PER_PARTICLE = 4;
// This ordering ensures that un-rotated particles render upright in the viewer.
const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE](
vec4(-1.0, 1.0, 0.0, 0.0),
vec4(-1.0, -1.0, 0.0, 0.0),
vec4(1.0, 1.0, 0.0, 0.0),
vec4(1.0, -1.0, 0.0, 0.0)
);
float bezierInterpolate(float y1, float y2, float y3, float u) {
// https://en.wikipedia.org/wiki/Bezier_curve
return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3;
@ -103,6 +101,15 @@ vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) {
interpolate3Points(y1.w, y2.w, y3.w, u));
}
const int NUM_VERTICES_PER_PARTICLE = 4;
const vec2 TEX_COORDS[NUM_VERTICES_PER_PARTICLE] = vec2[NUM_VERTICES_PER_PARTICLE](
vec2(-1.0, 0.0),
vec2(-1.0, 1.0),
vec2(0.0, 0.0),
vec2(0.0, 1.0)
);
void main(void) {
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
@ -116,25 +123,47 @@ void main(void) {
float age = inColor.x / particle.lifespan.x;
float seed = inColor.y;
// Pass the texcoord and the z texcoord is representing the texture icon
// Offset for corrected vertex ordering.
varTexcoord = vec2((UNIT_QUAD[twoTriID].xy -1.0) * vec2(0.5, -0.5));
// Pass the texcoord
varTexcoord = TEX_COORDS[twoTriID].xy;
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
vec3 colorSpread = 2.0 * vec3(hifi_hash(seed), hifi_hash(seed * 2.0), hifi_hash(seed * 3.0)) - 1.0;
varColor.rgb = clamp(varColor.rgb + colorSpread * particle.color.spread.rgb, vec3(0), vec3(1));
float alphaSpread = 2.0 * hifi_hash(seed * 4.0) - 1.0;
varColor.a = clamp(varColor.a + alphaSpread * particle.color.spread.a, 0.0, 1.0);
float spin = interpolate3Points(particle.spin.start, particle.spin.middle, particle.spin.finish, age);
float spinSpread = 2.0 * hifi_hash(seed * 5.0) - 1.0;
spin = spin + spinSpread * particle.spin.spread;
// anchor point in eye space
float radius = interpolate3Points(particle.radius.start, particle.radius.middle, particle.radius.finish, age);
float radiusSpread = 2.0 * hifi_hash(seed * 5.0) - 1.0;
float radiusSpread = 2.0 * hifi_hash(seed * 6.0) - 1.0;
radius = max(radius + radiusSpread * particle.radius.spread, 0.0);
vec4 quadPos = radius * UNIT_QUAD[twoTriID];
vec4 anchorPoint;
vec4 _inPosition = vec4(inPosition, 1.0);
<$transformModelToEyePos(cam, obj, _inPosition, anchorPoint)$>
vec4 eyePos = anchorPoint + quadPos;
mat3 view3 = mat3(cam._view);
vec3 worldUpEye = normalize(view3 * vec3(0, 1, 0));
vec3 right = cross(vec3(0, 0, -1), worldUpEye);
vec3 up = cross(right, vec3(0, 0, -1));
// This ordering ensures that un-rotated particles render upright in the viewer.
vec3 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec3[NUM_VERTICES_PER_PARTICLE](
normalize(-right + up),
normalize(-right - up),
normalize(right + up),
normalize(right - up)
);
float c = cos(spin);
float s = sin(spin);
mat4 rotation = mat4(
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
vec4 quadPos = radius * vec4(UNIT_QUAD[twoTriID], 0.0);
vec4 eyePos = anchorPoint + rotation * quadPos;
<$transformEyeToClipPos(cam, eyePos, gl_Position)$>
}

View file

@ -369,6 +369,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot);
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_DATA, materialData);
CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_SPIN, particleSpin);
CHECK_PROPERTY_CHANGE(PROP_SPIN_SPREAD, spinSpread);
CHECK_PROPERTY_CHANGE(PROP_SPIN_START, spinStart);
CHECK_PROPERTY_CHANGE(PROP_SPIN_FINISH, spinFinish);
// Certifiable Properties
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
@ -908,6 +912,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* <code>alpha</code> value is used.
* @property {number} alphaSpread=0 - The spread in alpha that each particle is given. If <code>alpha == 0.5</code>
* and <code>alphaSpread == 0.25</code>, each particle will have an alpha in the range <code>0.25</code> &ndash; <code>0.75</code>.
* @property {number} particleSpin=0 - The spin of each particle at the middle of its life. <code>-2*PI - 2*PI</code>.
* @property {number} spinStart=null - The spin of each particle at the start of its life. In the range <code>-2*PI</code> &ndash; <code>2*PI</code>.
* If <code>null</code>, the <code>particleSpin</code> value is used.
* @property {number} spinFinish=null - The spin of each particle at the end of its life. In the range <code>-2*PI</code> &ndash; <code>2*PI</code>.
* If <code>null</code>, the <code>particleSpin</code> value is used.
* @property {number} spinSpread=0 - The spread in spin that each particle is given. <code>-2*PI - 2*PI</code>. If <code>particleSpin == PI</code>
* and <code>spinSpread == PI/2</code>, each particle will have a spin in the range <code>PI/2</code> &ndash; <code>3*PI/2</code>.
*
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
*
@ -1291,6 +1302,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_SPIN, particleSpin);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_SPREAD, spinSpread);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_START, spinStart);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_FINISH, spinFinish);
}
// Models only
@ -1583,6 +1598,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingRot, float, setMaterialMappingRot);
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialData, QString, setMaterialData);
COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera);
COPY_PROPERTY_FROM_QSCRIPTVALUE(particleSpin, float, setParticleSpin);
COPY_PROPERTY_FROM_QSCRIPTVALUE(spinSpread, float, setSpinSpread);
COPY_PROPERTY_FROM_QSCRIPTVALUE(spinStart, float, setSpinStart);
COPY_PROPERTY_FROM_QSCRIPTVALUE(spinFinish, float, setSpinFinish);
// Certifiable Properties
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
@ -1751,6 +1770,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(radiusSpread);
COPY_PROPERTY_IF_CHANGED(radiusStart);
COPY_PROPERTY_IF_CHANGED(radiusFinish);
COPY_PROPERTY_IF_CHANGED(particleSpin);
COPY_PROPERTY_IF_CHANGED(spinSpread);
COPY_PROPERTY_IF_CHANGED(spinStart);
COPY_PROPERTY_IF_CHANGED(spinFinish);
// Certifiable Properties
COPY_PROPERTY_IF_CHANGED(itemName);
@ -1964,6 +1987,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool);
ADD_PROPERTY_TO_MAP(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float);
ADD_PROPERTY_TO_MAP(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float);
ADD_PROPERTY_TO_MAP(PROP_SPIN_START, SpinStart, spinStart, float);
ADD_PROPERTY_TO_MAP(PROP_SPIN_FINISH, SpinFinish, spinFinish, float);
// Certifiable Properties
ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString);
ADD_PROPERTY_TO_MAP(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString);
@ -2292,6 +2320,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart());
APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish());
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail());
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, properties.getParticleSpin());
APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, properties.getSpinSpread());
APPEND_ENTITY_PROPERTY(PROP_SPIN_START, properties.getSpinStart());
APPEND_ENTITY_PROPERTY(PROP_SPIN_FINISH, properties.getSpinFinish());
}
if (properties.getType() == EntityTypes::Zone) {
@ -2667,6 +2699,10 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_SPIN, float, setParticleSpin);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_SPREAD, float, setSpinSpread);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_START, float, setSpinStart);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_FINISH, float, setSpinFinish);
}
if (properties.getType() == EntityTypes::Zone) {
@ -2961,6 +2997,10 @@ void EntityItemProperties::markAllChanged() {
_colorFinishChanged = true;
_alphaStartChanged = true;
_alphaFinishChanged = true;
_particleSpinChanged = true;
_spinStartChanged = true;
_spinFinishChanged = true;
_spinSpreadChanged = true;
_materialURLChanged = true;
_materialMappingModeChanged = true;
@ -3309,6 +3349,18 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (radiusFinishChanged()) {
out += "radiusFinish";
}
if (particleSpinChanged()) {
out += "particleSpin";
}
if (spinSpreadChanged()) {
out += "spinSpread";
}
if (spinStartChanged()) {
out += "spinStart";
}
if (spinFinishChanged()) {
out += "spinFinish";
}
if (materialURLChanged()) {
out += "materialURL";
}

View file

@ -235,6 +235,11 @@ public:
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN);
DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD);
DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START);
DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH)
// Certifiable Properties - related to Proof of Purchase certificates
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);
DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION);

View file

@ -251,6 +251,11 @@ enum EntityPropertyList {
PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire, only used locally
PROP_PARTICLE_SPIN,
PROP_SPIN_START,
PROP_SPIN_FINISH,
PROP_SPIN_SPREAD,
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line
PROP_AFTER_LAST_ITEM,

View file

@ -91,6 +91,7 @@ bool operator==(const Properties& a, const Properties& b) {
(a.color == b.color) &&
(a.alpha == b.alpha) &&
(a.radius == b.radius) &&
(a.spin == b.spin) &&
(a.radiusStart == b.radiusStart) &&
(a.lifespan == b.lifespan) &&
(a.maxParticles == b.maxParticles) &&
@ -130,7 +131,11 @@ bool Properties::valid() const {
(radius.gradient.target == glm::clamp(radius.gradient.target, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
(radius.range.start == glm::clamp(radius.range.start, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
(radius.range.finish == glm::clamp(radius.range.finish, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
(radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS));
(radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) &&
(spin.gradient.target == glm::clamp(spin.gradient.target, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) &&
(spin.range.start == glm::clamp(spin.range.start, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) &&
(spin.range.finish == glm::clamp(spin.range.finish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) &&
(spin.gradient.spread == glm::clamp(spin.gradient.spread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN));
}
bool Properties::emitting() const {
@ -332,6 +337,43 @@ void ParticleEffectEntityItem::setRadiusSpread(float radiusSpread) {
}
}
void ParticleEffectEntityItem::setParticleSpin(float particleSpin) {
particleSpin = glm::clamp(particleSpin, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
if (particleSpin != _particleProperties.spin.gradient.target) {
withWriteLock([&] {
_particleProperties.spin.gradient.target = particleSpin;
});
}
}
void ParticleEffectEntityItem::setSpinStart(float spinStart) {
spinStart =
glm::isnan(spinStart) ? spinStart : glm::clamp(spinStart, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
if (spinStart != _particleProperties.spin.range.start) {
withWriteLock([&] {
_particleProperties.spin.range.start = spinStart;
});
}
}
void ParticleEffectEntityItem::setSpinFinish(float spinFinish) {
spinFinish =
glm::isnan(spinFinish) ? spinFinish : glm::clamp(spinFinish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
if (spinFinish != _particleProperties.spin.range.finish) {
withWriteLock([&] {
_particleProperties.spin.range.finish = spinFinish;
});
}
}
void ParticleEffectEntityItem::setSpinSpread(float spinSpread) {
spinSpread = glm::clamp(spinSpread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN);
if (spinSpread != _particleProperties.spin.gradient.spread) {
withWriteLock([&] {
_particleProperties.spin.gradient.spread = spinSpread;
});
}
}
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
particle::Properties particleProperties;
@ -398,6 +440,10 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleSpin, getParticleSpin);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinSpread, getSpinSpread);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinStart, getSpinStart);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinFinish, getSpinFinish);
return properties;
}
@ -435,6 +481,10 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleSpin, setParticleSpin);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinSpread, setSpinSpread);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinStart, setSpinStart);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinFinish, setSpinFinish);
if (somethingChanged) {
bool wantDebug = false;
@ -515,6 +565,11 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail);
READ_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, float, setParticleSpin);
READ_ENTITY_PROPERTY(PROP_SPIN_SPREAD, float, setSpinSpread);
READ_ENTITY_PROPERTY(PROP_SPIN_START, float, setSpinStart);
READ_ENTITY_PROPERTY(PROP_SPIN_FINISH, float, setSpinFinish);
return bytesRead;
}
@ -551,6 +606,10 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
requestedProperties += PROP_AZIMUTH_START;
requestedProperties += PROP_AZIMUTH_FINISH;
requestedProperties += PROP_EMITTER_SHOULD_TRAIL;
requestedProperties += PROP_PARTICLE_SPIN;
requestedProperties += PROP_SPIN_SPREAD;
requestedProperties += PROP_SPIN_START;
requestedProperties += PROP_SPIN_FINISH;
return requestedProperties;
}
@ -594,6 +653,10 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart());
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish());
APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail());
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, getParticleSpin());
APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, getSpinSpread());
APPEND_ENTITY_PROPERTY(PROP_SPIN_START, getSpinStart());
APPEND_ENTITY_PROPERTY(PROP_SPIN_FINISH, getSpinFinish());
}
@ -689,6 +752,12 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
if (glm::isnan(result.radius.range.finish)) {
result.radius.range.finish = getParticleRadius();
}
if (glm::isnan(result.spin.range.start)) {
result.spin.range.start = getParticleSpin();
}
if (glm::isnan(result.spin.range.finish)) {
result.spin.range.finish = getParticleSpin();
}
});
if (!result.valid()) {

View file

@ -39,7 +39,7 @@ namespace particle {
static const float MINIMUM_EMIT_RATE = 0.0f;
static const float MAXIMUM_EMIT_RATE = 100000.0f;
static const float DEFAULT_EMIT_SPEED = 5.0f;
static const float MINIMUM_EMIT_SPEED = 0.0f;
static const float MINIMUM_EMIT_SPEED = -1000.0f;
static const float MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3
static const float DEFAULT_SPEED_SPREAD = 1.0f;
static const glm::quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical
@ -69,6 +69,12 @@ namespace particle {
static const float DEFAULT_RADIUS_SPREAD = 0.0f;
static const float DEFAULT_RADIUS_START = UNINITIALIZED;
static const float DEFAULT_RADIUS_FINISH = UNINITIALIZED;
static const float DEFAULT_PARTICLE_SPIN = 0.0f;
static const float DEFAULT_SPIN_START = UNINITIALIZED;
static const float DEFAULT_SPIN_FINISH = UNINITIALIZED;
static const float DEFAULT_SPIN_SPREAD = 0.0f;
static const float MINIMUM_PARTICLE_SPIN = -2.0f * SCRIPT_MAXIMUM_PI;
static const float MAXIMUM_PARTICLE_SPIN = 2.0f * SCRIPT_MAXIMUM_PI;
static const QString DEFAULT_TEXTURES = "";
static const bool DEFAULT_EMITTER_SHOULD_TRAIL = false;
@ -151,6 +157,7 @@ namespace particle {
RangeGradient<float> alpha { DEFAULT_ALPHA, DEFAULT_ALPHA_START, DEFAULT_ALPHA_FINISH, DEFAULT_ALPHA_SPREAD };
float radiusStart { DEFAULT_EMIT_RADIUS_START };
RangeGradient<float> radius { DEFAULT_PARTICLE_RADIUS, DEFAULT_RADIUS_START, DEFAULT_RADIUS_FINISH, DEFAULT_RADIUS_SPREAD };
RangeGradient<float> spin { DEFAULT_PARTICLE_SPIN, DEFAULT_SPIN_START, DEFAULT_SPIN_FINISH, DEFAULT_SPIN_SPREAD };
float lifespan { DEFAULT_LIFESPAN };
uint32_t maxParticles { DEFAULT_MAX_PARTICLES };
EmitProperties emission;
@ -168,6 +175,7 @@ namespace particle {
Properties& operator =(const Properties& other) {
color = other.color;
alpha = other.alpha;
spin = other.spin;
radius = other.radius;
lifespan = other.lifespan;
maxParticles = other.maxParticles;
@ -306,6 +314,18 @@ public:
void setRadiusSpread(float radiusSpread);
float getRadiusSpread() const { return _particleProperties.radius.gradient.spread; }
void setParticleSpin(float particleSpin);
float getParticleSpin() const { return _particleProperties.spin.gradient.target; }
void setSpinStart(float spinStart);
float getSpinStart() const { return _particleProperties.spin.range.start; }
void setSpinFinish(float spinFinish);
float getSpinFinish() const { return _particleProperties.spin.range.finish; }
void setSpinSpread(float spinSpread);
float getSpinSpread() const { return _particleProperties.spin.gradient.spread; }
void computeAndUpdateDimensions();
void setTextures(const QString& textures);

View file

@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::ParticleEntityFix);
return static_cast<PacketVersion>(EntityVersion::ParticleSpin);
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
case PacketType::AvatarIdentity:

View file

@ -238,7 +238,8 @@ enum class EntityVersion : PacketVersion {
CloneableData,
CollisionMask16Bytes,
YieldSimulationOwnership,
ParticleEntityFix
ParticleEntityFix,
ParticleSpin
};
enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -361,6 +361,48 @@
type: "Row"
}
],
Spim: [
{
id: "particleSpin",
name: "Particle Spin",
type: "SliderRadian",
min: -360.0,
max: 360.0
},
{
type: "Row"
},
{
id: "spinSpread",
name: "Spin Spread",
type: "SliderRadian",
min: -360.0,
max: 360.0
},
{
type: "Row"
},
{
id: "spinStart",
name: "Spin Start",
type: "SliderRadian",
min: -360.0,
max: 360.0
},
{
type: "Row"
},
{
id: "spinFinish",
name: "Spin Finish",
type: "SliderRadian",
min: -360.0,
max: 360.0
},
{
type: "Row"
}
],
Polar: [
{
id: "polarStart",

View file

@ -75,6 +75,12 @@ ParticleExplorerTool = function(createToolsWindow) {
if (isNaN(properties.colorFinish.red)) {
properties.colorFinish = properties.color;
}
if (isNaN(properties.spinStart)) {
properties.spinStart = properties.particleSpin;
}
if (isNaN(properties.spinFinish)) {
properties.spinFinish = properties.particleSpin;
}
sendParticleProperties(properties);
}
@ -88,8 +94,8 @@ ParticleExplorerTool = function(createToolsWindow) {
if (data.messageType === "settings_update") {
var updatedSettings = data.updatedSettings;
var optionalProps = ["alphaStart", "alphaFinish", "radiusStart", "radiusFinish", "colorStart", "colorFinish"];
var fallbackProps = ["alpha", "particleRadius", "color"];
var optionalProps = ["alphaStart", "alphaFinish", "radiusStart", "radiusFinish", "colorStart", "colorFinish", "spinStart", "spinFinish"];
var fallbackProps = ["alpha", "particleRadius", "color", "particleSpin"];
for (var i = 0; i < optionalProps.length; i++) {
var fallbackProp = fallbackProps[Math.floor(i / 2)];
var optionalValue = updatedSettings[optionalProps[i]];