diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index becc8c86b2..c23f8b0dcc 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -143,6 +143,41 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Blue:", value: properties.color.blue }); index++; } + + if (properties.type == "Light") { + array.push({ label: "Light Properties:", type: "header" }); + index++; + array.push({ label: "Is Spot Light:", value: properties.isSpotlight }); + index++; + array.push({ label: "Diffuse Red:", value: properties.diffuseColor.red }); + index++; + array.push({ label: "Diffuse Green:", value: properties.diffuseColor.green }); + index++; + array.push({ label: "Diffuse Blue:", value: properties.diffuseColor.blue }); + index++; + array.push({ label: "Ambient Red:", value: properties.ambientColor.red }); + index++; + array.push({ label: "Ambient Green:", value: properties.ambientColor.green }); + index++; + array.push({ label: "Ambient Blue:", value: properties.ambientColor.blue }); + index++; + array.push({ label: "Specular Red:", value: properties.specularColor.red }); + index++; + array.push({ label: "Specular Green:", value: properties.specularColor.green }); + index++; + array.push({ label: "Specular Blue:", value: properties.specularColor.blue }); + index++; + array.push({ label: "Constant Attenuation:", value: properties.constantAttenuation }); + index++; + array.push({ label: "Linear Attenuation:", value: properties.linearAttenuation }); + index++; + array.push({ label: "Quadratic Attenuation:", value: properties.quadraticAttenuation }); + index++; + array.push({ label: "Exponent:", value: properties.exponent }); + index++; + array.push({ label: "Cutoff (in degrees):", value: properties.cutoff }); + index++; + } array.push({ button: "Cancel" }); index++; @@ -243,6 +278,25 @@ EntityPropertyDialogBox = (function () { properties.color.green = array[index++].value; properties.color.blue = array[index++].value; } + if (properties.type == "Light") { + index++; // skip header + properties.isSpotlight = array[index++].value; + properties.diffuseColor.red = array[index++].value; + properties.diffuseColor.green = array[index++].value; + properties.diffuseColor.blue = array[index++].value; + properties.ambientColor.red = array[index++].value; + properties.ambientColor.green = array[index++].value; + properties.ambientColor.blue = array[index++].value; + properties.specularColor.red = array[index++].value; + properties.specularColor.green = array[index++].value; + properties.specularColor.blue = array[index++].value; + properties.constantAttenuation = array[index++].value; + properties.linearAttenuation = array[index++].value; + properties.quadraticAttenuation = array[index++].value; + properties.exponent = array[index++].value; + properties.cutoff = array[index++].value; + } + Entities.editEntity(editModelID, properties); selectionDisplay.select(editModelID, false); } diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index aa38baf9e2..6369f87417 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -73,6 +73,7 @@ var toolBar = (function () { newModelButton, newCubeButton, newSphereButton, + newLightButton, browseModelsButton, loadURLMenuItem, loadFileMenuItem, @@ -157,6 +158,15 @@ var toolBar = (function () { visible: true }); + newLightButton = toolBar.addTool({ + imageURL: toolIconUrl + "light.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: true + }); + } function toggleNewModelButton(active) { @@ -319,7 +329,32 @@ var toolBar = (function () { color: { red: 255, green: 0, blue: 0 } }); } else { - print("Can't create box: Box would be out of bounds."); + print("Can't create sphere: Sphere would be out of bounds."); + } + return true; + } + + if (newLightButton === toolBar.clicked(clickedOverlay)) { + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + + if (position.x > 0 && position.y > 0 && position.z > 0) { + Entities.addEntity({ + type: "Light", + position: position, + dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, + isSpotlight: false, + diffuseColor: { red: 255, green: 255, blue: 255 }, + ambientColor: { red: 255, green: 255, blue: 255 }, + specularColor: { red: 0, green: 0, blue: 0 }, + + constantAttenuation: 1, + linearAttenuation: 0, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees + }); + } else { + print("Can't create Light: Light would be out of bounds."); } return true; } diff --git a/examples/spotlightExample.js b/examples/spotlightExample.js new file mode 100644 index 0000000000..5eb5432f3f --- /dev/null +++ b/examples/spotlightExample.js @@ -0,0 +1,37 @@ +// +// spotlightExample.js +// examples +// +// Created by Brad Hefta-Gaub on 10/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates creating and editing a particle +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var lightProperties = { + type: "Light", + position: { x: 0, y: 0, z: 0 }, + dimensions: { x: 1000, y: 1000, z: 1000 }, + angularVelocity: { x: 0, y: 10, z: 0 }, + angularDamping: 0, + + isSpotlight: true, + diffuseColor: { red: 255, green: 255, blue: 255 }, + ambientColor: { red: 255, green: 255, blue: 255 }, + specularColor: { red: 255, green: 255, blue: 255 }, + + constantAttenuation: 1, + linearAttenuation: 0, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees +}; + +var spotlightID = Entities.addEntity(lightProperties); + +Script.scriptEnding.connect(function() { + Entities.deleteEntity(spotlightID); +}); diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 2c7c970376..7eb28c2d3e 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -24,6 +24,7 @@ #include "EntityTreeRenderer.h" #include "RenderableBoxEntityItem.h" +#include "RenderableLightEntityItem.h" #include "RenderableModelEntityItem.h" #include "RenderableSphereEntityItem.h" @@ -39,6 +40,7 @@ EntityTreeRenderer::EntityTreeRenderer() : REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory) + REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) } EntityTreeRenderer::~EntityTreeRenderer() { diff --git a/interface/src/entities/RenderableLightEntityItem.cpp b/interface/src/entities/RenderableLightEntityItem.cpp new file mode 100644 index 0000000000..bf9939e164 --- /dev/null +++ b/interface/src/entities/RenderableLightEntityItem.cpp @@ -0,0 +1,89 @@ +// +// RenderableLightEntityItem.cpp +// interface/src +// +// Created by Brad Hefta-Gaub on 8/6/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include + +#include "InterfaceConfig.h" + +#include +#include + + +#include "Application.h" +#include "Menu.h" +#include "EntityTreeRenderer.h" +#include "RenderableLightEntityItem.h" + + +EntityItem* RenderableLightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return new RenderableLightEntityItem(entityID, properties); +} + +void RenderableLightEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderableLightEntityItem::render"); + assert(getType() == EntityTypes::Light); + glm::vec3 position = getPositionInMeters(); + glm::vec3 center = getCenterInMeters(); + glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE; + glm::quat rotation = getRotation(); + float largestDiameter = glm::max(dimensions.x, dimensions.y, dimensions.z); + + const float MAX_COLOR = 255.0f; + float diffuseR = getDiffuseColor()[RED_INDEX] / MAX_COLOR; + float diffuseG = getDiffuseColor()[GREEN_INDEX] / MAX_COLOR; + float diffuseB = getDiffuseColor()[BLUE_INDEX] / MAX_COLOR; + + float ambientR = getAmbientColor()[RED_INDEX] / MAX_COLOR; + float ambientG = getAmbientColor()[GREEN_INDEX] / MAX_COLOR; + float ambientB = getAmbientColor()[BLUE_INDEX] / MAX_COLOR; + + float specularR = getSpecularColor()[RED_INDEX] / MAX_COLOR; + float specularG = getSpecularColor()[GREEN_INDEX] / MAX_COLOR; + float specularB = getSpecularColor()[BLUE_INDEX] / MAX_COLOR; + + glm::vec3 ambient = glm::vec3(ambientR, ambientG, ambientB); + glm::vec3 diffuse = glm::vec3(diffuseR, diffuseG, diffuseB); + glm::vec3 specular = glm::vec3(specularR, specularG, specularB); + glm::vec3 direction = IDENTITY_FRONT * rotation; + float constantAttenuation = getConstantAttenuation(); + float linearAttenuation = getLinearAttenuation(); + float quadraticAttenuation = getQuadraticAttenuation(); + float exponent = getExponent(); + float cutoff = glm::radians(getCutoff()); + + if (_isSpotlight) { + Application::getInstance()->getDeferredLightingEffect()->addSpotLight(position, largestDiameter / 2.0f, + ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation, + direction, exponent, cutoff); + } else { + Application::getInstance()->getDeferredLightingEffect()->addPointLight(position, largestDiameter / 2.0f, + ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); + } + + bool wantDebug = false; + if (wantDebug) { + glColor4f(diffuseR, diffuseG, diffuseB, 1.0f); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + + glScalef(dimensions.x, dimensions.y, dimensions.z); + Application::getInstance()->getDeferredLightingEffect()->renderWireSphere(0.5f, 15, 15); + glPopMatrix(); + glPopMatrix(); + } +}; diff --git a/interface/src/entities/RenderableLightEntityItem.h b/interface/src/entities/RenderableLightEntityItem.h new file mode 100644 index 0000000000..cecd9b761e --- /dev/null +++ b/interface/src/entities/RenderableLightEntityItem.h @@ -0,0 +1,40 @@ +// +// RenderableLightEntityItem.h +// interface/src/entities +// +// Created by Brad Hefta-Gaub on 8/6/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderableLightEntityItem_h +#define hifi_RenderableLightEntityItem_h + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +class RenderableLightEntityItem : public LightEntityItem { +public: + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + RenderableLightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + LightEntityItem(entityItemID, properties) + { } + + virtual void render(RenderArgs* args); +}; + + +#endif // hifi_RenderableLightEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 90f56e36b9..c05fe5417d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -68,8 +68,8 @@ EntityItemProperties::EntityItemProperties() : _animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS), _glowLevel(0.0f), _localRenderAlpha(1.0f), + _isSpotlight(false), - _naturalDimensions(1.0f, 1.0f, 1.0f), _colorChanged(false), _modelURLChanged(false), _animationURLChanged(false), @@ -78,11 +78,48 @@ EntityItemProperties::EntityItemProperties() : _animationFPSChanged(false), _glowLevelChanged(false), _localRenderAlphaChanged(false), + _isSpotlightChanged(false), - _defaultSettings(true) + _diffuseColor(), + _ambientColor(), + _specularColor(), + _constantAttenuation(1.0f), + _linearAttenuation(0.0f), + _quadraticAttenuation(0.0f), + _exponent(0.0f), + _cutoff(PI), + + _diffuseColorChanged(false), + _ambientColorChanged(false), + _specularColorChanged(false), + _constantAttenuationChanged(false), + _linearAttenuationChanged(false), + _quadraticAttenuationChanged(false), + _exponentChanged(false), + _cutoffChanged(false), + + _defaultSettings(true), + _sittingPoints(NULL), + _naturalDimensions(1.0f, 1.0f, 1.0f) { + if (_sittingPoints) { + delete _sittingPoints; + _sittingPoints = NULL; + } } +void EntityItemProperties::setSittingPoints(const QVector& sittingPoints) { + if (!_sittingPoints) { + _sittingPoints = new QVector; + } + _sittingPoints->clear(); + + foreach (SittingPoint sitPoint, sittingPoints) { + _sittingPoints->append(sitPoint); + } +} + + void EntityItemProperties::debugDump() const { qDebug() << "EntityItemProperties..."; qDebug() << " _type=" << EntityTypes::getEntityTypeName(_type); @@ -120,6 +157,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping); CHECK_PROPERTY_CHANGE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions); CHECK_PROPERTY_CHANGE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove); + CHECK_PROPERTY_CHANGE(PROP_IS_SPOTLIGHT, isSpotlight); + CHECK_PROPERTY_CHANGE(PROP_DIFFUSE_COLOR, diffuseColor); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_COLOR, ambientColor); + CHECK_PROPERTY_CHANGE(PROP_SPECULAR_COLOR, specularColor); + CHECK_PROPERTY_CHANGE(PROP_CONSTANT_ATTENUATION, constantAttenuation); + CHECK_PROPERTY_CHANGE(PROP_LINEAR_ATTENUATION, linearAttenuation); + CHECK_PROPERTY_CHANGE(PROP_QUADRATIC_ATTENUATION, quadraticAttenuation); + CHECK_PROPERTY_CHANGE(PROP_EXPONENT, exponent); + CHECK_PROPERTY_CHANGE(PROP_CUTOFF, cutoff); return changedProperties; } @@ -161,17 +207,30 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions); COPY_PROPERTY_TO_QSCRIPTVALUE(collisionsWillMove); + COPY_PROPERTY_TO_QSCRIPTVALUE(isSpotlight); + COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(diffuseColor, getDiffuseColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(ambientColor, getAmbientColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(specularColor, getSpecularColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE(constantAttenuation); + COPY_PROPERTY_TO_QSCRIPTVALUE(linearAttenuation); + COPY_PROPERTY_TO_QSCRIPTVALUE(quadraticAttenuation); + COPY_PROPERTY_TO_QSCRIPTVALUE(exponent); + COPY_PROPERTY_TO_QSCRIPTVALUE(cutoff); // Sitting properties support QScriptValue sittingPoints = engine->newObject(); - for (int i = 0; i < _sittingPoints.size(); ++i) { - QScriptValue sittingPoint = engine->newObject(); - sittingPoint.setProperty("name", _sittingPoints[i].name); - sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints[i].position)); - sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints[i].rotation)); - sittingPoints.setProperty(i, sittingPoint); + if (_sittingPoints) { + for (int i = 0; i < _sittingPoints->size(); ++i) { + QScriptValue sittingPoint = engine->newObject(); + sittingPoint.setProperty("name", _sittingPoints->at(i).name); + sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints->at(i).position)); + sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints->at(i).rotation)); + sittingPoints.setProperty(i, sittingPoint); + } + sittingPoints.setProperty("length", _sittingPoints->size()); + } else { + sittingPoints.setProperty("length", 0); } - sittingPoints.setProperty("length", _sittingPoints.size()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable AABox aaBox = getAABoxInMeters(); @@ -220,6 +279,15 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localRenderAlpha, setLocalRenderAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(ignoreForCollisions, setIgnoreForCollisions); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(collisionsWillMove, setCollisionsWillMove); + COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(isSpotlight, setIsSpotlight); + COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(diffuseColor, setDiffuseColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(ambientColor, setAmbientColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(specularColor, setSpecularColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(constantAttenuation, setConstantAttenuation); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(linearAttenuation, setLinearAttenuation); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(quadraticAttenuation, setQuadraticAttenuation); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(exponent, setExponent); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(cutoff, setCutoff); _lastEdited = usecTimestampNow(); } @@ -370,6 +438,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, properties.getVisible()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, properties.getIgnoreForCollisions()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, properties.getCollisionsWillMove()); + APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, appendValue, properties.getIsSpotlight()); + APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, properties.getDiffuseColor()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, properties.getAmbientColor()); + APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, properties.getSpecularColor()); + APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, properties.getConstantAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, properties.getLinearAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, properties.getQuadraticAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, properties.getExponent()); + APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -568,6 +645,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_FOR_COLLISIONS, bool, setIgnoreForCollisions); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); + READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_DIFFUSE_COLOR, setDiffuseColor); + READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_AMBIENT_COLOR, setAmbientColor); + READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_SPECULAR_COLOR, setSpecularColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CONSTANT_ATTENUATION, float, setConstantAttenuation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINEAR_ATTENUATION, float, setLinearAttenuation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUADRATIC_ATTENUATION, float, setQuadraticAttenuation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff); return valid; } @@ -622,6 +708,16 @@ void EntityItemProperties::markAllChanged() { _animationFPSChanged = true; _glowLevelChanged = true; _localRenderAlphaChanged = true; + _isSpotlightChanged = true; + + _diffuseColorChanged = true; + _ambientColorChanged = true; + _specularColorChanged = true; + _constantAttenuationChanged = true; + _linearAttenuationChanged = true; + _quadraticAttenuationChanged = true; + _exponentChanged = true; + _cutoffChanged = true; } AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6e1594fb9b..5fef2151b2 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -32,7 +32,6 @@ #include "EntityItemPropertiesMacros.h" #include "EntityTypes.h" -// PropertyFlags support enum EntityPropertyList { PROP_PAGED_PROPERTY, PROP_CUSTOM_PROPERTIES_INCLUDED, @@ -65,7 +64,18 @@ enum EntityPropertyList { PROP_IGNORE_FOR_COLLISIONS, PROP_COLLISIONS_WILL_MOVE, - PROP_LAST_ITEM = PROP_COLLISIONS_WILL_MOVE + // property used by Light entity + PROP_IS_SPOTLIGHT, + PROP_DIFFUSE_COLOR, + PROP_AMBIENT_COLOR, + PROP_SPECULAR_COLOR, + PROP_CONSTANT_ATTENUATION, + PROP_LINEAR_ATTENUATION, + PROP_QUADRATIC_ATTENUATION, + PROP_EXPONENT, + PROP_CUTOFF, + + PROP_LAST_ITEM = PROP_CUTOFF }; typedef PropertyFlags EntityPropertyFlags; @@ -83,6 +93,7 @@ class EntityItemProperties { friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods friend class BoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class SphereEntityItem; // TODO: consider removing this friend relationship and use public methods + friend class LightEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(); virtual ~EntityItemProperties() { }; @@ -209,8 +220,7 @@ public: void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } void markAllChanged(); - QVector getSittingPoints() const { return _sittingPoints; } - void setSittingPoints(QVector sittingPoints) { _sittingPoints = sittingPoints; } + void setSittingPoints(const QVector& sittingPoints); const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } @@ -233,6 +243,36 @@ public: bool getCollisionsWillMove() const { return _collisionsWillMove; } void setCollisionsWillMove(bool value) { _collisionsWillMove = value; _collisionsWillMoveChanged = true; } + bool getIsSpotlight() const { return _isSpotlight; } + void setIsSpotlight(bool value) { _isSpotlight = value; _isSpotlightChanged = true; } + + xColor getDiffuseColor() const { return _diffuseColor; } + xColor getAmbientColor() const { return _ambientColor; } + xColor getSpecularColor() const { return _specularColor; } + + void setDiffuseColor(const xColor& value) { _diffuseColor = value; _diffuseColorChanged = true; } + void setAmbientColor(const xColor& value) { _ambientColor = value; _ambientColorChanged = true; } + void setSpecularColor(const xColor& value) { _specularColor = value; _specularColorChanged = true; } + + bool diffuseColorChanged() const { return _colorChanged; } + bool ambientColorChanged() const { return _ambientColorChanged; } + bool specularColorChanged() const { return _specularColorChanged; } + + bool getConstantAttenuation() const { return _constantAttenuation; } + void setConstantAttenuation(float value) { _constantAttenuation = value; _constantAttenuationChanged = true; } + + bool getLinearAttenuation() const { return _linearAttenuation; } + void setLinearAttenuation(float value) { _linearAttenuation = value; _linearAttenuationChanged = true; } + + bool getQuadraticAttenuation() const { return _quadraticAttenuation; } + void setQuadraticAttenuation(float value) { _quadraticAttenuation = value; _quadraticAttenuationChanged = true; } + + bool getExponent() const { return _exponent; } + void setExponent(bool value) { _exponent = value; _exponentChanged = true; } + + bool getCutoff() const { return _cutoff; } + void setCutoff(bool value) { _cutoff = value; _cutoffChanged = true; } + void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } private: @@ -288,8 +328,7 @@ private: float _animationFPS; float _glowLevel; float _localRenderAlpha; - QVector _sittingPoints; - glm::vec3 _naturalDimensions; + bool _isSpotlight; bool _colorChanged; bool _modelURLChanged; @@ -299,8 +338,32 @@ private: bool _animationFPSChanged; bool _glowLevelChanged; bool _localRenderAlphaChanged; + bool _isSpotlightChanged; + + xColor _diffuseColor; + xColor _ambientColor; + xColor _specularColor; + float _constantAttenuation; + float _linearAttenuation; + float _quadraticAttenuation; + float _exponent; + float _cutoff; + + bool _diffuseColorChanged; + bool _ambientColorChanged; + bool _specularColorChanged; + bool _constantAttenuationChanged; + bool _linearAttenuationChanged; + bool _quadraticAttenuationChanged; + bool _exponentChanged; + bool _cutoffChanged; bool _defaultSettings; + + // NOTE: The following are pseudo client only properties. They are only used in clients which can access + // properties of model geometry. But these properties are not serialized like other properties. + QVector* _sittingPoints; + glm::vec3 _naturalDimensions; }; Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 16f883a36a..b5a489f88c 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -147,6 +147,10 @@ QScriptValue P = xColorToScriptValue(engine, _##P); \ properties.setProperty(#P, P); +#define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(P,G) \ + QScriptValue P = xColorToScriptValue(engine, G); \ + properties.setProperty(#P, P); + #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ properties.setProperty(#P, G); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 7505c3f768..aaa297f4fd 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -19,6 +19,7 @@ #include "EntityTypes.h" #include "BoxEntityItem.h" +#include "LightEntityItem.h" #include "ModelEntityItem.h" #include "SphereEntityItem.h" @@ -33,6 +34,7 @@ const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown"; REGISTER_ENTITY_TYPE(Model) REGISTER_ENTITY_TYPE(Box) REGISTER_ENTITY_TYPE(Sphere) +REGISTER_ENTITY_TYPE(Light) const QString& EntityTypes::getEntityTypeName(EntityType entityType) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 8851b04b7e..85bbff99ef 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -33,7 +33,8 @@ public: Model, Box, Sphere, - LAST = Sphere + Light, + LAST = Light } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp new file mode 100644 index 0000000000..20f28cd98c --- /dev/null +++ b/libraries/entities/src/LightEntityItem.cpp @@ -0,0 +1,148 @@ +// +// LightEntityItem.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include + +#include + +#include "EntityTree.h" +#include "EntityTreeElement.h" +#include "LightEntityItem.h" + + +EntityItem* LightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return new LightEntityItem(entityID, properties); +} + +// our non-pure virtual subclass for now... +LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + EntityItem(entityItemID, properties) +{ + _type = EntityTypes::Light; + + // default property values + const quint8 MAX_COLOR = 255; + _ambientColor[RED_INDEX] = _ambientColor[GREEN_INDEX] = _ambientColor[BLUE_INDEX] = 0; + _diffuseColor[RED_INDEX] = _diffuseColor[GREEN_INDEX] = _diffuseColor[BLUE_INDEX] = MAX_COLOR; + _specularColor[RED_INDEX] = _specularColor[GREEN_INDEX] = _specularColor[BLUE_INDEX] = MAX_COLOR; + _constantAttenuation = 1.0f; + _linearAttenuation = 0.0f; + _quadraticAttenuation = 0.0f; + _exponent = 0.0f; + _cutoff = PI; + + setProperties(properties, true); + + // a light is not collide-able so we make it's shape be a tiny sphere at origin + _emptyShape.setTranslation(glm::vec3(0.0f, 0.0f, 0.0f)); + _emptyShape.setRadius(0.0f); +} + +EntityItemProperties LightEntityItem::getProperties() const { + EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(diffuseColor, getDiffuseXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientColor, getAmbientXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(specularColor, getSpecularXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(constantAttenuation, getConstantAttenuation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(linearAttenuation, getLinearAttenuation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(quadraticAttenuation, getQuadraticAttenuation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(exponent, getExponent); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cutoff, getCutoff); + + return properties; +} + +bool LightEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) { + bool somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(diffuseColor, setDiffuseColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientColor, setAmbientColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(specularColor, setSpecularColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(constantAttenuation, setConstantAttenuation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(linearAttenuation, setLinearAttenuation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(quadraticAttenuation, setQuadraticAttenuation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(exponent, setExponent); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cutoff, setCutoff); + + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qDebug() << "LightEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties.getLastEdited()); + } + return somethingChanged; +} + +int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight); + READ_ENTITY_PROPERTY_COLOR(PROP_DIFFUSE_COLOR, _diffuseColor); + READ_ENTITY_PROPERTY_COLOR(PROP_AMBIENT_COLOR, _ambientColor); + READ_ENTITY_PROPERTY_COLOR(PROP_SPECULAR_COLOR, _specularColor); + READ_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, float, _constantAttenuation); + READ_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, float, _linearAttenuation); + READ_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, float, _quadraticAttenuation); + READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent); + READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff); + + return bytesRead; +} + + +// TODO: eventually only include properties changed since the params.lastViewFrustumSent time +EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_IS_SPOTLIGHT; + requestedProperties += PROP_DIFFUSE_COLOR; + requestedProperties += PROP_AMBIENT_COLOR; + requestedProperties += PROP_SPECULAR_COLOR; + requestedProperties += PROP_CONSTANT_ATTENUATION; + requestedProperties += PROP_LINEAR_ATTENUATION; + requestedProperties += PROP_QUADRATIC_ATTENUATION; + requestedProperties += PROP_EXPONENT; + requestedProperties += PROP_CUTOFF; + return requestedProperties; +} + +void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, appendValue, getIsSpotlight()); + APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, getDiffuseColor()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, getAmbientColor()); + APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, getSpecularColor()); + APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, getConstantAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, getLinearAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, getQuadraticAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, getExponent()); + APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, getCutoff()); +} diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h new file mode 100644 index 0000000000..2006efb896 --- /dev/null +++ b/libraries/entities/src/LightEntityItem.h @@ -0,0 +1,118 @@ +// +// LightEntityItem.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_LightEntityItem_h +#define hifi_LightEntityItem_h + +#include +#include "EntityItem.h" + +class LightEntityItem : public EntityItem { +public: + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + LightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties() const; + virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy = false); + + 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; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData); + + const rgbColor& getAmbientColor() const { return _ambientColor; } + xColor getAmbientXColor() const { + xColor color = { _ambientColor[RED_INDEX], _ambientColor[GREEN_INDEX], _ambientColor[BLUE_INDEX] }; return color; + } + + void setAmbientColor(const rgbColor& value) { memcpy(_ambientColor, value, sizeof(_ambientColor)); } + void setAmbientColor(const xColor& value) { + _ambientColor[RED_INDEX] = value.red; + _ambientColor[GREEN_INDEX] = value.green; + _ambientColor[BLUE_INDEX] = value.blue; + } + + const rgbColor& getDiffuseColor() const { return _diffuseColor; } + xColor getDiffuseXColor() const { + xColor color = { _diffuseColor[RED_INDEX], _diffuseColor[GREEN_INDEX], _diffuseColor[BLUE_INDEX] }; return color; + } + + void setDiffuseColor(const rgbColor& value) { memcpy(_diffuseColor, value, sizeof(_diffuseColor)); } + void setDiffuseColor(const xColor& value) { + _diffuseColor[RED_INDEX] = value.red; + _diffuseColor[GREEN_INDEX] = value.green; + _diffuseColor[BLUE_INDEX] = value.blue; + } + + const rgbColor& getSpecularColor() const { return _specularColor; } + xColor getSpecularXColor() const { + xColor color = { _specularColor[RED_INDEX], _specularColor[GREEN_INDEX], _specularColor[BLUE_INDEX] }; return color; + } + + void setSpecularColor(const rgbColor& value) { memcpy(_specularColor, value, sizeof(_specularColor)); } + void setSpecularColor(const xColor& value) { + _specularColor[RED_INDEX] = value.red; + _specularColor[GREEN_INDEX] = value.green; + _specularColor[BLUE_INDEX] = value.blue; + } + + bool getIsSpotlight() const { return _isSpotlight; } + void setIsSpotlight(bool value) { _isSpotlight = value; } + + float getConstantAttenuation() const { return _constantAttenuation; } + void setConstantAttenuation(float value) { _constantAttenuation = value; } + + float getLinearAttenuation() const { return _linearAttenuation; } + void setLinearAttenuation(float value) { _linearAttenuation = value; } + + float getQuadraticAttenuation() const { return _quadraticAttenuation; } + void setQuadraticAttenuation(float value) { _quadraticAttenuation = value; } + + float getExponent() const { return _exponent; } + void setExponent(float value) { _exponent = value; } + + float getCutoff() const { return _cutoff; } + void setCutoff(float value) { _cutoff = value; } + + virtual const Shape& getCollisionShapeInMeters() const { return _emptyShape; } + +protected: + virtual void recalculateCollisionShape() { /* nothing to do */ } + + // properties of a light + rgbColor _ambientColor; + rgbColor _diffuseColor; + rgbColor _specularColor; + bool _isSpotlight; + float _constantAttenuation; + float _linearAttenuation; + float _quadraticAttenuation; + float _exponent; + float _cutoff; + + // used for collision detection + SphereShape _emptyShape; +}; + +#endif // hifi_LightEntityItem_h diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 5da218c11a..f5b8eb27e9 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -33,10 +33,7 @@ SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const Entit EntityItemProperties SphereEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class - properties.setColor(getXColor()); - properties.setGlowLevel(getGlowLevel()); - return properties; }