Merge pull request #4820 from hyperlogic/ajt/particle-system-improvements

Improvements to particle entity.
This commit is contained in:
Brad Hefta-Gaub 2015-05-12 08:40:11 -07:00
commit 4d9e413b3f
9 changed files with 491 additions and 232 deletions

93
examples/ajt-test.js Normal file
View file

@ -0,0 +1,93 @@
(function () {
var spawnPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
// constructor
function TestBox() {
this.entity = Entities.addEntity({ type: "Box",
position: spawnPoint,
dimentions: { x: 1, y: 1, z: 1 },
color: { red: 100, green: 100, blue: 255 },
gravity: { x: 0, y: 0, z: 0 },
visible: true,
locked: false,
lifetime: 6000 });
var self = this;
this.timer = Script.setInterval(function () {
var colorProp = { color: { red: Math.random() * 255,
green: Math.random() * 255,
blue: Math.random() * 255 } };
Entities.editEntity(self.entity, colorProp);
}, 1000);
}
TestBox.prototype.Destroy = function () {
Script.clearInterval(this.timer);
Entities.editEntity(this.entity, { locked: false });
Entities.deleteEntity(this.entity);
}
// constructor
function TestFx(color, emitDirection, emitRate, emitStrength, blinkRate) {
var animationSettings = JSON.stringify({ fps: 30,
frameIndex: 0,
running: true,
firstFrame: 0,
lastFrame: 30,
loop: true });
this.entity = Entities.addEntity({ type: "ParticleEffect",
animationSettings: animationSettings,
position: spawnPoint,
textures: "http://www.hyperlogic.org/images/particle.png",
emitRate: emitRate,
emitStrength: emitStrength,
emitDirection: emitDirection,
color: color,
visible: true,
locked: false });
this.isPlaying = true;
var self = this;
this.timer = Script.setInterval(function () {
// flip is playing state
self.isPlaying = !self.isPlaying;
var animProp = { animationIsPlaying: self.isPlaying };
Entities.editEntity(self.entity, animProp);
}, (1 / blinkRate) * 1000);
}
TestFx.prototype.Destroy = function () {
Script.clearInterval(this.timer);
Entities.editEntity(this.entity, { locked: false });
Entities.deleteEntity(this.entity);
}
var objs = [];
function Init() {
objs.push(new TestBox());
objs.push(new TestFx({ red: 255, blue: 0, green: 0 },
{ x: 0.5, y: 1.0, z: 0.0 },
100, 3, 1));
objs.push(new TestFx({ red: 0, blue: 255, green: 0 },
{ x: 0, y: 1, z: 0 },
1000, 5, 0.5));
objs.push(new TestFx({ red: 0, blue: 0, green: 255 },
{ x: -0.5, y: 1, z: 0 },
100, 3, 1));
}
function ShutDown() {
var i, len = objs.length;
for (i = 0; i < len; i++) {
objs[i].Destroy();
}
objs = [];
}
Init();
Script.scriptEnding.connect(ShutDown);
})();

View file

