From e7a8255af2d17266a842ebdd4abff849231df411 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 May 2015 13:02:28 -0700 Subject: [PATCH 01/12] Replace zoneOverlayManager with internal drawZoneBoundaries --- examples/edit.js | 7 +- examples/libraries/zoneOverlayManager.js | 146 ----------------------- 2 files changed, 2 insertions(+), 151 deletions(-) delete mode 100644 examples/libraries/zoneOverlayManager.js diff --git a/examples/edit.js b/examples/edit.js index e6dd03de96..adc3374eb0 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -28,7 +28,6 @@ Script.include([ "libraries/gridTool.js", "libraries/entityList.js", "libraries/lightOverlayManager.js", - "libraries/zoneOverlayManager.js", ]); var selectionDisplay = SelectionDisplay; @@ -36,7 +35,6 @@ var selectionManager = SelectionManager; var entityPropertyDialogBox = EntityPropertyDialogBox; var lightOverlayManager = new LightOverlayManager(); -var zoneOverlayManager = new ZoneOverlayManager(); var cameraManager = new CameraManager(); @@ -49,7 +47,6 @@ var entityListTool = EntityListTool(); selectionManager.addEventListener(function() { selectionDisplay.updateHandles(); lightOverlayManager.updatePositions(); - zoneOverlayManager.updatePositions(); }); var windowDimensions = Controller.getViewportDimensions(); @@ -246,7 +243,7 @@ var toolBar = (function () { } toolBar.selectTool(activeButton, isActive); lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); - zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + Entities.drawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; // Sets visibility of tool buttons, excluding the power button @@ -1000,7 +997,7 @@ function handeMenuEvent(menuItem) { } else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) { lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); } else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) { - zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + Entities.drawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } tooltip.show(false); } diff --git a/examples/libraries/zoneOverlayManager.js b/examples/libraries/zoneOverlayManager.js deleted file mode 100644 index aac3af119b..0000000000 --- a/examples/libraries/zoneOverlayManager.js +++ /dev/null @@ -1,146 +0,0 @@ -ZoneOverlayManager = function(isEntityFunc, entityAddedFunc, entityRemovedFunc, entityMovedFunc) { - var self = this; - - var visible = false; - - // List of all created overlays - var allOverlays = []; - - // List of overlays not currently being used - var unusedOverlays = []; - - // Map from EntityItemID.id to overlay id - var entityOverlays = {}; - - // Map from EntityItemID.id to EntityItemID object - var entityIDs = {}; - - this.updatePositions = function(ids) { - for (var id in entityIDs) { - var entityID = entityIDs[id]; - var properties = Entities.getEntityProperties(entityID); - Overlays.editOverlay(entityOverlays[entityID.id].solid, { - position: properties.position, - rotation: properties.rotation, - dimensions: properties.dimensions, - }); - Overlays.editOverlay(entityOverlays[entityID.id].outline, { - position: properties.position, - rotation: properties.rotation, - dimensions: properties.dimensions, - }); - } - }; - - this.setVisible = function(isVisible) { - if (visible != isVisible) { - visible = isVisible; - for (var id in entityOverlays) { - Overlays.editOverlay(entityOverlays[id].solid, { visible: visible }); - Overlays.editOverlay(entityOverlays[id].outline, { visible: visible }); - } - } - }; - - // Allocate or get an unused overlay - function getOverlay() { - if (unusedOverlays.length == 0) { - var overlay = Overlays.addOverlay("cube", { - }); - allOverlays.push(overlay); - } else { - var overlay = unusedOverlays.pop(); - }; - return overlay; - } - - function releaseOverlay(overlay) { - unusedOverlays.push(overlay); - Overlays.editOverlay(overlay, { visible: false }); - } - - function addEntity(entityID) { - var properties = Entities.getEntityProperties(entityID); - if (properties.type == "Zone" && !(entityID.id in entityOverlays)) { - var overlaySolid = getOverlay(); - var overlayOutline = getOverlay(); - - entityOverlays[entityID.id] = { - solid: overlaySolid, - outline: overlayOutline, - } - entityIDs[entityID.id] = entityID; - - var color = { - red: Math.round(Math.random() * 255), - green: Math.round(Math.random() * 255), - blue: Math.round(Math.random() * 255) - }; - Overlays.editOverlay(overlaySolid, { - position: properties.position, - rotation: properties.rotation, - dimensions: properties.dimensions, - visible: visible, - solid: true, - alpha: 0.1, - color: color, - ignoreRayIntersection: true, - }); - Overlays.editOverlay(overlayOutline, { - position: properties.position, - rotation: properties.rotation, - dimensions: properties.dimensions, - visible: visible, - solid: false, - dashed: false, - lineWidth: 2.0, - alpha: 1.0, - color: color, - ignoreRayIntersection: true, - }); - } - } - - function deleteEntity(entityID) { - if (entityID.id in entityOverlays) { - releaseOverlay(entityOverlays[entityID.id].outline); - releaseOverlay(entityOverlays[entityID.id].solid); - delete entityIDs[entityID.id]; - delete entityOverlays[entityID.id]; - } - } - - function changeEntityID(oldEntityID, newEntityID) { - entityOverlays[newEntityID.id] = entityOverlays[oldEntityID.id]; - entityIDs[newEntityID.id] = newEntityID; - - delete entityOverlays[oldEntityID.id]; - delete entityIDs[oldEntityID.id]; - } - - function clearEntities() { - for (var id in entityOverlays) { - releaseOverlay(entityOverlays[id].outline); - releaseOverlay(entityOverlays[id].solid); - } - entityOverlays = {}; - entityIDs = {}; - } - - Entities.addingEntity.connect(addEntity); - Entities.changingEntityID.connect(changeEntityID); - Entities.deletingEntity.connect(deleteEntity); - Entities.clearingEntities.connect(clearEntities); - - // Add existing entities - var ids = Entities.findEntities(MyAvatar.position, 64000); - for (var i = 0; i < ids.length; i++) { - addEntity(ids[i]); - } - - Script.scriptEnding.connect(function() { - for (var i = 0; i < allOverlays.length; i++) { - Overlays.deleteOverlay(allOverlays[i]); - } - }); -}; From ede42285b1ba1c6d1d2045c0be42ab699e684ab9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 9 May 2015 07:57:29 -0700 Subject: [PATCH 02/12] Improvements to particle entity. * Changed particle geometry to billboarded quads * Added texture support * Added ajt-test.js particle test script. * GeometryCache support for batched quads with texCoords. * Bug fix for infinite loop if _lifetime was very large. * Don't reset the simulation on animation loop. * stop emitting particles on animation stop, but keep simulating until there are no more living particles. * Removed some trailing whitespace --- examples/ajt-test.js | 83 ++++++++++ .../RenderableParticleEffectEntityItem.cpp | 148 ++++++++++++++---- .../src/RenderableParticleEffectEntityItem.h | 9 +- .../entities/src/ParticleEffectEntityItem.cpp | 86 +++++----- .../entities/src/ParticleEffectEntityItem.h | 39 ++--- libraries/render-utils/src/GeometryCache.cpp | 70 +++++++++ libraries/render-utils/src/GeometryCache.h | 1 + 7 files changed, 341 insertions(+), 95 deletions(-) create mode 100644 examples/ajt-test.js diff --git a/examples/ajt-test.js b/examples/ajt-test.js new file mode 100644 index 0000000000..a908978a02 --- /dev/null +++ b/examples/ajt-test.js @@ -0,0 +1,83 @@ + +(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() { + var animationSettings = JSON.stringify({ fps: 30, + frameIndex: 0, + running: true, + firstFrame: 0, + lastFrame: 30, + loop: false }); + + this.entity = Entities.addEntity({ type: "ParticleEffect", + animationSettings: animationSettings, + position: spawnPoint, + textures: "http://www.hyperlogic.org/images/particle.png", + emitRate: 30, + emitStrength: 3, + color: { red: 128, green: 255, blue: 255 }, + visible: true, + locked: false }); + + var self = this; + this.timer = Script.setInterval(function () { + print("AJT: setting animation props"); + var animProp = { animationFrameIndex: 0, + animationIsPlaying: true }; + Entities.editEntity(self.entity, animProp); + }, 2000); + + } + + 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()); + } + + function ShutDown() { + var i, len = entities.length; + for (i = 0; i < len; i++) { + objs[i].Destroy(); + } + objs = []; + } + + Init(); + Script.scriptEnding.connect(ShutDown); +})(); + + diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index b4dd5b4c64..32fd06c837 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -16,6 +16,7 @@ #include #include #include +#include "EntitiesRendererLogging.h" #include "RenderableParticleEffectEntityItem.h" @@ -29,62 +30,155 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent } void RenderableParticleEffectEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render"); + assert(getType() == EntityTypes::ParticleEffect); + PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render"); + + if (_texturesChangedFlag) + { + if (_textures.isEmpty()) { + _texture.clear(); + } else { + // for now use the textures string directly. + // Eventually we'll want multiple textures in a map or array. + _texture = DependencyManager::get()->getTexture(_textures); + } + _texturesChangedFlag = false; + } + + if (!_texture) { + renderUntexturedQuads(args); + } else if (_texture && !_texture->isLoaded()) { + renderUntexturedQuads(args); + } else { + renderTexturedQuads(args); + } +}; + +void RenderableParticleEffectEntityItem::renderUntexturedQuads(RenderArgs* args) { + float pa_rad = getParticleRadius(); const float MAX_COLOR = 255.0f; glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); - // Right now we're just iterating over particles and rendering as a cross of four quads. - // This is pretty dumb, it was quick enough to code up. Really, there should be many - // rendering modes, including the all-important textured billboards. + glm::vec3 up_offset = args->_viewFrustum->getUp() * pa_rad; + glm::vec3 right_offset = args->_viewFrustum->getRight() * pa_rad; QVector* pointVec = new QVector(_paCount * VERTS_PER_PARTICLE); + quint32 paCount = 0; quint32 paIter = _paHead; - while (_paLife[paIter] > 0.0f) { + while (_paLife[paIter] > 0.0f && paCount < _maxParticles) { 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])); + glm::vec3 pos(_paPosition[j], _paPosition[j + 1], _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)); + pointVec->append(pos - right_offset + up_offset); + pointVec->append(pos + right_offset + up_offset); + pointVec->append(pos + right_offset - up_offset); + pointVec->append(pos - right_offset - up_offset); paIter = (paIter + 1) % _maxParticles; + paCount++; } DependencyManager::get()->updateVertices(_cacheID, *pointVec, paColor); - + 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()->renderVertices(gpu::QUADS, _cacheID); glPopMatrix(); glPopMatrix(); delete pointVec; -}; +} + +static glm::vec3 s_dir; +static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) { + return glm::dot(rhs, s_dir) > glm::dot(lhs, s_dir); +} + +void RenderableParticleEffectEntityItem::renderTexturedQuads(RenderArgs* args) { + + float pa_rad = getParticleRadius(); + + const float MAX_COLOR = 255.0f; + glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, + getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); + + QVector* posVec = new QVector(_paCount); + quint32 paCount = 0; + quint32 paIter = _paHead; + while (_paLife[paIter] > 0.0f && paCount < _maxParticles) { + int j = paIter * XYZ_STRIDE; + posVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1], _paPosition[j + 2])); + + paIter = (paIter + 1) % _maxParticles; + paCount++; + } + + // sort particles back to front + qSort(posVec->begin(), posVec->end(), zSort); + + QVector* vertVec = new QVector(_paCount * VERTS_PER_PARTICLE); + QVector* texCoordVec = new QVector(_paCount * VERTS_PER_PARTICLE); + + s_dir = args->_viewFrustum->getDirection(); + glm::vec3 up_offset = args->_viewFrustum->getUp() * pa_rad; + glm::vec3 right_offset = args->_viewFrustum->getRight() * pa_rad; + + for (int i = 0; i < posVec->size(); i++) { + glm::vec3 pos = posVec->at(i); + + vertVec->append(pos - right_offset + up_offset); + vertVec->append(pos + right_offset + up_offset); + vertVec->append(pos + right_offset - up_offset); + vertVec->append(pos - right_offset - up_offset); + + texCoordVec->append(glm::vec2(0, 1)); + texCoordVec->append(glm::vec2(1, 1)); + texCoordVec->append(glm::vec2(1, 0)); + texCoordVec->append(glm::vec2(0, 0)); + } + + DependencyManager::get()->updateVertices(_cacheID, *vertVec, *texCoordVec, paColor); + + 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()->renderVertices(gpu::QUADS, _cacheID); + + glPopMatrix(); + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + delete posVec; + delete vertVec; + delete texCoordVec; +} + diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 74b29574c3..25bbe5c147 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -12,6 +12,7 @@ #define hifi_RenderableParticleEffectEntityItem_h #include +#include class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { public: @@ -19,9 +20,15 @@ public: RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); virtual void render(RenderArgs* args); + void renderUntexturedQuads(RenderArgs* args); + void renderTexturedQuads(RenderArgs* args); + protected: + int _cacheID; - const int VERTS_PER_PARTICLE = 16; + const int VERTS_PER_PARTICLE = 4; + + NetworkTexturePointer _texture; }; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 45312f465b..fc4927fdcd 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -18,12 +18,8 @@ // - 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. // @@ -55,7 +51,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_LOCAL_GRAVITY = -9.8f; const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f; - +const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return new ParticleEffectEntityItem(entityID, properties); @@ -72,6 +68,7 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _emitStrength = DEFAULT_EMIT_STRENGTH; _localGravity = DEFAULT_LOCAL_GRAVITY; _particleRadius = DEFAULT_PARTICLE_RADIUS; + _textures = DEFAULT_TEXTURES; 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 @@ -96,7 +93,7 @@ ParticleEffectEntityItem::~ParticleEffectEntityItem() { EntityItemProperties ParticleEffectEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex); @@ -111,6 +108,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitStrength, getEmitStrength); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localGravity, getLocalGravity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); return properties; } @@ -132,6 +130,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitStrength, setEmitStrength); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localGravity, setLocalGravity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); if (somethingChanged) { bool wantDebug = false; @@ -186,6 +185,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, _emitStrength); READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, _localGravity); READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, _particleRadius); + READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, _textures); return bytesRead; } @@ -194,7 +194,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch // TODO: eventually only include properties changed since the params.lastViewFrustumSent time EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - + requestedProperties += PROP_COLOR; requestedProperties += PROP_ANIMATION_FPS; requestedProperties += PROP_ANIMATION_FRAME_INDEX; @@ -208,6 +208,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_EMIT_STRENGTH; requestedProperties += PROP_LOCAL_GRAVITY; requestedProperties += PROP_PARTICLE_RADIUS; + requestedProperties += PROP_TEXTURES; return requestedProperties; } @@ -234,11 +235,12 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, getEmitStrength()); APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, getLocalGravity()); APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, getParticleRadius()); + APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, getTextures()); } bool ParticleEffectEntityItem::isAnimatingSomething() const { - return getAnimationIsPlaying() && - getAnimationFPS() != 0.0f; + // keep animating if there are particles still alive. + return (getAnimationIsPlaying() || _paCount > 0) && getAnimationFPS() != 0.0f; } bool ParticleEffectEntityItem::needsToCallUpdate() const { @@ -246,33 +248,25 @@ bool ParticleEffectEntityItem::needsToCallUpdate() const { } 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 if (getAnimationIsPlaying()) { - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - float lastFrame = _animationLoop.getFrameIndex(); _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 - glm::vec3 dims; - dims.x = glm::max(glm::abs(_paXmin), glm::abs(_paXmax)) * 2.0f; - dims.y = glm::max(glm::abs(_paYmin), glm::abs(_paYmax)) * 2.0f; - dims.z = glm::max(glm::abs(_paZmin), glm::abs(_paZmax)) * 2.0f; - setDimensions(dims); + if (isAnimatingSomething()) { + stepSimulation(deltaTime); + + // update the dimensions + glm::vec3 dims; + dims.x = glm::max(glm::abs(_paXmin), glm::abs(_paXmax)) * 2.0f; + dims.y = glm::max(glm::abs(_paYmin), glm::abs(_paYmax)) * 2.0f; + dims.z = glm::max(glm::abs(_paZmin), glm::abs(_paZmax)) * 2.0f; + setDimensions(dims); + } EntityItem::update(now); // let our base class handle it's updates... } @@ -422,9 +416,12 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { _paXmin = _paYmin = _paZmin = -1.0f; _paXmax = _paYmax = _paZmax = 1.0f; + float oneHalfATSquared = 0.5f * _localGravity * deltaTime * deltaTime; + // update particles + quint32 updateCount = 0; quint32 updateIter = _paHead; - while (_paLife[updateIter] > 0.0f) { + while (_paLife[updateIter] > 0.0f && updateCount < _maxParticles) { _paLife[updateIter] -= deltaTime; if (_paLife[updateIter] <= 0.0f) { _paLife[updateIter] = -1.0f; @@ -432,10 +429,10 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { _paCount--; } else { - // DUMB FORWARD EULER just to get it done int j = updateIter * XYZ_STRIDE; + _paPosition[j] += _paVelocity[j] * deltaTime; - _paPosition[j+1] += _paVelocity[j+1] * deltaTime; + _paPosition[j+1] += _paVelocity[j+1] * deltaTime + oneHalfATSquared; _paPosition[j+2] += _paVelocity[j+2] * deltaTime; _paXmin = glm::min(_paXmin, _paPosition[j]); @@ -449,29 +446,37 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { _paVelocity[j + 1] += deltaTime * _localGravity; } updateIter = (updateIter + 1) % _maxParticles; + updateCount++; } - // emit new particles quint32 emitIdx = updateIter; - _partialEmit += ((float)_emitRate) * deltaTime; - quint32 birthed = (quint32)_partialEmit; - _partialEmit -= (float)birthed; + quint32 birthed = 0; glm::vec3 randOffset; + // emit new particles, but only if animaiton is playing + if (getAnimationIsPlaying()) { + _partialEmit += ((float)_emitRate) * deltaTime; + birthed = (quint32)_partialEmit; + _partialEmit -= (float)birthed; + } else { + _partialEmit = 0; + } + for (quint32 i = 0; i < birthed; i++) { if (_paLife[emitIdx] < 0.0f) { 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 _paPosition[j] += _paVelocity[j] * deltaTime; - _paPosition[j + 1] += _paVelocity[j + 1] * deltaTime; + _paPosition[j + 1] += _paVelocity[j + 1] * deltaTime + oneHalfATSquared; _paPosition[j + 2] += _paVelocity[j + 2] * deltaTime; _paXmin = glm::min(_paXmin, _paPosition[j]); @@ -510,4 +515,3 @@ void ParticleEffectEntityItem::resetSimulation() { srand(_randSeed); } - diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index b00eb94685..3533ce84e7 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -2,29 +2,6 @@ // ParticleEffectEntityItem.h // 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. // // Distributed under the Apache License, Version 2.0. @@ -39,14 +16,14 @@ class ParticleEffectEntityItem : public EntityItem { public: - + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); virtual ~ParticleEffectEntityItem(); ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of this entity virtual EntityItemProperties getProperties() const; virtual bool setProperties(const EntityItemProperties& properties); @@ -141,6 +118,15 @@ public: float getAnimationFPS() const { return _animationLoop.getFPS(); } 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: bool isAnimatingSomething() const; @@ -159,6 +145,8 @@ protected: quint64 _lastAnimated; AnimationLoop _animationLoop; QString _animationSettings; + QString _textures; + bool _texturesChangedFlag; ShapeType _shapeType = SHAPE_TYPE_NONE; // all the internals of running the particle sim @@ -180,4 +168,3 @@ protected: }; #endif // hifi_ParticleEffectEntityItem_h - diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 61d92ccc17..770717edbe 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -721,6 +721,76 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif } +void GeometryCache::updateVertices(int id, const QVector& points, const QVector& 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) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 8ccbb65dbb..672e4ddb78 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -198,6 +198,7 @@ public: void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const glm::vec4& color); + void updateVertices(int id, const QVector& points, const QVector& texCoords, const glm::vec4& color); void renderVertices(gpu::Primitive primitiveType, int id); /// Loads geometry from the specified URL. From 28bacb4d82672666677cc8cce76c5722ace0d150 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 11 May 2015 13:26:52 -0700 Subject: [PATCH 03/12] add center in zone button to edit.js --- examples/edit.js | 19 +++++++++++++++++++ examples/html/entityProperties.html | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/examples/edit.js b/examples/edit.js index 64050d92aa..9c7db22eca 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1295,6 +1295,25 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } + } else if (data.action == "centerAtmosphereToZone") { + if (selectionManager.hasSelection()) { + selectionManager.saveProperties(); + for (var i = 0; i < selectionManager.selections.length; i++) { + var properties = selectionManager.savedProperties[selectionManager.selections[i].id]; + if (properties.type == "Zone") { + var centerOfZone = properties.boundingBox.center; + var atmosphereCenter = { x: centerOfZone.x, + y: centerOfZone.y - properties.atmosphere.innerRadius, + z: centerOfZone.z }; + + Entities.editEntity(selectionManager.selections[i], { + atmosphere: { center: atmosphereCenter }, + }); + } + } + pushCommandForSelections(); + selectionManager._update(); + } } } }); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 56fc84ef96..4247a5e6cb 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -300,6 +300,8 @@ var elZoneAtmosphereCenterX = document.getElementById("property-zone-atmosphere-center-x"); var elZoneAtmosphereCenterY = document.getElementById("property-zone-atmosphere-center-y"); var elZoneAtmosphereCenterZ = document.getElementById("property-zone-atmosphere-center-z"); + var elCenterAtmosphereToZone = document.getElementById("center-atmosphere-in-zone"); + var elZoneAtmosphereInnerRadius = document.getElementById("property-zone-atmosphere-inner-radius"); var elZoneAtmosphereOuterRadius = document.getElementById("property-zone-atmosphere-outer-radius"); var elZoneAtmosphereMieScattering = document.getElementById("property-zone-atmosphere-mie-scattering"); @@ -705,6 +707,12 @@ percentage: parseInt(elRescaleDimensionsPct.value), })); }); + elCenterAtmosphereToZone.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "centerAtmosphereToZone", + })); + }); window.onblur = function() { // Fake a change event @@ -1156,6 +1164,9 @@
X
Y
Z
+
+ +
From e58976fe5a1c4c311643c9d7c3af72dd4a935750 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 11 May 2015 13:52:36 -0700 Subject: [PATCH 04/12] Fix magnifier location --- interface/src/ui/ApplicationOverlay.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 6d9701ded7..b65e75923e 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -1226,19 +1226,17 @@ glm::vec2 ApplicationOverlay::sphericalToOverlay(const glm::vec2& sphericalPos) result /= _textureFov; result.x /= _textureAspectRatio; result += 0.5f; - result.x = (-sphericalPos.x / (_textureFov * _textureAspectRatio) + 0.5f); - result.y = (sphericalPos.y / _textureFov + 0.5f); result *= qApp->getCanvasSize(); return result; } glm::vec2 ApplicationOverlay::overlayToSpherical(const glm::vec2& overlayPos) const { glm::vec2 result = overlayPos; - result.x *= -1.0; result /= qApp->getCanvasSize(); result -= 0.5f; result *= _textureFov; result.x *= _textureAspectRatio; + result.x *= -1.0f; return result; } From 73428ec12d15bb2af8182d620338471cf3de6807 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 11 May 2015 14:21:58 -0700 Subject: [PATCH 05/12] add support to not displace very large clipboards on import --- examples/edit.js | 7 +++++-- .../scripting/ClipboardScriptingInterface.cpp | 4 ++++ .../scripting/ClipboardScriptingInterface.h | 1 + libraries/entities/src/EntityTree.cpp | 21 +++++++++++++++++++ libraries/entities/src/EntityTree.h | 2 ++ libraries/entities/src/EntityTreeElement.cpp | 10 +++++++++ libraries/entities/src/EntityTreeElement.h | 2 ++ libraries/shared/src/Extents.cpp | 6 ++++++ libraries/shared/src/Extents.h | 6 ++++++ 9 files changed, 57 insertions(+), 2 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 9c7db22eca..1bc4a34393 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1030,8 +1030,11 @@ function importSVO(importURL) { var success = Clipboard.importEntities(importURL); if (success) { - var position = getPositionToCreateEntity(); - + var VERY_LARGE = 10000; + var position = { x: 0, y: 0, z: 0}; + if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { + position = getPositionToCreateEntity(); + } var pastedEntityIDs = Clipboard.pasteEntities(position); if (isActive) { diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index 616fc5f141..f833ad43cc 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -14,6 +14,10 @@ ClipboardScriptingInterface::ClipboardScriptingInterface() { } +float ClipboardScriptingInterface::getClipboardContentsLargestDimension() { + return Application::getInstance()->getEntityClipboard()->getContentsLargestDimension(); +} + bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector& entityIDs) { return Application::getInstance()->exportEntities(filename, entityIDs); } diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 92204af60d..73bebd4836 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -22,6 +22,7 @@ signals: void readyToImport(); public slots: + float getClipboardContentsLargestDimension(); /// returns the largest dimension of everything on the clipboard bool importEntities(const QString& filename); bool exportEntities(const QString& filename, const QVector& entityIDs); bool exportEntities(const QString& filename, float x, float y, float z, float s); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1b59d3bd8e..ef49aca87a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1068,6 +1068,27 @@ void EntityTree::debugDumpMap() { qCDebug(entities) << "-----------------------------------------------------"; } +class ContentsDimensionOperator : public RecurseOctreeOperator { +public: + virtual bool preRecursion(OctreeElement* element); + virtual bool postRecursion(OctreeElement* element) { return true; } + float getLargestDimension() const { return _contentExtents.largestDimension(); } +private: + Extents _contentExtents; +}; + +bool ContentsDimensionOperator::preRecursion(OctreeElement* element) { + EntityTreeElement* entityTreeElement = static_cast(element); + entityTreeElement->expandExtentsToContents(_contentExtents); + return true; +} + +float EntityTree::getContentsLargestDimension() { + ContentsDimensionOperator theOperator; + recurseTreeWithOperator(&theOperator); + return theOperator.getLargestDimension(); +} + class DebugOperator : public RecurseOctreeOperator { public: virtual bool preRecursion(OctreeElement* element); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index f99160f4ed..76d648cf46 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -166,6 +166,8 @@ public: bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues); bool readFromMap(QVariantMap& entityDescription); + + float getContentsLargestDimension(); signals: void deletingEntity(const EntityItemID& entityID); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 349bd51372..3acc5fbcef 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -819,6 +819,16 @@ bool EntityTreeElement::pruneChildren() { return somethingPruned; } +void EntityTreeElement::expandExtentsToContents(Extents& extents) { + if (_entityItems->size()) { + for (uint16_t i = 0; i < _entityItems->size(); i++) { + EntityItem* entity = (*_entityItems)[i]; + extents.add(entity->getAABox()); + } + } +} + + void EntityTreeElement::debugDump() { qCDebug(entities) << "EntityTreeElement..."; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 0b28dd30d0..658dfeab92 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -196,6 +196,8 @@ public: bool pruneChildren(); + void expandExtentsToContents(Extents& extents); + protected: virtual void init(unsigned char * octalCode); EntityTree* _myTree; diff --git a/libraries/shared/src/Extents.cpp b/libraries/shared/src/Extents.cpp index 02e09bfa3c..f48ba3c99f 100644 --- a/libraries/shared/src/Extents.cpp +++ b/libraries/shared/src/Extents.cpp @@ -14,6 +14,7 @@ #include #include +#include "AABox.h" #include "Extents.h" void Extents::reset() { @@ -37,6 +38,11 @@ void Extents::addPoint(const glm::vec3& point) { maximum = glm::max(maximum, point); } +void Extents::add(const AABox& box) { + minimum = glm::min(minimum, box.getMinimumPoint()); + maximum = glm::max(maximum, box.getMaximumPoint()); +} + void Extents::rotate(const glm::quat& rotation) { glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z); glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z); diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index 024d72013a..489d48b1c1 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -19,6 +19,8 @@ #include #include "StreamUtils.h" +class AABox; + class Extents { public: /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively @@ -28,6 +30,10 @@ public: /// expand current limits to contain other extents void addExtents(const Extents& extents); + /// \param aabox another intance of extents + /// expand current limits to contain other aabox + void add(const AABox& box); + /// \param point new point to compare against existing limits /// compare point to current limits and expand them if necessary to contain point void addPoint(const glm::vec3& point); From 24739d24e168e711581fe980affb308df4e5491e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 May 2015 15:23:39 -0700 Subject: [PATCH 06/12] Fix calls to set draw zone boundaries --- examples/edit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 208abf1e20..1545358448 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -243,7 +243,7 @@ var toolBar = (function () { } toolBar.selectTool(activeButton, isActive); lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); - Entities.drawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; // Sets visibility of tool buttons, excluding the power button @@ -997,7 +997,7 @@ function handeMenuEvent(menuItem) { } else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) { lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); } else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) { - Entities.drawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } tooltip.show(false); } From a2a1bf4e8cab30984cf10716f02407b3242720f5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 11 May 2015 16:32:27 -0700 Subject: [PATCH 07/12] make stars fade in as sun drops below horizon --- interface/src/Application.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 088b2ddf96..dc38f64f58 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3220,18 +3220,40 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs // compute starfield alpha based on distance from atmosphere float alpha = 1.0f; bool hasStars = true; + if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { // TODO: handle this correctly for zones const EnvironmentData& closestData = _environment.getClosestData(theCamera.getPosition()); - + if (closestData.getHasStars()) { + const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; + const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; + + glm::vec3 sunDirection = (getAvatarPosition() - closestData.getSunLocation()) + / closestData.getAtmosphereOuterRadius(); float height = glm::distance(theCamera.getPosition(), closestData.getAtmosphereCenter()); if (height < closestData.getAtmosphereInnerRadius()) { + // If we're inside the atmosphere, then determine if our keyLight is below the horizon alpha = 0.0f; + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + + } else if (height < closestData.getAtmosphereOuterRadius()) { alpha = (height - closestData.getAtmosphereInnerRadius()) / (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } } } else { hasStars = false; From b3af5152240c20583f93855906b0beac66d50c35 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 11 May 2015 19:21:33 -0700 Subject: [PATCH 08/12] Particle entity improvements based on code review. * Updated variable naming to match coding standards. * Changed particle raw float arrays to vectors. * Bug fix: changing maxParticles will no longer lead to memory corruption. * Made particle ring buffer more explicit, added _particleTailIndex. * Added getLivingParticleCount() private method. * Moved integration and bounds code into private methods. * Bug fix: high emit rates now properly integrate particles forward for the remaing frame time, not the entire frame. * Bug fix: new particles were not initiaized to origin. * Added more particles to ajt-test.js. * Bug fix: ajt-test.js script was not shutting down properly. * Removed random seed, unless we have a psudo random number generator per particle system instance, it's unlikely that this will preserve sync. * Bumped packet version number. --- examples/ajt-test.js | 34 ++- .../RenderableParticleEffectEntityItem.cpp | 143 +++++------ .../entities/src/ParticleEffectEntityItem.cpp | 232 +++++++++--------- .../entities/src/ParticleEffectEntityItem.h | 51 ++-- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 1 + 6 files changed, 238 insertions(+), 225 deletions(-) diff --git a/examples/ajt-test.js b/examples/ajt-test.js index a908978a02..c26458a9af 100644 --- a/examples/ajt-test.js +++ b/examples/ajt-test.js @@ -28,32 +28,34 @@ } // constructor - function TestFx() { + function TestFx(color, emitDirection, emitRate, emitStrength, blinkRate) { var animationSettings = JSON.stringify({ fps: 30, frameIndex: 0, running: true, firstFrame: 0, lastFrame: 30, - loop: false }); + loop: true }); this.entity = Entities.addEntity({ type: "ParticleEffect", animationSettings: animationSettings, position: spawnPoint, textures: "http://www.hyperlogic.org/images/particle.png", - emitRate: 30, - emitStrength: 3, - color: { red: 128, green: 255, blue: 255 }, + emitRate: emitRate, + emitStrength: emitStrength, + emitDirection: emitDirection, + color: color, visible: true, locked: false }); + this.isPlaying = true; + var self = this; this.timer = Script.setInterval(function () { - print("AJT: setting animation props"); - var animProp = { animationFrameIndex: 0, - animationIsPlaying: true }; + // flip is playing state + self.isPlaying = !self.isPlaying; + var animProp = { animationIsPlaying: self.isPlaying }; Entities.editEntity(self.entity, animProp); - }, 2000); - + }, (1 / blinkRate) * 1000); } TestFx.prototype.Destroy = function () { @@ -65,11 +67,19 @@ var objs = []; function Init() { objs.push(new TestBox()); - objs.push(new TestFx()); + 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 = entities.length; + var i, len = objs.length; for (i = 0; i < len; i++) { objs[i].Destroy(); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 32fd06c837..009d26481a 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -34,8 +34,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { assert(getType() == EntityTypes::ParticleEffect); PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render"); - if (_texturesChangedFlag) - { + if (_texturesChangedFlag) { if (_textures.isEmpty()) { _texture.clear(); } else { @@ -57,35 +56,38 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { void RenderableParticleEffectEntityItem::renderUntexturedQuads(RenderArgs* args) { - float pa_rad = getParticleRadius(); + float particleRadius = getParticleRadius(); const float MAX_COLOR = 255.0f; - glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, - getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); + glm::vec4 particleColor(getColor()[RED_INDEX] / MAX_COLOR, + getColor()[GREEN_INDEX] / MAX_COLOR, + getColor()[BLUE_INDEX] / MAX_COLOR, + getLocalRenderAlpha()); - glm::vec3 up_offset = args->_viewFrustum->getUp() * pa_rad; - glm::vec3 right_offset = args->_viewFrustum->getRight() * pa_rad; + glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius; + glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius; - QVector* pointVec = new QVector(_paCount * VERTS_PER_PARTICLE); - quint32 paCount = 0; - quint32 paIter = _paHead; - while (_paLife[paIter] > 0.0f && paCount < _maxParticles) { - int j = paIter * XYZ_STRIDE; + QVector vertices(getLivingParticleCount() * VERTS_PER_PARTICLE); + quint32 count = 0; + for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { + glm::vec3 pos = _particlePositions[i]; - glm::vec3 pos(_paPosition[j], _paPosition[j + 1], _paPosition[j + 2]); - - pointVec->append(pos - right_offset + up_offset); - pointVec->append(pos + right_offset + up_offset); - pointVec->append(pos + right_offset - up_offset); - pointVec->append(pos - right_offset - up_offset); - - paIter = (paIter + 1) % _maxParticles; - paCount++; + // 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++; } - DependencyManager::get()->updateVertices(_cacheID, *pointVec, paColor); + // 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()->updateVertices(_cacheID, vertices, particleColor); glPushMatrix(); + glm::vec3 position = getPosition(); glTranslatef(position.x, position.y, position.z); glm::quat rotation = getRotation(); @@ -93,92 +95,93 @@ void RenderableParticleEffectEntityItem::renderUntexturedQuads(RenderArgs* args) 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()->renderVertices(gpu::QUADS, _cacheID); - glPopMatrix(); - glPopMatrix(); - delete pointVec; + glPopMatrix(); + + glPopMatrix(); } -static glm::vec3 s_dir; +static glm::vec3 zSortAxis; static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) { - return glm::dot(rhs, s_dir) > glm::dot(lhs, s_dir); + return glm::dot(rhs, ::zSortAxis) > glm::dot(lhs, ::zSortAxis); } void RenderableParticleEffectEntityItem::renderTexturedQuads(RenderArgs* args) { - float pa_rad = getParticleRadius(); + float particleRadius = getParticleRadius(); const float MAX_COLOR = 255.0f; - glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, - getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); + glm::vec4 particleColor(getColor()[RED_INDEX] / MAX_COLOR, + getColor()[GREEN_INDEX] / MAX_COLOR, + getColor()[BLUE_INDEX] / MAX_COLOR, + getLocalRenderAlpha()); - QVector* posVec = new QVector(_paCount); - quint32 paCount = 0; - quint32 paIter = _paHead; - while (_paLife[paIter] > 0.0f && paCount < _maxParticles) { - int j = paIter * XYZ_STRIDE; - posVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1], _paPosition[j + 2])); - - paIter = (paIter + 1) % _maxParticles; - paCount++; + QVector 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 - qSort(posVec->begin(), posVec->end(), zSort); + ::zSortAxis = args->_viewFrustum->getDirection(); + qSort(positions.begin(), positions.end(), zSort); - QVector* vertVec = new QVector(_paCount * VERTS_PER_PARTICLE); - QVector* texCoordVec = new QVector(_paCount * VERTS_PER_PARTICLE); + QVector vertices(getLivingParticleCount() * VERTS_PER_PARTICLE); + QVector textureCoords(getLivingParticleCount() * VERTS_PER_PARTICLE); - s_dir = args->_viewFrustum->getDirection(); - glm::vec3 up_offset = args->_viewFrustum->getUp() * pa_rad; - glm::vec3 right_offset = args->_viewFrustum->getRight() * pa_rad; + glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius; + glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius; - for (int i = 0; i < posVec->size(); i++) { - glm::vec3 pos = posVec->at(i); + for (int i = 0; i < positions.size(); i++) { + glm::vec3 pos = positions[i]; - vertVec->append(pos - right_offset + up_offset); - vertVec->append(pos + right_offset + up_offset); - vertVec->append(pos + right_offset - up_offset); - vertVec->append(pos - right_offset - up_offset); + // 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); - texCoordVec->append(glm::vec2(0, 1)); - texCoordVec->append(glm::vec2(1, 1)); - texCoordVec->append(glm::vec2(1, 0)); - texCoordVec->append(glm::vec2(0, 0)); + textureCoords.append(glm::vec2(0, 1)); + textureCoords.append(glm::vec2(1, 1)); + textureCoords.append(glm::vec2(1, 0)); + textureCoords.append(glm::vec2(0, 0)); } - DependencyManager::get()->updateVertices(_cacheID, *vertVec, *texCoordVec, paColor); + // update geometry cache with all the verts in model coordinates. + DependencyManager::get()->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); + 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(); + glPushMatrix(); - glm::vec3 positionToCenter = getCenter() - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + glm::vec3 positionToCenter = getCenter() - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - DependencyManager::get()->renderVertices(gpu::QUADS, _cacheID); + DependencyManager::get()->renderVertices(gpu::QUADS, _cacheID); + + glPopMatrix(); - glPopMatrix(); glPopMatrix(); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); - - delete posVec; - delete vertVec; - delete texCoordVec; } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index fc4927fdcd..d5492cb485 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -41,6 +41,7 @@ #include "EntitiesLogging.h" #include "ParticleEffectEntityItem.h" +const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 }; const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f; const bool ParticleEffectEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false; const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f; @@ -53,42 +54,42 @@ const float ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY = -9.8f; const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; + EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return new ParticleEffectEntityItem(entityID, properties); } // our non-pure virtual subclass for now... 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; - _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; - _textures = DEFAULT_TEXTURES; + setColor(DEFAULT_COLOR); 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() { - delete [] _paLife; - delete [] _paPosition; - delete [] _paVelocity; } EntityItemProperties ParticleEffectEntityItem::getProperties() const { @@ -146,8 +147,8 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert } int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { int bytesRead = 0; const unsigned char* dataAt = data; @@ -214,12 +215,12 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea } void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); @@ -240,7 +241,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, bool ParticleEffectEntityItem::isAnimatingSomething() const { // keep animating if there are particles still alive. - return (getAnimationIsPlaying() || _paCount > 0) && getAnimationFPS() != 0.0f; + return (getAnimationIsPlaying() || getLivingParticleCount() > 0) && getAnimationFPS() != 0.0f; } bool ParticleEffectEntityItem::needsToCallUpdate() const { @@ -262,9 +263,9 @@ void ParticleEffectEntityItem::update(const quint64& now) { // update the dimensions glm::vec3 dims; - dims.x = glm::max(glm::abs(_paXmin), glm::abs(_paXmax)) * 2.0f; - dims.y = glm::max(glm::abs(_paYmin), glm::abs(_paYmax)) * 2.0f; - dims.z = glm::max(glm::abs(_paZmin), glm::abs(_paZmax)) * 2.0f; + 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); } @@ -412,106 +413,103 @@ QString ParticleEffectEntityItem::getAnimationSettings() const { return jsonByteString; } +void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) { + _particleMinBound.x = glm::min(_particleMinBound.x, point.x); + _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); +} + +void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) { + glm::vec3 atSquared(0.0f, 0.5f * _localGravity * deltaTime * deltaTime, 0.0f); + glm::vec3 at(0.0f, _localGravity * deltaTime, 0.0f); + _particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared; + _particleVelocities[index] += at; +} + void ParticleEffectEntityItem::stepSimulation(float deltaTime) { - _paXmin = _paYmin = _paZmin = -1.0f; - _paXmax = _paYmax = _paZmax = 1.0f; - float oneHalfATSquared = 0.5f * _localGravity * deltaTime * deltaTime; + _particleMinBound = glm::vec3(-1.0f, -1.0f, -1.0f); + _particleMaxBound = glm::vec3(1.0f, 1.0f, 1.0f); - // update particles - quint32 updateCount = 0; - quint32 updateIter = _paHead; - while (_paLife[updateIter] > 0.0f && updateCount < _maxParticles) { - _paLife[updateIter] -= deltaTime; - if (_paLife[updateIter] <= 0.0f) { - _paLife[updateIter] = -1.0f; - _paHead = (_paHead + 1) % _maxParticles; - _paCount--; + // 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 { - int j = updateIter * XYZ_STRIDE; - - _paPosition[j] += _paVelocity[j] * deltaTime; - _paPosition[j+1] += _paVelocity[j+1] * deltaTime + oneHalfATSquared; - _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; + integrateParticle(i, deltaTime); + extendBounds(_particlePositions[i]); } - updateIter = (updateIter + 1) % _maxParticles; - updateCount++; } - quint32 emitIdx = updateIter; - quint32 birthed = 0; - glm::vec3 randOffset; - // emit new particles, but only if animaiton is playing if (getAnimationIsPlaying()) { - _partialEmit += ((float)_emitRate) * deltaTime; - birthed = (quint32)_partialEmit; - _partialEmit -= (float)birthed; - } else { - _partialEmit = 0; - } - for (quint32 i = 0; i < birthed; i++) { - if (_paLife[emitIdx] < 0.0f) { - int j = emitIdx * XYZ_STRIDE; - _paLife[emitIdx] = _lifespan; + float timeLeftInFrame = deltaTime; + while (_timeUntilNextEmit < timeLeftInFrame) { - 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; + timeLeftInFrame -= _timeUntilNextEmit; + _timeUntilNextEmit = 1.0f / _emitRate; - _paVelocity[j] = (_emitDirection.x * _emitStrength) + randOffset.x; - _paVelocity[j + 1] = (_emitDirection.y * _emitStrength) + randOffset.y; - _paVelocity[j + 2] = (_emitDirection.z * _emitStrength) + randOffset.z; + // emit a new particle at tail index. + quint32 i = _particleTailIndex; + _particleLifetimes[i] = _lifespan; - _paPosition[j] += _paVelocity[j] * deltaTime; - _paPosition[j + 1] += _paVelocity[j + 1] * deltaTime + oneHalfATSquared; - _paPosition[j + 2] += _paVelocity[j + 2] * deltaTime; + // jitter the _emitDirection by a random offset + glm::vec3 randOffset; + randOffset.x = (randFloat() - 0.5f) * 0.25f * _emitStrength; + randOffset.y = (randFloat() - 0.5f) * 0.25f * _emitStrength; + randOffset.z = (randFloat() - 0.5f) * 0.25f * _emitStrength; - _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]); + // set initial conditions + _particlePositions[i] = glm::vec3(0.0f, 0.0f, 0.0f); + _particleVelocities[i] = _emitDirection * _emitStrength + randOffset; - // massless particles - // and simple gravity down - _paVelocity[j + 1] += deltaTime * _localGravity; + integrateParticle(i, timeLeftInFrame); + extendBounds(_particlePositions[i]); - emitIdx = (emitIdx + 1) % _maxParticles; - _paCount++; + _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() { - for (quint32 i = 0; i < _maxParticles; i++) { - 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; +void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { + _maxParticles = maxParticles; - 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; + } } diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 3533ce84e7..6d1ef601f6 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -31,16 +31,16 @@ public: virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData); + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData); virtual void update(const quint64& now); virtual bool needsToCallUpdate() const; @@ -48,6 +48,7 @@ public: const rgbColor& getColor() const { 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 xColor& value) { _color[RED_INDEX] = value.red; @@ -86,7 +87,7 @@ public: float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); } static const quint32 DEFAULT_MAX_PARTICLES; - void setMaxParticles(quint32 maxParticles) { _maxParticles = maxParticles; } + void setMaxParticles(quint32 maxParticles); quint32 getMaxParticles() const { return _maxParticles; } static const float DEFAULT_LIFESPAN; @@ -98,7 +99,7 @@ public: float getEmitRate() const { return _emitRate; } 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; } static const float DEFAULT_EMIT_STRENGTH; @@ -131,7 +132,9 @@ protected: bool isAnimatingSomething() const; 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 rgbColor _color; @@ -150,21 +153,19 @@ protected: ShapeType _shapeType = SHAPE_TYPE_NONE; // all the internals of running the particle sim - const quint32 XYZ_STRIDE = 3; - float* _paLife; - float* _paPosition; - float* _paVelocity; - float _partialEmit; - quint32 _paCount; - quint32 _paHead; - float _paXmin; - float _paXmax; - float _paYmin; - float _paYmax; - float _paZmin; - float _paZmax; - unsigned int _randSeed; + QVector _particleLifetimes; + QVector _particlePositions; + QVector _particleVelocities; + float _timeUntilNextEmit; + // 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 diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index ec83e18c3d..87851ed80e 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX; + return VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 38aeed4993..82fc927166 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -142,5 +142,6 @@ const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE = 18; const PacketVersion VERSION_ENTITIES_HAVE_NAMES = 19; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_ATMOSPHERE = 20; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX = 21; +const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 22; #endif // hifi_PacketHeaders_h From 00864afe53aefd06ffd62a8c2652dba74dab8e04 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 May 2015 17:06:40 +0200 Subject: [PATCH 09/12] Quiet compiler --- interface/src/devices/DdeFaceTracker.cpp | 2 +- .../entities/src/SimpleEntitySimulation.cpp | 2 -- libraries/model/src/model/Stage.cpp | 25 +++++++++++-------- libraries/model/src/model/TextureStorage.h | 5 ++-- libraries/render-utils/src/GlowEffect.cpp | 12 --------- 5 files changed, 18 insertions(+), 28 deletions(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index 6ed253c1ec..14728f021b 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -183,8 +183,8 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui _filteredEyeBlinks(), _lastEyeCoefficients(), _isCalibrating(false), - _calibrationValues(), _calibrationCount(0), + _calibrationValues(), _calibrationBillboard(NULL), _calibrationBillboardID(0), _calibrationMessage(QString()) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index 518d10d056..18d5c4ecb0 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -54,8 +54,6 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItem* entity) { _hasSimulationOwnerEntities.remove(entity); } -const int SIMPLE_SIMULATION_DIRTY_FLAGS = EntityItem::DIRTY_VELOCITIES | EntityItem::DIRTY_MOTION_TYPE; - void SimpleEntitySimulation::changeEntityInternal(EntityItem* entity) { EntitySimulation::changeEntityInternal(entity); if (!entity->getSimulatorID().isNull()) { diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index e5d23e9191..a255a1f7c9 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "SkyFromAtmosphere_vert.h" #include "SkyFromAtmosphere_frag.h" @@ -292,17 +293,19 @@ void SunSkyStage::updateGraphicsObject() const { } // Background - switch (getBackgroundMode()) { - case NO_BACKGROUND: { - break; - } - case SKY_DOME: { - break; - } - case SKY_BOX: { - break; - } - }; + switch (getBackgroundMode()) { + case NO_BACKGROUND: { + break; + } + case SKY_DOME: { + break; + } + case SKY_BOX: { + break; + } + case NUM_BACKGROUND_MODES: + Q_UNREACHABLE(); + }; static int firstTime = 0; if (firstTime == 0) { diff --git a/libraries/model/src/model/TextureStorage.h b/libraries/model/src/model/TextureStorage.h index 2b19a6cc1d..ebc027298b 100755 --- a/libraries/model/src/model/TextureStorage.h +++ b/libraries/model/src/model/TextureStorage.h @@ -38,9 +38,10 @@ public: ~TextureStorage(); const QUrl& getUrl() const { return _url; } - const gpu::Texture::Type getType() const { return _usage._type; } + gpu::Texture::Type getType() const { return _usage._type; } const gpu::TexturePointer& getGPUTexture() const { return _gpuTexture; } - + + virtual void reset() { Storage::reset(); } void reset(const QUrl& url, const TextureUsage& usage); protected: diff --git a/libraries/render-utils/src/GlowEffect.cpp b/libraries/render-utils/src/GlowEffect.cpp index bb18729f12..cb69421b82 100644 --- a/libraries/render-utils/src/GlowEffect.cpp +++ b/libraries/render-utils/src/GlowEffect.cpp @@ -117,18 +117,6 @@ void GlowEffect::end() { glBlendColor(0.0f, 0.0f, 0.0f, _intensity = _intensityStack.pop()); } -static void maybeBind(const gpu::FramebufferPointer& fbo) { - if (fbo) { - glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(fbo)); - } -} - -static void maybeRelease(const gpu::FramebufferPointer& fbo) { - if (fbo) { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } -} - gpu::FramebufferPointer GlowEffect::render() { PerformanceTimer perfTimer("glowEffect"); From 9f7e90d03687a92509aa2cc8c8ee13a0e6909ee2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 May 2015 08:40:44 -0700 Subject: [PATCH 10/12] Add color picker to entity properties --- examples/html/colpick.js | 520 ++++++++++++++++++++++++++++ examples/html/css/colpick.css | 420 ++++++++++++++++++++++ examples/html/entityProperties.html | 107 +++++- examples/html/jquery-2.1.4.min.js | 4 + examples/html/style.css | 7 + 5 files changed, 1052 insertions(+), 6 deletions(-) create mode 100644 examples/html/colpick.js create mode 100644 examples/html/css/colpick.css create mode 100644 examples/html/jquery-2.1.4.min.js diff --git a/examples/html/colpick.js b/examples/html/colpick.js new file mode 100644 index 0000000000..d77432a342 --- /dev/null +++ b/examples/html/colpick.js @@ -0,0 +1,520 @@ +/* +colpick Color Picker +Copyright 2013 Jose Vargas. Licensed under GPL license. Based on Stefan Petre's Color Picker www.eyecon.ro, dual licensed under the MIT and GPL licenses + +For usage and examples: colpick.com/plugin + */ + +(function ($) { + var colpick = function () { + var + tpl = '
#
R
G
B
H
S
B
', + defaults = { + showEvent: 'click', + onShow: function () {}, + onBeforeShow: function(){}, + onHide: function () {}, + onChange: function () {}, + onSubmit: function () {}, + colorScheme: 'light', + color: '3289c7', + livePreview: true, + flat: false, + layout: 'full', + submit: 1, + submitText: 'OK', + height: 156 + }, + //Fill the inputs of the plugin + fillRGBFields = function (hsb, cal) { + var rgb = hsbToRgb(hsb); + $(cal).data('colpick').fields + .eq(1).val(rgb.r).end() + .eq(2).val(rgb.g).end() + .eq(3).val(rgb.b).end(); + }, + fillHSBFields = function (hsb, cal) { + $(cal).data('colpick').fields + .eq(4).val(Math.round(hsb.h)).end() + .eq(5).val(Math.round(hsb.s)).end() + .eq(6).val(Math.round(hsb.b)).end(); + }, + fillHexFields = function (hsb, cal) { + $(cal).data('colpick').fields.eq(0).val(hsbToHex(hsb)); + }, + //Set the round selector position + setSelector = function (hsb, cal) { + $(cal).data('colpick').selector.css('backgroundColor', '#' + hsbToHex({h: hsb.h, s: 100, b: 100})); + $(cal).data('colpick').selectorIndic.css({ + left: parseInt($(cal).data('colpick').height * hsb.s/100, 10), + top: parseInt($(cal).data('colpick').height * (100-hsb.b)/100, 10) + }); + }, + //Set the hue selector position + setHue = function (hsb, cal) { + $(cal).data('colpick').hue.css('top', parseInt($(cal).data('colpick').height - $(cal).data('colpick').height * hsb.h/360, 10)); + }, + //Set current and new colors + setCurrentColor = function (hsb, cal) { + $(cal).data('colpick').currentColor.css('backgroundColor', '#' + hsbToHex(hsb)); + }, + setNewColor = function (hsb, cal) { + $(cal).data('colpick').newColor.css('backgroundColor', '#' + hsbToHex(hsb)); + }, + //Called when the new color is changed + change = function (ev) { + var cal = $(this).parent().parent(), col; + if (this.parentNode.className.indexOf('_hex') > 0) { + cal.data('colpick').color = col = hexToHsb(fixHex(this.value)); + fillRGBFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + } else if (this.parentNode.className.indexOf('_hsb') > 0) { + cal.data('colpick').color = col = fixHSB({ + h: parseInt(cal.data('colpick').fields.eq(4).val(), 10), + s: parseInt(cal.data('colpick').fields.eq(5).val(), 10), + b: parseInt(cal.data('colpick').fields.eq(6).val(), 10) + }); + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + } else { + cal.data('colpick').color = col = rgbToHsb(fixRGB({ + r: parseInt(cal.data('colpick').fields.eq(1).val(), 10), + g: parseInt(cal.data('colpick').fields.eq(2).val(), 10), + b: parseInt(cal.data('colpick').fields.eq(3).val(), 10) + })); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + } + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 0]); + }, + //Change style on blur and on focus of inputs + blur = function (ev) { + $(this).parent().removeClass('colpick_focus'); + }, + focus = function () { + $(this).parent().parent().data('colpick').fields.parent().removeClass('colpick_focus'); + $(this).parent().addClass('colpick_focus'); + }, + //Increment/decrement arrows functions + downIncrement = function (ev) { + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + var field = $(this).parent().find('input').focus(); + var current = { + el: $(this).parent().addClass('colpick_slider'), + max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), + y: ev.pageY, + field: field, + val: parseInt(field.val(), 10), + preview: $(this).parent().parent().data('colpick').livePreview + }; + $(document).mouseup(current, upIncrement); + $(document).mousemove(current, moveIncrement); + }, + moveIncrement = function (ev) { + ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val - ev.pageY + ev.data.y, 10)))); + if (ev.data.preview) { + change.apply(ev.data.field.get(0), [true]); + } + return false; + }, + upIncrement = function (ev) { + change.apply(ev.data.field.get(0), [true]); + ev.data.el.removeClass('colpick_slider').find('input').focus(); + $(document).off('mouseup', upIncrement); + $(document).off('mousemove', moveIncrement); + return false; + }, + //Hue slider functions + downHue = function (ev) { + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + var current = { + cal: $(this).parent(), + y: $(this).offset().top + }; + $(document).on('mouseup touchend',current,upHue); + $(document).on('mousemove touchmove',current,moveHue); + + var pageY = ((ev.type == 'touchstart') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY ); + change.apply( + current.cal.data('colpick') + .fields.eq(4).val(parseInt(360*(current.cal.data('colpick').height - (pageY - current.y))/current.cal.data('colpick').height, 10)) + .get(0), + [current.cal.data('colpick').livePreview] + ); + return false; + }, + moveHue = function (ev) { + var pageY = ((ev.type == 'touchmove') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY ); + change.apply( + ev.data.cal.data('colpick') + .fields.eq(4).val(parseInt(360*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageY - ev.data.y))))/ev.data.cal.data('colpick').height, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upHue = function (ev) { + fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0)); + $(document).off('mouseup touchend',upHue); + $(document).off('mousemove touchmove',moveHue); + return false; + }, + //Color selector functions + downSelector = function (ev) { + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + var current = { + cal: $(this).parent(), + pos: $(this).offset() + }; + current.preview = current.cal.data('colpick').livePreview; + + $(document).on('mouseup touchend',current,upSelector); + $(document).on('mousemove touchmove',current,moveSelector); + + var payeX,pageY; + if(ev.type == 'touchstart') { + pageX = ev.originalEvent.changedTouches[0].pageX, + pageY = ev.originalEvent.changedTouches[0].pageY; + } else { + pageX = ev.pageX; + pageY = ev.pageY; + } + + change.apply( + current.cal.data('colpick').fields + .eq(6).val(parseInt(100*(current.cal.data('colpick').height - (pageY - current.pos.top))/current.cal.data('colpick').height, 10)).end() + .eq(5).val(parseInt(100*(pageX - current.pos.left)/current.cal.data('colpick').height, 10)) + .get(0), + [current.preview] + ); + return false; + }, + moveSelector = function (ev) { + var payeX,pageY; + if(ev.type == 'touchmove') { + pageX = ev.originalEvent.changedTouches[0].pageX, + pageY = ev.originalEvent.changedTouches[0].pageY; + } else { + pageX = ev.pageX; + pageY = ev.pageY; + } + + change.apply( + ev.data.cal.data('colpick').fields + .eq(6).val(parseInt(100*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageY - ev.data.pos.top))))/ev.data.cal.data('colpick').height, 10)).end() + .eq(5).val(parseInt(100*(Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageX - ev.data.pos.left))))/ev.data.cal.data('colpick').height, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upSelector = function (ev) { + fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0)); + $(document).off('mouseup touchend',upSelector); + $(document).off('mousemove touchmove',moveSelector); + return false; + }, + //Submit button + clickSubmit = function (ev) { + var cal = $(this).parent(); + var col = cal.data('colpick').color; + cal.data('colpick').origColor = col; + setCurrentColor(col, cal.get(0)); + cal.data('colpick').onSubmit(col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el); + }, + //Show/hide the color picker + show = function (ev) { + // Prevent the trigger of any direct parent + ev.stopPropagation(); + var cal = $('#' + $(this).data('colpickId')); + cal.data('colpick').onBeforeShow.apply(this, [cal.get(0)]); + var pos = $(this).offset(); + var top = pos.top + this.offsetHeight; + var left = pos.left; + var viewPort = getViewport(); + var calW = cal.width(); + if (left + calW > viewPort.l + viewPort.w) { + left -= calW; + } + cal.css({left: left + 'px', top: top + 'px'}); + if (cal.data('colpick').onShow.apply(this, [cal.get(0)]) != false) { + cal.show(); + } + //Hide when user clicks outside + $('html').mousedown({cal:cal}, hide); + cal.mousedown(function(ev){ev.stopPropagation();}) + }, + hide = function (ev) { + if (ev.data.cal.data('colpick').onHide.apply(this, [ev.data.cal.get(0)]) != false) { + ev.data.cal.hide(); + } + $('html').off('mousedown', hide); + }, + getViewport = function () { + var m = document.compatMode == 'CSS1Compat'; + return { + l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), + w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth) + }; + }, + //Fix the values if the user enters a negative or high value + fixHSB = function (hsb) { + return { + h: Math.min(360, Math.max(0, hsb.h)), + s: Math.min(100, Math.max(0, hsb.s)), + b: Math.min(100, Math.max(0, hsb.b)) + }; + }, + fixRGB = function (rgb) { + return { + r: Math.min(255, Math.max(0, rgb.r)), + g: Math.min(255, Math.max(0, rgb.g)), + b: Math.min(255, Math.max(0, rgb.b)) + }; + }, + fixHex = function (hex) { + var len = 6 - hex.length; + if (len > 0) { + var o = []; + for (var i=0; i
').attr('style','height:8.333333%; filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='+stops[i]+', endColorstr='+stops[i+1]+'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='+stops[i]+', endColorstr='+stops[i+1]+')";'); + huebar.append(div); + } + } else { + stopList = stops.join(','); + huebar.attr('style','background:-webkit-linear-gradient(top,'+stopList+'); background: -o-linear-gradient(top,'+stopList+'); background: -ms-linear-gradient(top,'+stopList+'); background:-moz-linear-gradient(top,'+stopList+'); -webkit-linear-gradient(top,'+stopList+'); background:linear-gradient(to bottom,'+stopList+'); '); + } + cal.find('div.colpick_hue').on('mousedown touchstart',downHue); + options.newColor = cal.find('div.colpick_new_color'); + options.currentColor = cal.find('div.colpick_current_color'); + //Store options and fill with default color + cal.data('colpick', options); + fillRGBFields(options.color, cal.get(0)); + fillHSBFields(options.color, cal.get(0)); + fillHexFields(options.color, cal.get(0)); + setHue(options.color, cal.get(0)); + setSelector(options.color, cal.get(0)); + setCurrentColor(options.color, cal.get(0)); + setNewColor(options.color, cal.get(0)); + //Append to body if flat=false, else show in place + if (options.flat) { + cal.appendTo(this).show(); + cal.css({ + position: 'relative', + display: 'block' + }); + } else { + cal.appendTo(document.body); + $(this).on(options.showEvent, show); + cal.css({ + position:'absolute' + }); + } + } + }); + }, + //Shows the picker + showPicker: function() { + return this.each( function () { + if ($(this).data('colpickId')) { + show.apply(this); + } + }); + }, + //Hides the picker + hidePicker: function() { + return this.each( function () { + if ($(this).data('colpickId')) { + $('#' + $(this).data('colpickId')).hide(); + } + }); + }, + //Sets a color as new and current (default) + setColor: function(col, setCurrent) { + setCurrent = (typeof setCurrent === "undefined") ? 1 : setCurrent; + if (typeof col == 'string') { + col = hexToHsb(col); + } else if (col.r != undefined && col.g != undefined && col.b != undefined) { + col = rgbToHsb(col); + } else if (col.h != undefined && col.s != undefined && col.b != undefined) { + col = fixHSB(col); + } else { + return this; + } + return this.each(function(){ + if ($(this).data('colpickId')) { + var cal = $('#' + $(this).data('colpickId')); + cal.data('colpick').color = col; + cal.data('colpick').origColor = col; + fillRGBFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + setHue(col, cal.get(0)); + setSelector(col, cal.get(0)); + + setNewColor(col, cal.get(0)); + cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 1]); + if(setCurrent) { + setCurrentColor(col, cal.get(0)); + } + } + }); + } + }; + }(); + //Color space convertions + var hexToRgb = function (hex) { + var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); + return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; + }; + var hexToHsb = function (hex) { + return rgbToHsb(hexToRgb(hex)); + }; + var rgbToHsb = function (rgb) { + var hsb = {h: 0, s: 0, b: 0}; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + hsb.s = max != 0 ? 255 * delta / max : 0; + if (hsb.s != 0) { + if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta; + else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta; + else hsb.h = 4 + (rgb.r - rgb.g) / delta; + } else hsb.h = -1; + hsb.h *= 60; + if (hsb.h < 0) hsb.h += 360; + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }; + var hsbToRgb = function (hsb) { + var rgb = {}; + var h = hsb.h; + var s = hsb.s*255/100; + var v = hsb.b*255/100; + if(s == 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255-s)*v/255; + var t3 = (t1-t2)*(h%60)/60; + if(h==360) h = 0; + if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} + else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} + else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} + else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} + else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} + else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} + else {rgb.r=0; rgb.g=0; rgb.b=0} + } + return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; + }; + var rgbToHex = function (rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function (nr, val) { + if (val.length == 1) { + hex[nr] = '0' + val; + } + }); + return hex.join(''); + }; + var hsbToHex = function (hsb) { + return rgbToHex(hsbToRgb(hsb)); + }; + $.fn.extend({ + colpick: colpick.init, + colpickHide: colpick.hidePicker, + colpickShow: colpick.showPicker, + colpickSetColor: colpick.setColor + }); + $.extend({ + colpick:{ + rgbToHex: rgbToHex, + rgbToHsb: rgbToHsb, + hsbToHex: hsbToHex, + hsbToRgb: hsbToRgb, + hexToHsb: hexToHsb, + hexToRgb: hexToRgb + } + }); +})(jQuery); diff --git a/examples/html/css/colpick.css b/examples/html/css/colpick.css new file mode 100644 index 0000000000..564f60cb3b --- /dev/null +++ b/examples/html/css/colpick.css @@ -0,0 +1,420 @@ +/* +colpick Color Picker / colpick.com +*/ + +/*Main container*/ +.colpick { + position: absolute; + width: 346px; + height: 170px; + overflow: hidden; + display: none; + font-family: Arial, Helvetica, sans-serif; + background:#ebebeb; + border: 1px solid #bbb; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + + /*Prevents selecting text when dragging the selectors*/ + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} +/*Color selection box with gradients*/ +.colpick_color { + position: absolute; + left: 7px; + top: 7px; + width: 156px; + height: 156px; + overflow: hidden; + outline: 1px solid #aaa; + cursor: crosshair; +} +.colpick_color_overlay1 { + position: absolute; + left:0; + top:0; + width: 156px; + height: 156px; + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')"; /* IE8 */ + background: -moz-linear-gradient(left, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ */ + background: linear-gradient(to right, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff'); /* IE6 & IE7 */ +} +.colpick_color_overlay2 { + position: absolute; + left:0; + top:0; + width: 156px; + height: 156px; + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')"; /* IE8 */ + background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* IE10+ */ + background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#000000',GradientType=0 ); /* IE6-9 */ +} +/*Circular color selector*/ +.colpick_selector_outer { + background:none; + position: absolute; + width: 11px; + height: 11px; + margin: -6px 0 0 -6px; + border: 1px solid black; + border-radius: 50%; +} +.colpick_selector_inner{ + position: absolute; + width: 9px; + height: 9px; + border: 1px solid white; + border-radius: 50%; +} +/*Vertical hue bar*/ +.colpick_hue { + position: absolute; + top: 6px; + left: 175px; + width: 19px; + height: 156px; + border: 1px solid #aaa; + cursor: n-resize; +} +/*Hue bar sliding indicator*/ +.colpick_hue_arrs { + position: absolute; + left: -8px; + width: 35px; + height: 7px; + margin: -7px 0 0 0; +} +.colpick_hue_larr { + position:absolute; + width: 0; + height: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-left: 7px solid #858585; +} +.colpick_hue_rarr { + position:absolute; + right:0; + width: 0; + height: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-right: 7px solid #858585; +} +/*New color box*/ +.colpick_new_color { + position: absolute; + left: 207px; + top: 6px; + width: 60px; + height: 27px; + background: #f00; + border: 1px solid #8f8f8f; +} +/*Current color box*/ +.colpick_current_color { + position: absolute; + left: 277px; + top: 6px; + width: 60px; + height: 27px; + background: #f00; + border: 1px solid #8f8f8f; +} +/*Input field containers*/ +.colpick_field, .colpick_hex_field { + position: absolute; + height: 20px; + width: 60px; + overflow:hidden; + background:#f3f3f3; + color:#b8b8b8; + font-size:12px; + border:1px solid #bdbdbd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.colpick_rgb_r { + top: 40px; + left: 207px; +} +.colpick_rgb_g { + top: 67px; + left: 207px; +} +.colpick_rgb_b { + top: 94px; + left: 207px; +} +.colpick_hsb_h { + top: 40px; + left: 277px; +} +.colpick_hsb_s { + top: 67px; + left: 277px; +} +.colpick_hsb_b { + top: 94px; + left: 277px; +} +.colpick_hex_field { + width: 68px; + left: 207px; + top: 121px; +} +/*Text field container on focus*/ +.colpick_focus { + border-color: #999; +} +/*Field label container*/ +.colpick_field_letter { + position: absolute; + width: 12px; + height: 20px; + line-height: 20px; + padding-left: 4px; + background: #efefef; + border-right: 1px solid #bdbdbd; + font-weight: bold; + color:#777; +} +/*Text inputs*/ +.colpick_field input, .colpick_hex_field input { + position: absolute; + right: 11px; + margin: 0; + padding: 0; + height: 20px; + line-height: 20px; + background: transparent; + border: none; + font-size: 12px; + font-family: Arial, Helvetica, sans-serif; + color: #555; + text-align: right; + outline: none; +} +.colpick_hex_field input { + right: 4px; +} +/*Field up/down arrows*/ +.colpick_field_arrs { + position: absolute; + top: 0; + right: 0; + width: 9px; + height: 21px; + cursor: n-resize; +} +.colpick_field_uarr { + position: absolute; + top: 5px; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 4px solid #959595; +} +.colpick_field_darr { + position: absolute; + bottom:5px; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #959595; +} +/*Submit/Select button*/ +.colpick_submit { + position: absolute; + left: 207px; + top: 149px; + width: 130px; + height: 22px; + line-height:22px; + background: #efefef; + text-align: center; + color: #555; + font-size: 12px; + font-weight:bold; + border: 1px solid #bdbdbd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.colpick_submit:hover { + background:#f3f3f3; + border-color:#999; + cursor: pointer; +} + +/*full layout with no submit button*/ +.colpick_full_ns .colpick_submit, .colpick_full_ns .colpick_current_color{ + display:none; +} +.colpick_full_ns .colpick_new_color { + width: 130px; + height: 25px; +} +.colpick_full_ns .colpick_rgb_r, .colpick_full_ns .colpick_hsb_h { + top: 42px; +} +.colpick_full_ns .colpick_rgb_g, .colpick_full_ns .colpick_hsb_s { + top: 73px; +} +.colpick_full_ns .colpick_rgb_b, .colpick_full_ns .colpick_hsb_b { + top: 104px; +} +.colpick_full_ns .colpick_hex_field { + top: 135px; +} + +/*rgbhex layout*/ +.colpick_rgbhex .colpick_hsb_h, .colpick_rgbhex .colpick_hsb_s, .colpick_rgbhex .colpick_hsb_b { + display:none; +} +.colpick_rgbhex { + width:282px; +} +.colpick_rgbhex .colpick_field, .colpick_rgbhex .colpick_submit { + width:68px; +} +.colpick_rgbhex .colpick_new_color { + width:34px; + border-right:none; +} +.colpick_rgbhex .colpick_current_color { + width:34px; + left:240px; + border-left:none; +} + +/*rgbhex layout, no submit button*/ +.colpick_rgbhex_ns .colpick_submit, .colpick_rgbhex_ns .colpick_current_color{ + display:none; +} +.colpick_rgbhex_ns .colpick_new_color{ + width:68px; + border: 1px solid #8f8f8f; +} +.colpick_rgbhex_ns .colpick_rgb_r { + top: 42px; +} +.colpick_rgbhex_ns .colpick_rgb_g { + top: 73px; +} +.colpick_rgbhex_ns .colpick_rgb_b { + top: 104px; +} +.colpick_rgbhex_ns .colpick_hex_field { + top: 135px; +} + +/*hex layout*/ +.colpick_hex .colpick_hsb_h, .colpick_hex .colpick_hsb_s, .colpick_hex .colpick_hsb_b, .colpick_hex .colpick_rgb_r, .colpick_hex .colpick_rgb_g, .colpick_hex .colpick_rgb_b { + display:none; +} +.colpick_hex { + width:206px; + height:201px; +} +.colpick_hex .colpick_hex_field { + width:72px; + height:25px; + top:168px; + left:80px; +} +.colpick_hex .colpick_hex_field div, .colpick_hex .colpick_hex_field input { + height: 25px; + line-height: 25px; +} +.colpick_hex .colpick_new_color { + left:9px; + top:168px; + width:30px; + border-right:none; +} +.colpick_hex .colpick_current_color { + left:39px; + top:168px; + width:30px; + border-left:none; +} +.colpick_hex .colpick_submit { + left:164px; + top: 168px; + width:30px; + height:25px; + line-height: 25px; +} + +/*hex layout, no submit button*/ +.colpick_hex_ns .colpick_submit, .colpick_hex_ns .colpick_current_color { + display:none; +} +.colpick_hex_ns .colpick_hex_field { + width:80px; +} +.colpick_hex_ns .colpick_new_color{ + width:60px; + border: 1px solid #8f8f8f; +} + +/*Dark color scheme*/ +.colpick_dark { + background: #161616; + border-color: #2a2a2a; +} +.colpick_dark .colpick_color { + outline-color: #333; +} +.colpick_dark .colpick_hue { + border-color: #555; +} +.colpick_dark .colpick_field, .colpick_dark .colpick_hex_field { + background: #101010; + border-color: #2d2d2d; +} +.colpick_dark .colpick_field_letter { + background: #131313; + border-color: #2d2d2d; + color: #696969; +} +.colpick_dark .colpick_field input, .colpick_dark .colpick_hex_field input { + color: #7a7a7a; +} +.colpick_dark .colpick_field_uarr { + border-bottom-color:#696969; +} +.colpick_dark .colpick_field_darr { + border-top-color:#696969; +} +.colpick_dark .colpick_focus { + border-color:#444; +} +.colpick_dark .colpick_submit { + background: #131313; + border-color:#2d2d2d; + color:#7a7a7a; +} +.colpick_dark .colpick_submit:hover { + background-color:#101010; + border-color:#444; +} \ No newline at end of file diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 56fc84ef96..2bf161dee3 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -1,6 +1,9 @@ + + + @@ -915,6 +1004,7 @@
Color
+
R
G
B
@@ -1004,6 +1094,7 @@
Text Color
+
R
G
B
@@ -1012,6 +1103,7 @@
Background Color
+
R
G
B
@@ -1027,6 +1119,7 @@
Color
+
R
G
B
@@ -1061,6 +1154,7 @@
Key Light Color
+
R
G
B
@@ -1139,6 +1233,7 @@
Skybox Color
+
R
G
B
diff --git a/examples/html/jquery-2.1.4.min.js b/examples/html/jquery-2.1.4.min.js new file mode 100644 index 0000000000..49990d6e14 --- /dev/null +++ b/examples/html/jquery-2.1.4.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ +return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("