@ -16,6 +16,7 @@
#include <DeferredLightingEffect.h> #include <DeferredLightingEffect.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <GeometryCache.h> #include <GeometryCache.h>
#include "EntitiesRendererLogging.h"
#include "RenderableParticleEffectEntityItem.h" #include "RenderableParticleEffectEntityItem.h"
@ -29,62 +30,158 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
} }
void RenderableParticleEffectEntityItem::render(RenderArgs* args) { void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render");
assert(getType() == EntityTypes::ParticleEffect); assert(getType() == EntityTypes::ParticleEffect);
float pa_rad = getParticleRadius(); PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render");
const float MAX_COLOR = 255.0f; if (_texturesChangedFlag) {
glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, if (_textures.isEmpty()) {
getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); _texture.clear();
} else {
// Right now we're just iterating over particles and rendering as a cross of four quads. // for now use the textures string directly.
// This is pretty dumb, it was quick enough to code up. Really, there should be many // Eventually we'll want multiple textures in a map or array.
// rendering modes, including the all-important textured billboards. _texture = DependencyManager::get<TextureCache>()->getTexture(_textures);
}
QVector<glm::vec3>* pointVec = new QVector<glm::vec3>(_paCount * VERTS_PER_PARTICLE); _texturesChangedFlag = false;
quint32 paIter = _paHead;
while (_paLife[paIter] > 0.0f) {
int j = paIter * XYZ_STRIDE;
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] - pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] + pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] + pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] - pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] + pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] - pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] - pa_rad));
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] + pa_rad));
paIter = (paIter + 1) % _maxParticles;
} }
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, *pointVec, paColor); if (!_texture) {
renderUntexturedQuads(args);
} else if (_texture && !_texture->isLoaded()) {
renderUntexturedQuads(args);
} else {
renderTexturedQuads(args);
}
};
void RenderableParticleEffectEntityItem::renderUntexturedQuads(RenderArgs* args) {
float particleRadius = getParticleRadius();
const float MAX_COLOR = 255.0f;
glm::vec4 particleColor(getColor()[RED_INDEX] / MAX_COLOR,
getColor()[GREEN_INDEX] / MAX_COLOR,
getColor()[BLUE_INDEX] / MAX_COLOR,
getLocalRenderAlpha());
glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius;
glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius;
QVector<glm::vec3> vertices(getLivingParticleCount() * VERTS_PER_PARTICLE);
quint32 count = 0;
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
glm::vec3 pos = _particlePositions[i];
// generate corners of quad, aligned to face the camera
vertices.append(pos - rightOffset + upOffset);
vertices.append(pos + rightOffset + upOffset);
vertices.append(pos + rightOffset - upOffset);
vertices.append(pos - rightOffset - upOffset);
count++;
}
// just double checking, cause if this invarient is false, we might have memory corruption bugs.
assert(count == getLivingParticleCount());
// update geometry cache with all the verts in model coordinates.
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, particleColor);
glPushMatrix(); glPushMatrix();
glm::vec3 position = getPosition(); glm::vec3 position = getPosition();
glTranslatef(position.x, position.y, position.z); glTranslatef(position.x, position.y, position.z);
glm::quat rotation = getRotation(); glm::quat rotation = getRotation();
glm::vec3 axis = glm::axis(rotation); glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix(); glPushMatrix();
glm::vec3 positionToCenter = getCenter() - position; glm::vec3 positionToCenter = getCenter() - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
DependencyManager::get<GeometryCache>()->renderVertices(gpu::QUADS, _cacheID); DependencyManager::get<GeometryCache>()->renderVertices(gpu::QUADS, _cacheID);
glPopMatrix(); glPopMatrix();
glPopMatrix();
}
static glm::vec3 zSortAxis;
static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) {
return glm::dot(rhs, ::zSortAxis) > glm::dot(lhs, ::zSortAxis);
}
void RenderableParticleEffectEntityItem::renderTexturedQuads(RenderArgs* args) {
float particleRadius = getParticleRadius();
const float MAX_COLOR = 255.0f;
glm::vec4 particleColor(getColor()[RED_INDEX] / MAX_COLOR,
getColor()[GREEN_INDEX] / MAX_COLOR,
getColor()[BLUE_INDEX] / MAX_COLOR,
getLocalRenderAlpha());
QVector<glm::vec3> positions(getLivingParticleCount());
quint32 count = 0;
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
positions.append(_particlePositions[i]);
count++;
}
// just double checking, cause if this invarient is false, we might have memory corruption bugs.
assert(count == getLivingParticleCount());
// sort particles back to front
::zSortAxis = args->_viewFrustum->getDirection();
qSort(positions.begin(), positions.end(), zSort);
QVector<glm::vec3> vertices(getLivingParticleCount() * VERTS_PER_PARTICLE);
QVector<glm::vec2> textureCoords(getLivingParticleCount() * VERTS_PER_PARTICLE);
glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius;
glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius;
for (int i = 0; i < positions.size(); i++) {
glm::vec3 pos = positions[i];
// generate corners of quad aligned to face the camera.
vertices.append(pos - rightOffset + upOffset);
vertices.append(pos + rightOffset + upOffset);
vertices.append(pos + rightOffset - upOffset);
vertices.append(pos - rightOffset - upOffset);
textureCoords.append(glm::vec2(0, 1));
textureCoords.append(glm::vec2(1, 1));
textureCoords.append(glm::vec2(1, 0));
textureCoords.append(glm::vec2(0, 0));
}
// update geometry cache with all the verts in model coordinates.
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, textureCoords, particleColor);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _texture->getID());
glPushMatrix();
glm::vec3 position = getPosition();
glTranslatef(position.x, position.y, position.z);
glm::quat rotation = getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = getCenter() - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
DependencyManager::get<GeometryCache>()->renderVertices(gpu::QUADS, _cacheID);
glPopMatrix();
glPopMatrix(); glPopMatrix();
delete pointVec; glDisable(GL_TEXTURE_2D);
}; glBindTexture(GL_TEXTURE_2D, 0);
}

View file

@ -12,6 +12,7 @@
#define hifi_RenderableParticleEffectEntityItem_h #define hifi_RenderableParticleEffectEntityItem_h
#include <ParticleEffectEntityItem.h> #include <ParticleEffectEntityItem.h>
#include <TextureCache.h>
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
public: public:
@ -19,9 +20,15 @@ public:
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual void render(RenderArgs* args); virtual void render(RenderArgs* args);
void renderUntexturedQuads(RenderArgs* args);
void renderTexturedQuads(RenderArgs* args);
protected: protected:
int _cacheID; int _cacheID;
const int VERTS_PER_PARTICLE = 16; const int VERTS_PER_PARTICLE = 4;
NetworkTexturePointer _texture;
}; };

View file

@ -18,12 +18,8 @@
// - Add controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented). // - Add controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented).
// - Add drag. // - Add drag.
// - Add some kind of support for collisions. // - Add some kind of support for collisions.
// - For simplicity, I'm currently just rendering each particle as a cross of four axis-aligned quads. Really, we'd
// want multiple render modes, including (the most important) textured billboards (always facing camera). Also, these
// should support animated textures.
// - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so // - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so
// there's no gaurantee that different clients will see simulations that look anything like the other. // there's no gaurantee that different clients will see simulations that look anything like the other.
// - MORE?
// //
// Created by Jason Rickwald on 3/2/15. // Created by Jason Rickwald on 3/2/15.
// //
@ -45,6 +41,7 @@
#include "EntitiesLogging.h" #include "EntitiesLogging.h"
#include "ParticleEffectEntityItem.h" #include "ParticleEffectEntityItem.h"
const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 };
const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f; const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
const bool ParticleEffectEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false; const bool ParticleEffectEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f; const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f;
@ -55,6 +52,7 @@ const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION(0.0f, 1.0f, 0.0
const float ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH = 25.0f; const float ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH = 25.0f;
const float ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY = -9.8f; const float ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY = -9.8f;
const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f; const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f;
const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = "";
EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
@ -63,40 +61,40 @@ EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, cons
// our non-pure virtual subclass for now... // our non-pure virtual subclass for now...
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { EntityItem(entityItemID, properties),
_maxParticles(DEFAULT_MAX_PARTICLES),
_lifespan(DEFAULT_LIFESPAN),
_emitRate(DEFAULT_EMIT_RATE),
_emitDirection(DEFAULT_EMIT_DIRECTION),
_emitStrength(DEFAULT_EMIT_STRENGTH),
_localGravity(DEFAULT_LOCAL_GRAVITY),
_particleRadius(DEFAULT_PARTICLE_RADIUS),
_lastAnimated(usecTimestampNow()),
_animationLoop(),
_animationSettings(),
_textures(DEFAULT_TEXTURES),
_texturesChangedFlag(false),
_shapeType(SHAPE_TYPE_NONE),
_particleLifetimes(DEFAULT_MAX_PARTICLES, 0.0f),
_particlePositions(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
_particleVelocities(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
_timeUntilNextEmit(0.0f),
_particleHeadIndex(0),
_particleTailIndex(0),
_particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)),
_particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) {
_type = EntityTypes::ParticleEffect; _type = EntityTypes::ParticleEffect;
_maxParticles = DEFAULT_MAX_PARTICLES; setColor(DEFAULT_COLOR);
_lifespan = DEFAULT_LIFESPAN;
_emitRate = DEFAULT_EMIT_RATE;
_emitDirection = DEFAULT_EMIT_DIRECTION;
_emitStrength = DEFAULT_EMIT_STRENGTH;
_localGravity = DEFAULT_LOCAL_GRAVITY;
_particleRadius = DEFAULT_PARTICLE_RADIUS;
setProperties(properties); setProperties(properties);
// this is a pretty dumb thing to do, and it should probably be changed to use a more dynamic
// data structure in the future. I'm just trying to get some code out the door for now (and it's
// at least time efficient (though not space efficient).
// Also, this being a real-time application, it's doubtful we'll ever have millions of particles
// to keep track of, so this really isn't all that bad.
_paLife = new float[_maxParticles];
_paPosition = new float[_maxParticles * XYZ_STRIDE]; // x,y,z
_paVelocity = new float[_maxParticles * XYZ_STRIDE]; // x,y,z
_paXmax = _paYmax = _paZmax = 1.0f;
_paXmin = _paYmin = _paZmin = -1.0f;
_randSeed = (unsigned int) glm::abs(_lifespan + _emitRate + _localGravity + getPosition().x + getPosition().y + getPosition().z);
resetSimulation();
_lastAnimated = usecTimestampNow();
} }
ParticleEffectEntityItem::~ParticleEffectEntityItem() { ParticleEffectEntityItem::~ParticleEffectEntityItem() {
delete [] _paLife;
delete [] _paPosition;
delete [] _paVelocity;
} }
EntityItemProperties ParticleEffectEntityItem::getProperties() const { EntityItemProperties ParticleEffectEntityItem::getProperties() const {
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex);
@ -111,6 +109,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitStrength, getEmitStrength); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitStrength, getEmitStrength);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(localGravity, getLocalGravity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localGravity, getLocalGravity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius); COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
return properties; return properties;
} }
@ -132,6 +131,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitStrength, setEmitStrength); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitStrength, setEmitStrength);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localGravity, setLocalGravity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localGravity, setLocalGravity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius); SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;
@ -147,8 +147,8 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
} }
int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args, ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
int bytesRead = 0; int bytesRead = 0;
const unsigned char* dataAt = data; const unsigned char* dataAt = data;
@ -186,6 +186,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, _emitStrength); READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, _emitStrength);
READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, _localGravity); READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, _localGravity);
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, _particleRadius); READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, _particleRadius);
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, _textures);
return bytesRead; return bytesRead;
} }
@ -194,7 +195,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time // TODO: eventually only include properties changed since the params.lastViewFrustumSent time
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_COLOR; requestedProperties += PROP_COLOR;
requestedProperties += PROP_ANIMATION_FPS; requestedProperties += PROP_ANIMATION_FPS;
requestedProperties += PROP_ANIMATION_FRAME_INDEX; requestedProperties += PROP_ANIMATION_FRAME_INDEX;
@ -208,17 +209,18 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
requestedProperties += PROP_EMIT_STRENGTH; requestedProperties += PROP_EMIT_STRENGTH;
requestedProperties += PROP_LOCAL_GRAVITY; requestedProperties += PROP_LOCAL_GRAVITY;
requestedProperties += PROP_PARTICLE_RADIUS; requestedProperties += PROP_PARTICLE_RADIUS;
requestedProperties += PROP_TEXTURES;
return requestedProperties; return requestedProperties;
} }
void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties, EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit, EntityPropertyFlags& propertiesDidntFit,
int& propertyCount, int& propertyCount,
OctreeElement::AppendState& appendState) const { OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true; bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
@ -234,11 +236,12 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, getEmitStrength()); APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, getEmitStrength());
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, getLocalGravity()); APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, getLocalGravity());
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, getParticleRadius()); APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, getParticleRadius());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, getTextures());
} }
bool ParticleEffectEntityItem::isAnimatingSomething() const { bool ParticleEffectEntityItem::isAnimatingSomething() const {
return getAnimationIsPlaying() && // keep animating if there are particles still alive.
getAnimationFPS() != 0.0f; return (getAnimationIsPlaying() || getLivingParticleCount() > 0) && getAnimationFPS() != 0.0f;
} }
bool ParticleEffectEntityItem::needsToCallUpdate() const { bool ParticleEffectEntityItem::needsToCallUpdate() const {
@ -246,33 +249,25 @@ bool ParticleEffectEntityItem::needsToCallUpdate() const {
} }
void ParticleEffectEntityItem::update(const quint64& now) { void ParticleEffectEntityItem::update(const quint64& now) {
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
_lastAnimated = now;
// only advance the frame index if we're playing // only advance the frame index if we're playing
if (getAnimationIsPlaying()) { if (getAnimationIsPlaying()) {
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
_lastAnimated = now;
float lastFrame = _animationLoop.getFrameIndex();
_animationLoop.simulate(deltaTime); _animationLoop.simulate(deltaTime);
float curFrame = _animationLoop.getFrameIndex();
if (curFrame > lastFrame) {
stepSimulation(deltaTime);
}
else if (curFrame < lastFrame) {
// we looped around, so restart the sim and only sim up to the point
// since the beginning of the frame range.
resetSimulation();
stepSimulation((curFrame - _animationLoop.getFirstFrame()) / _animationLoop.getFPS());
}
}
else {
_lastAnimated = now;
} }
// update the dimensions if (isAnimatingSomething()) {
glm::vec3 dims; stepSimulation(deltaTime);
dims.x = glm::max(glm::abs(_paXmin), glm::abs(_paXmax)) * 2.0f;
dims.y = glm::max(glm::abs(_paYmin), glm::abs(_paYmax)) * 2.0f; // update the dimensions
dims.z = glm::max(glm::abs(_paZmin), glm::abs(_paZmax)) * 2.0f; glm::vec3 dims;
setDimensions(dims); dims.x = glm::max(glm::abs(_particleMinBound.x), glm::abs(_particleMaxBound.x)) * 2.0f;
dims.y = glm::max(glm::abs(_particleMinBound.y), glm::abs(_particleMaxBound.y)) * 2.0f;
dims.z = glm::max(glm::abs(_particleMinBound.z), glm::abs(_particleMaxBound.z)) * 2.0f;
setDimensions(dims);
}
EntityItem::update(now); // let our base class handle it's updates... EntityItem::update(now); // let our base class handle it's updates...
} }
@ -418,96 +413,103 @@ QString ParticleEffectEntityItem::getAnimationSettings() const {
return jsonByteString; return jsonByteString;
} }
void ParticleEffectEntityItem::stepSimulation(float deltaTime) { void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) {
_paXmin = _paYmin = _paZmin = -1.0f; _particleMinBound.x = glm::min(_particleMinBound.x, point.x);
_paXmax = _paYmax = _paZmax = 1.0f; _particleMinBound.y = glm::min(_particleMinBound.y, point.y);
_particleMinBound.z = glm::min(_particleMinBound.z, point.z);
_particleMaxBound.x = glm::max(_particleMaxBound.x, point.x);
_particleMaxBound.y = glm::max(_particleMaxBound.y, point.y);
_particleMaxBound.z = glm::max(_particleMaxBound.z, point.z);
}
// update particles void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) {
quint32 updateIter = _paHead; glm::vec3 atSquared(0.0f, 0.5f * _localGravity * deltaTime * deltaTime, 0.0f);
while (_paLife[updateIter] > 0.0f) { glm::vec3 at(0.0f, _localGravity * deltaTime, 0.0f);
_paLife[updateIter] -= deltaTime; _particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared;
if (_paLife[updateIter] <= 0.0f) { _particleVelocities[index] += at;
_paLife[updateIter] = -1.0f; }
_paHead = (_paHead + 1) % _maxParticles;
_paCount--; void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
_particleMinBound = glm::vec3(-1.0f, -1.0f, -1.0f);
_particleMaxBound = glm::vec3(1.0f, 1.0f, 1.0f);
// update particles between head and tail
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
_particleLifetimes[i] -= deltaTime;
// if particle has died.
if (_particleLifetimes[i] <= 0.0f) {
// move head forward
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
} }
else { else {
// DUMB FORWARD EULER just to get it done integrateParticle(i, deltaTime);
int j = updateIter * XYZ_STRIDE; extendBounds(_particlePositions[i]);
_paPosition[j] += _paVelocity[j] * deltaTime;
_paPosition[j+1] += _paVelocity[j+1] * deltaTime;
_paPosition[j+2] += _paVelocity[j+2] * deltaTime;
_paXmin = glm::min(_paXmin, _paPosition[j]);
_paYmin = glm::min(_paYmin, _paPosition[j+1]);
_paZmin = glm::min(_paZmin, _paPosition[j+2]);
_paXmax = glm::max(_paXmax, _paPosition[j]);
_paYmax = glm::max(_paYmax, _paPosition[j + 1]);
_paZmax = glm::max(_paZmax, _paPosition[j + 2]);
// massless particles
_paVelocity[j + 1] += deltaTime * _localGravity;
} }
updateIter = (updateIter + 1) % _maxParticles;
} }
// emit new particles // emit new particles, but only if animaiton is playing
quint32 emitIdx = updateIter; if (getAnimationIsPlaying()) {
_partialEmit += ((float)_emitRate) * deltaTime;
quint32 birthed = (quint32)_partialEmit;
_partialEmit -= (float)birthed;
glm::vec3 randOffset;
for (quint32 i = 0; i < birthed; i++) { float timeLeftInFrame = deltaTime;
if (_paLife[emitIdx] < 0.0f) { while (_timeUntilNextEmit < timeLeftInFrame) {
int j = emitIdx * XYZ_STRIDE;
_paLife[emitIdx] = _lifespan;
randOffset.x = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
randOffset.y = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
randOffset.z = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
_paVelocity[j] = (_emitDirection.x * _emitStrength) + randOffset.x;
_paVelocity[j + 1] = (_emitDirection.y * _emitStrength) + randOffset.y;
_paVelocity[j + 2] = (_emitDirection.z * _emitStrength) + randOffset.z;
// DUMB FORWARD EULER just to get it done timeLeftInFrame -= _timeUntilNextEmit;
_paPosition[j] += _paVelocity[j] * deltaTime; _timeUntilNextEmit = 1.0f / _emitRate;
_paPosition[j + 1] += _paVelocity[j + 1] * deltaTime;
_paPosition[j + 2] += _paVelocity[j + 2] * deltaTime;
_paXmin = glm::min(_paXmin, _paPosition[j]); // emit a new particle at tail index.
_paYmin = glm::min(_paYmin, _paPosition[j + 1]); quint32 i = _particleTailIndex;
_paZmin = glm::min(_paZmin, _paPosition[j + 2]); _particleLifetimes[i] = _lifespan;
_paXmax = glm::max(_paXmax, _paPosition[j]);
_paYmax = glm::max(_paYmax, _paPosition[j + 1]);
_paZmax = glm::max(_paZmax, _paPosition[j + 2]);
// massless particles // jitter the _emitDirection by a random offset
// and simple gravity down glm::vec3 randOffset;
_paVelocity[j + 1] += deltaTime * _localGravity; randOffset.x = (randFloat() - 0.5f) * 0.25f * _emitStrength;
randOffset.y = (randFloat() - 0.5f) * 0.25f * _emitStrength;
randOffset.z = (randFloat() - 0.5f) * 0.25f * _emitStrength;
emitIdx = (emitIdx + 1) % _maxParticles; // set initial conditions
_paCount++; _particlePositions[i] = glm::vec3(0.0f, 0.0f, 0.0f);
_particleVelocities[i] = _emitDirection * _emitStrength + randOffset;
integrateParticle(i, timeLeftInFrame);
extendBounds(_particlePositions[i]);
_particleTailIndex = (_particleTailIndex + 1) % _maxParticles;
// overflow! move head forward by one.
// because the case of head == tail indicates an empty array, not a full one.
// This can drop an existing older particle, but this is by design, newer particles are a higher priority.
if (_particleTailIndex == _particleHeadIndex) {
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
}
} }
else
break; _timeUntilNextEmit -= timeLeftInFrame;
} }
} }
void ParticleEffectEntityItem::resetSimulation() { void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) {
for (quint32 i = 0; i < _maxParticles; i++) { _maxParticles = maxParticles;
quint32 j = i * XYZ_STRIDE;
_paLife[i] = -1.0f;
_paPosition[j] = 0.0f;
_paPosition[j+1] = 0.0f;
_paPosition[j+2] = 0.0f;
_paVelocity[j] = 0.0f;
_paVelocity[j+1] = 0.0f;
_paVelocity[j+2] = 0.0f;
}
_paCount = 0;
_paHead = 0;
_partialEmit = 0.0f;
srand(_randSeed); // TODO: try to do something smart here and preserve the state of existing particles.
// resize vectors
_particleLifetimes.resize(_maxParticles);
_particlePositions.resize(_maxParticles);
_particleVelocities.resize(_maxParticles);
// effectivly clear all particles and start emitting new ones from scratch.
_particleHeadIndex = 0;
_particleTailIndex = 0;
_timeUntilNextEmit = 0.0f;
} }
// because particles are in a ring buffer, this isn't trivial
quint32 ParticleEffectEntityItem::getLivingParticleCount() const {
if (_particleTailIndex >= _particleHeadIndex) {
return _particleTailIndex - _particleHeadIndex;
} else {
return (_maxParticles - _particleHeadIndex) + _particleTailIndex;
}
}

View file

@ -2,29 +2,6 @@
// ParticleEffectEntityItem.h // ParticleEffectEntityItem.h
// libraries/entities/src // libraries/entities/src
// //
// Some starter code for a particle simulation entity, which could ideally be used for a variety of effects.
// This is some really early and rough stuff here. It was enough for now to just get it up and running in the interface.
//
// Todo's and other notes:
// - The simulation should restart when the AnimationLoop's max frame is reached (or passed), but there doesn't seem
// to be a good way to set that max frame to something reasonable right now.
// - There seems to be a bug whereby entities on the edge of screen will just pop off or on. This is probably due
// to my lack of understanding of how entities in the octree are picked for rendering. I am updating the entity
// dimensions based on the bounds of the sim, but maybe I need to update a dirty flag or something.
// - This should support some kind of pre-roll of the simulation.
// - Just to get this out the door, I just did forward Euler integration. There are better ways.
// - Gravity always points along the Y axis. Support an actual gravity vector.
// - Add the ability to add arbitrary forces to the simulation.
// - Add controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented).
// - Add drag.
// - Add some kind of support for collisions.
// - For simplicity, I'm currently just rendering each particle as a cross of four axis-aligned quads. Really, we'd
// want multiple render modes, including (the most important) textured billboards (always facing camera). Also, these
// should support animated textures.
// - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so
// there's no gaurantee that different clients will see simulations that look anything like the other.
// - MORE?
//
// Created by Jason Rickwald on 3/2/15. // Created by Jason Rickwald on 3/2/15.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
@ -39,14 +16,14 @@
class ParticleEffectEntityItem : public EntityItem { class ParticleEffectEntityItem : public EntityItem {
public: public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual ~ParticleEffectEntityItem(); virtual ~ParticleEffectEntityItem();
ALLOW_INSTANTIATION // This class can be instantiated ALLOW_INSTANTIATION // This class can be instantiated
// methods for getting/setting all properties of this entity // methods for getting/setting all properties of this entity
virtual EntityItemProperties getProperties() const; virtual EntityItemProperties getProperties() const;
virtual bool setProperties(const EntityItemProperties& properties); virtual bool setProperties(const EntityItemProperties& properties);
@ -54,16 +31,16 @@ public:
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties, EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit, EntityPropertyFlags& propertiesDidntFit,
int& propertyCount, int& propertyCount,
OctreeElement::AppendState& appendState) const; OctreeElement::AppendState& appendState) const;
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args, ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData); EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
virtual void update(const quint64& now); virtual void update(const quint64& now);
virtual bool needsToCallUpdate() const; virtual bool needsToCallUpdate() const;
@ -71,6 +48,7 @@ public:
const rgbColor& getColor() const { return _color; } const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
static const xColor DEFAULT_COLOR;
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
void setColor(const xColor& value) { void setColor(const xColor& value) {
_color[RED_INDEX] = value.red; _color[RED_INDEX] = value.red;
@ -109,7 +87,7 @@ public:
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); } float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
static const quint32 DEFAULT_MAX_PARTICLES; static const quint32 DEFAULT_MAX_PARTICLES;
void setMaxParticles(quint32 maxParticles) { _maxParticles = maxParticles; } void setMaxParticles(quint32 maxParticles);
quint32 getMaxParticles() const { return _maxParticles; } quint32 getMaxParticles() const { return _maxParticles; }
static const float DEFAULT_LIFESPAN; static const float DEFAULT_LIFESPAN;
@ -121,7 +99,7 @@ public:
float getEmitRate() const { return _emitRate; } float getEmitRate() const { return _emitRate; }
static const glm::vec3 DEFAULT_EMIT_DIRECTION; static const glm::vec3 DEFAULT_EMIT_DIRECTION;
void setEmitDirection(glm::vec3 emitDirection) { _emitDirection = emitDirection; } void setEmitDirection(glm::vec3 emitDirection) { _emitDirection = glm::normalize(emitDirection); }
const glm::vec3& getEmitDirection() const { return _emitDirection; } const glm::vec3& getEmitDirection() const { return _emitDirection; }
static const float DEFAULT_EMIT_STRENGTH; static const float DEFAULT_EMIT_STRENGTH;
@ -141,11 +119,22 @@ public:
float getAnimationFPS() const { return _animationLoop.getFPS(); } float getAnimationFPS() const { return _animationLoop.getFPS(); }
QString getAnimationSettings() const; QString getAnimationSettings() const;
static const QString DEFAULT_TEXTURES;
const QString& getTextures() const { return _textures; }
void setTextures(const QString& textures) {
if (_textures != textures) {
_textures = textures;
_texturesChangedFlag = true;
}
}
protected: protected:
bool isAnimatingSomething() const; bool isAnimatingSomething() const;
void stepSimulation(float deltaTime); void stepSimulation(float deltaTime);
void resetSimulation(); void extendBounds(const glm::vec3& point);
void integrateParticle(quint32 index, float deltaTime);
quint32 getLivingParticleCount() const;
// the properties of this entity // the properties of this entity
rgbColor _color; rgbColor _color;
@ -159,25 +148,24 @@ protected:
quint64 _lastAnimated; quint64 _lastAnimated;
AnimationLoop _animationLoop; AnimationLoop _animationLoop;
QString _animationSettings; QString _animationSettings;
QString _textures;
bool _texturesChangedFlag;
ShapeType _shapeType = SHAPE_TYPE_NONE; ShapeType _shapeType = SHAPE_TYPE_NONE;
// all the internals of running the particle sim // all the internals of running the particle sim
const quint32 XYZ_STRIDE = 3; QVector<float> _particleLifetimes;
float* _paLife; QVector<glm::vec3> _particlePositions;
float* _paPosition; QVector<glm::vec3> _particleVelocities;
float* _paVelocity; float _timeUntilNextEmit;
float _partialEmit;
quint32 _paCount;
quint32 _paHead;
float _paXmin;
float _paXmax;
float _paYmin;
float _paYmax;
float _paZmin;
float _paZmax;
unsigned int _randSeed;
// particle arrays are a ring buffer, use these indicies
// to keep track of the living particles.
quint32 _particleHeadIndex;
quint32 _particleTailIndex;
// bounding volume
glm::vec3 _particleMaxBound;
glm::vec3 _particleMinBound;
}; };
#endif // hifi_ParticleEffectEntityItem_h #endif // hifi_ParticleEffectEntityItem_h

View file

@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
return 1; return 1;
case PacketTypeEntityAddOrEdit: case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData: case PacketTypeEntityData:
return VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY; return VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES;
case PacketTypeEntityErase: case PacketTypeEntityErase:
return 2; return 2;
case PacketTypeAudioStreamStats: case PacketTypeAudioStreamStats:

View file

@ -173,5 +173,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_NAMES = 19;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_ATMOSPHERE = 20; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_ATMOSPHERE = 20;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX = 21; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX = 21;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22;
const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23;
#endif // hifi_PacketHeaders_h #endif // hifi_PacketHeaders_h

View file

@ -721,6 +721,76 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
#endif #endif
} }
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& texCoords, const glm::vec4& color) {
BatchItemDetails& details = _registeredVertices[id];
if (details.isCreated) {
details.clear();
#ifdef WANT_DEBUG
qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED";
#endif // def WANT_DEBUG
}
const int FLOATS_PER_VERTEX = 5;
details.isCreated = true;
details.vertices = points.size();
details.vertexSize = FLOATS_PER_VERTEX;
gpu::BufferPointer verticesBuffer(new gpu::Buffer());
gpu::BufferPointer colorBuffer(new gpu::Buffer());
gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format());
gpu::BufferStreamPointer stream(new gpu::BufferStream());
details.verticesBuffer = verticesBuffer;
details.colorBuffer = colorBuffer;
details.streamFormat = streamFormat;
details.stream = stream;
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), 3 * sizeof(float));
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
assert(points.size() == texCoords.size());
details.vertices = points.size();
details.vertexSize = FLOATS_PER_VERTEX;
int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
((int(color.y * 255.0f) & 0xFF) << 8) |
((int(color.z * 255.0f) & 0xFF) << 16) |
((int(color.w * 255.0f) & 0xFF) << 24);
GLfloat* vertexData = new GLfloat[details.vertices * FLOATS_PER_VERTEX];
GLfloat* vertex = vertexData;
int* colorData = new int[details.vertices];
int* colorDataAt = colorData;
for (int i = 0; i < points.size(); i++) {
glm::vec3 point = points[i];
glm::vec2 texCoord = texCoords[i];
*(vertex++) = point.x;
*(vertex++) = point.y;
*(vertex++) = point.z;
*(vertex++) = texCoord.x;
*(vertex++) = texCoord.y;
*(colorDataAt++) = compactColor;
}
details.verticesBuffer->append(sizeof(GLfloat) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData);
delete[] vertexData;
delete[] colorData;
#ifdef WANT_DEBUG
qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size();
#endif
}
void GeometryCache::renderVertices(gpu::Primitive primitiveType, int id) { void GeometryCache::renderVertices(gpu::Primitive primitiveType, int id) {
BatchItemDetails& details = _registeredVertices[id]; BatchItemDetails& details = _registeredVertices[id];
if (details.isCreated) { if (details.isCreated) {

View file

@ -198,6 +198,7 @@ public:
void updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color); void updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color);
void updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color); void updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color);
void updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& texCoords, const glm::vec4& color);
void renderVertices(gpu::Primitive primitiveType, int id); void renderVertices(gpu::Primitive primitiveType, int id);
/// Loads geometry from the specified URL. /// Loads geometry from the specified URL.