wip live material swapping on model entities, model overlays, avatars,

and albedo swap on shape entities
This commit is contained in:
SamGondelman 2018-02-05 18:08:36 -08:00
parent 4d4294dd6a
commit 84cd0e1529
52 changed files with 1038 additions and 251 deletions

View file

@ -22,6 +22,8 @@ setup_memory_debugger()
symlink_or_copy_directory_beside_target(${_SHOULD_SYMLINK_RESOURCES} "${CMAKE_CURRENT_SOURCE_DIR}/resources" "resources")
# link the shared hifi libraries
include_hifi_library_headers(gpu)
include_hifi_library_headers(graphics)
link_hifi_libraries(embedded-webserver networking shared avatars)
# find OpenSSL

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path d="M348.5,162.5c1.7,0,3,1.4,3,3v181.4c0,1.7-1.3,3-3,3H167.2c-1.6,0-3-1.3-3-3V165.5c0-1.6,1.4-3,3-3H348.5 M348.5,145.5
H167.2c-11,0-20,9-20,20v181.4c0,11,9,20,20,20h181.4c11,0,20-9,20-20V165.5C368.5,154.5,359.6,145.5,348.5,145.5L348.5,145.5z"/>
<rect x="161.6" y="253.6" width="96.3" height="96.3"/>
<rect x="256.1" y="159.8" width="95.4" height="95.4"/>
</svg>

After

Width:  |  Height:  |  Size: 717 B

View file

@ -135,7 +135,7 @@ TabView {
}
NewEntityButton {
icon: "icons/create-icons/94-model-01.svg"
icon: "icons/create-icons/126-material-01.svg"
text: "MATERIAL"
onClicked: {
editRoot.sendToScript({

View file

@ -1580,6 +1580,42 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
});
EntityTree::setAddMaterialToAvatarOperator([](const QUuid& avatarID, graphics::MaterialPointer material, quint16 shapeID) {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto avatar = avatarManager->getAvatarBySessionID(avatarID);
if (avatar) {
avatar->addMaterial(material, shapeID);
return true;
}
return false;
});
EntityTree::setRemoveMaterialFromAvatarOperator([](const QUuid& avatarID, graphics::MaterialPointer material, quint16 shapeID) {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto avatar = avatarManager->getAvatarBySessionID(avatarID);
if (avatar) {
avatar->removeMaterial(material, shapeID);
return true;
}
return false;
});
EntityTree::setAddMaterialToOverlayOperator([&](const QUuid& overlayID, graphics::MaterialPointer material, quint16 shapeID) {
auto overlay = _overlays.getOverlay(overlayID);
if (overlay) {
overlay->addMaterial(material, shapeID);
return true;
}
return false;
});
EntityTree::setRemoveMaterialFromOverlayOperator([&](const QUuid& overlayID, graphics::MaterialPointer material, quint16 shapeID) {
auto overlay = _overlays.getOverlay(overlayID);
if (overlay) {
overlay->removeMaterial(material, shapeID);
return true;
}
return false;
});
// Keyboard focus handling for Web overlays.
auto overlays = &(qApp->getOverlays());
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {

View file

@ -632,3 +632,29 @@ uint32_t ModelOverlay::fetchMetaSubItems(render::ItemIDs& subItems) const {
}
return 0;
}
void ModelOverlay::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
Parent::addMaterial(material, shapeID);
if (_model && _model->fetchRenderItemIDs().size() > 0) {
_model->addMaterial(material, shapeID);
}
}
void ModelOverlay::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
Parent::removeMaterial(material, shapeID);
if (_model && _model->fetchRenderItemIDs().size() > 0) {
_model->removeMaterial(material, shapeID);
}
}
void ModelOverlay::processMaterials() {
assert(_model);
std::lock_guard<std::mutex> lock(_materialsLock);
for (auto& shapeMaterialPair : _materials) {
auto material = shapeMaterialPair.second;
while (!material.empty()) {
_model->addMaterial(material.top(), shapeMaterialPair.first);
material.pop();
}
}
}

View file

@ -59,6 +59,9 @@ public:
void setDrawInFront(bool drawInFront) override;
void setDrawHUDLayer(bool drawHUDLayer) override;
void addMaterial(graphics::MaterialPointer material, quint16 shapeID) override;
void removeMaterial(graphics::MaterialPointer material, quint16 shapeID) override;
protected:
Transform evalRenderTransform() override;
@ -110,6 +113,8 @@ private:
bool _drawInFrontDirty { false };
bool _drawInHUDDirty { false };
void processMaterials();
};
#endif // hifi_ModelOverlay_h

View file

@ -235,3 +235,13 @@ QVector<OverlayID> qVectorOverlayIDFromScriptValue(const QScriptValue& array) {
}
return newVector;
}
void Overlay::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].push(material);
}
void Overlay::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].remove(material);
}

View file

@ -91,6 +91,9 @@ public:
unsigned int getStackOrder() const { return _stackOrder; }
void setStackOrder(unsigned int stackOrder) { _stackOrder = stackOrder; }
virtual void addMaterial(graphics::MaterialPointer material, quint16 shapeID);
virtual void removeMaterial(graphics::MaterialPointer material, quint16 shapeID);
protected:
float updatePulse();
@ -117,6 +120,9 @@ protected:
static const xColor DEFAULT_OVERLAY_COLOR;
static const float DEFAULT_ALPHA;
std::unordered_map<quint16, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;
private:
OverlayID _overlayID; // only used for non-3d overlays
};

View file

@ -570,6 +570,7 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc
}
transaction.resetItem(_renderItemID, avatarPayloadPointer);
_skeletonModel->addToScene(scene, transaction);
processMaterials();
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, transaction);
}
@ -761,6 +762,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
_skeletonModel->removeFromScene(scene, transaction);
_skeletonModel->addToScene(scene, transaction);
processMaterials();
canTryFade = true;
_isAnimatingScale = true;
}
@ -1760,3 +1762,31 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const {
return DEFAULT_AVATAR_EYE_HEIGHT;
}
}
void Avatar::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].push(material);
if (_skeletonModel && _skeletonModel->fetchRenderItemIDs().size() > 0) {
_skeletonModel->addMaterial(material, shapeID);
}
}
void Avatar::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].remove(material);
if (_skeletonModel && _skeletonModel->fetchRenderItemIDs().size() > 0) {
_skeletonModel->removeMaterial(material, shapeID);
}
}
void Avatar::processMaterials() {
assert(_skeletonModel);
std::lock_guard<std::mutex> lock(_materialsLock);
for (auto& shapeMaterialPair : _materials) {
auto material = shapeMaterialPair.second;
while (!material.empty()) {
_skeletonModel->addMaterial(material.top(), shapeMaterialPair.first);
material.pop();
}
}
}

View file

@ -272,6 +272,9 @@ public:
virtual void setAvatarEntityDataChanged(bool value) override;
void addMaterial(graphics::MaterialPointer material, quint16 shapeID) override;
void removeMaterial(graphics::MaterialPointer material, quint16 shapeID) override;
public slots:
// FIXME - these should be migrated to use Pose data instead
@ -397,6 +400,11 @@ protected:
float _displayNameAlpha { 1.0f };
ThreadSafeValueCache<float> _unscaledEyeHeightCache { DEFAULT_AVATAR_EYE_HEIGHT };
std::unordered_map<quint16, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;
void processMaterials();
};
#endif // hifi_Avatar_h

View file

@ -1,3 +1,4 @@
set(TARGET_NAME avatars)
setup_hifi_library(Network Script)
link_hifi_libraries(shared networking)
include_hifi_library_headers(gpu)
link_hifi_libraries(shared networking graphics)

View file

@ -2558,4 +2558,4 @@ void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap&
value[EntityID] = binaryEntityProperties;
}
}
}

View file

@ -54,6 +54,8 @@
#include "HeadData.h"
#include "PathUtils.h"
#include <graphics/Material.h>
using AvatarSharedPointer = std::shared_ptr<AvatarData>;
using AvatarWeakPointer = std::weak_ptr<AvatarData>;
using AvatarHash = QHash<QUuid, AvatarSharedPointer>;
@ -694,6 +696,9 @@ public:
bool getIsReplicated() const { return _isReplicated; }
virtual void addMaterial(graphics::MaterialPointer material, quint16 shapeID) {}
virtual void removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {}
signals:
void displayNameChanged();
void sessionDisplayNameChanged();

View file

@ -146,6 +146,9 @@ EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity
_needsRenderUpdate = true;
emit requestRenderUpdate();
});
_materials = entity->getMaterials();
connect(entity.get(), &EntityItem::addMaterialToRenderItem, this, &EntityRenderer::addMaterial);
connect(entity.get(), &EntityItem::removeMaterialFromRenderItem, this, &EntityRenderer::removeMaterial);
}
EntityRenderer::~EntityRenderer() { }
@ -399,4 +402,14 @@ void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
void EntityRenderer::onRemoveFromScene(const EntityItemPointer& entity) {
entity->deregisterChangeHandler(_changeHandlerId);
QObject::disconnect(this, &EntityRenderer::requestRenderUpdate, this, nullptr);
}
void EntityRenderer::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].push(material);
}
void EntityRenderer::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].remove(material);
}

View file

@ -101,6 +101,10 @@ protected:
return result;
}
public slots:
virtual void addMaterial(graphics::MaterialPointer material, quint16 shapeID);
virtual void removeMaterial(graphics::MaterialPointer material, quint16 shapeID);
signals:
void requestRenderUpdate();
@ -129,6 +133,8 @@ protected:
// Only touched on the rendering thread
bool _renderUpdateQueued{ false };
std::unordered_map<quint16, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;
private:
// The base class relies on comparing the model transform to the entity transform in order

View file

@ -15,12 +15,18 @@ bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityP
if (entity->getMaterial() != _drawMaterial) {
return true;
}
if (entity->getParentID() != _parentID || entity->getClientOnly() != _clientOnly || entity->getOwningAvatarID() != _owningAvatarID) {
return true;
}
return false;
}
void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
withWriteLock([&] {
_drawMaterial = entity->getMaterial();
_parentID = entity->getParentID();
_clientOnly = entity->getClientOnly();
_owningAvatarID = entity->getOwningAvatarID();
_renderTransform = getModelTransform();
const float MATERIAL_ENTITY_SCALE = 0.5f;
_renderTransform.postScale(MATERIAL_ENTITY_SCALE);
@ -30,7 +36,7 @@ void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
ItemKey MaterialEntityRenderer::getKey() {
ItemKey::Builder builder;
builder.withTypeShape();
builder.withTypeShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
if (!_visible) {
builder.withInvisible();
@ -215,13 +221,25 @@ void MaterialEntityRenderer::generateMesh() {
void MaterialEntityRenderer::doRender(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableMaterialEntityItem::render");
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(_renderTransform);
// Don't render if our parent is set or our material is null
QUuid parentID;
Transform renderTransform;
graphics::MaterialPointer drawMaterial;
withReadLock([&] {
parentID = _clientOnly ? _owningAvatarID : _parentID;
renderTransform = _renderTransform;
drawMaterial = _drawMaterial;
});
if (!parentID.isNull() || !drawMaterial) {
return;
}
batch.setModelTransform(renderTransform);
// bind the material
args->_shapePipeline->bindMaterial(_drawMaterial, batch, args->_enableTexturing);
args->_shapePipeline->bindMaterial(drawMaterial, batch, args->_enableTexturing);
args->_details._materialSwitches++;
// Draw!

View file

@ -31,6 +31,9 @@ private:
ItemKey getKey() override;
ShapeKey getShapeKey() override;
QUuid _parentID;
bool _clientOnly;
QUuid _owningAvatarID;
Transform _renderTransform;
std::shared_ptr<NetworkMaterial> _drawMaterial;
@ -47,7 +50,7 @@ private:
static void addVertex(std::vector<float>& buffer, const glm::vec3& pos, const glm::vec3& tan, const glm::vec2 uv);
const int SLICES = 15;
const int STACKS = 9;
const float M_PI_TIMES_2 = 2.0f * M_PI;
const float M_PI_TIMES_2 = 2.0f * (float)M_PI;
};
} }

View file

@ -1380,6 +1380,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
auto entityRenderer = static_cast<EntityRenderer*>(&data);
entityRenderer->setSubRenderItemIDs(newRenderItemIDs);
});
processMaterials();
}
}
@ -1466,3 +1467,28 @@ void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStr
}
}
void ModelEntityRenderer::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
Parent::addMaterial(material, shapeID);
if (_model && _model->fetchRenderItemIDs().size() > 0) {
_model->addMaterial(material, shapeID);
}
}
void ModelEntityRenderer::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
Parent::removeMaterial(material, shapeID);
if (_model && _model->fetchRenderItemIDs().size() > 0) {
_model->removeMaterial(material, shapeID);
}
}
void ModelEntityRenderer::processMaterials() {
assert(_model);
std::lock_guard<std::mutex> lock(_materialsLock);
for (auto& shapeMaterialPair : _materials) {
auto material = shapeMaterialPair.second;
while (!material.empty()) {
_model->addMaterial(material.top(), shapeMaterialPair.first);
material.pop();
}
}
}

View file

@ -138,10 +138,15 @@ namespace render { namespace entities {
class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem> {
using Parent = TypedEntityRenderer<RenderableModelEntityItem>;
friend class EntityRenderer;
Q_OBJECT
public:
ModelEntityRenderer(const EntityItemPointer& entity);
public slots:
void addMaterial(graphics::MaterialPointer material, quint16 shapeID) override;
void removeMaterial(graphics::MaterialPointer material, quint16 shapeID) override;
protected:
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
@ -194,6 +199,8 @@ private:
uint64_t _lastAnimated { 0 };
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
void processMaterials();
};
} } // namespace

View file

@ -54,8 +54,7 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
if (_lastUserData != entity->getUserData()) {
return true;
}
glm::vec4 newColor(toGlm(entity->getXColor()), entity->getLocalRenderAlpha());
if (newColor != _color) {
if (_material != entity->getMaterial()) {
return true;
}
@ -78,7 +77,9 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
_procedural.setProceduralData(ProceduralData::parse(_lastUserData));
}
_color = vec4(toGlm(entity->getXColor()), entity->getLocalRenderAlpha());
removeMaterial(_material, 0);
_material = entity->getMaterial();
addMaterial(_material, 0);
_shape = entity->getShape();
_position = entity->getWorldPosition();
@ -112,14 +113,13 @@ bool ShapeEntityRenderer::isTransparent() const {
return Parent::isTransparent();
}
void ShapeEntityRenderer::doRender(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
std::shared_ptr<graphics::Material> mat;
auto geometryCache = DependencyManager::get<GeometryCache>();
GeometryCache::Shape geometryShape;
bool proceduralRender = false;
@ -127,15 +127,22 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
withReadLock([&] {
geometryShape = geometryCache->getShapeForEntityShape(_shape);
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
outColor = _color;
if (_procedural.isReady()) {
_procedural.prepare(batch, _position, _dimensions, _orientation);
outColor = _procedural.getColor(_color);
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
proceduralRender = true;
mat = _materials[0].top();
if (mat) {
outColor = glm::vec4(mat->getAlbedo(), mat->getOpacity());
if (_procedural.isReady()) {
_procedural.prepare(batch, _position, _dimensions, _orientation);
outColor = _procedural.getColor(outColor);
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
proceduralRender = true;
}
}
});
if (!mat) {
return;
}
if (proceduralRender) {
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
geometryCache->renderWireShape(batch, geometryShape, outColor);

View file

@ -34,7 +34,7 @@ private:
QString _lastUserData;
Transform _renderTransform;
entity::Shape _shape { entity::Sphere };
glm::vec4 _color;
std::shared_ptr<graphics::Material> _material;
glm::vec3 _position;
glm::vec3 _dimensions;
glm::quat _orientation;

View file

@ -93,7 +93,7 @@ bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) {
// and we can stop searching.
if (entityTreeElement == details.containingElement) {
EntityItemPointer theEntity = details.entity;
bool entityDeleted = entityTreeElement->removeEntityItem(theEntity); // remove it from the element
bool entityDeleted = entityTreeElement->removeEntityItem(theEntity, true); // remove it from the element
assert(entityDeleted);
(void)entityDeleted; // quite warning
_tree->clearEntityMapEntry(details.entity->getEntityItemID());

View file

@ -60,14 +60,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
}
EntityItem::~EntityItem() {
// clear out any left-over actions
EntityTreeElementPointer element = _element; // use local copy of _element for logic below
EntityTreePointer entityTree = element ? element->getTree() : nullptr;
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
if (simulation) {
clearActions(simulation);
}
// these pointers MUST be correct at delete, else we probably have a dangling backpointer
// to this EntityItem in the corresponding data structure.
assert(!_simulated);
@ -2937,3 +2929,34 @@ void EntityItem::retrieveMarketplacePublicKey() {
networkReply->deleteLater();
});
}
void EntityItem::preDelete() {
// clear out any left-over actions
EntityTreeElementPointer element = _element; // use local copy of _element for logic below
EntityTreePointer entityTree = element ? element->getTree() : nullptr;
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
if (simulation) {
clearActions(simulation);
}
}
void EntityItem::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].push(material);
emit addMaterialToRenderItem(material, shapeID);
}
void EntityItem::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[shapeID].remove(material);
emit removeMaterialFromRenderItem(material, shapeID);
}
std::unordered_map<quint16, graphics::MultiMaterial> EntityItem::getMaterials() {
std::unordered_map<quint16, graphics::MultiMaterial> toReturn;
{
std::lock_guard<std::mutex> lock(_materialsLock);
toReturn = _materials;
}
return toReturn;
}

View file

@ -36,6 +36,8 @@
#include "SimulationFlags.h"
#include "EntityDynamicInterface.h"
#include "graphics/Material.h"
class EntitySimulation;
class EntityTreeElement;
class EntityTreeElementExtraEncodeData;
@ -49,7 +51,6 @@ typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { };
@ -443,10 +444,10 @@ public:
void scriptHasUnloaded() { _loadedScript = ""; _loadedScriptTimestamp = 0; }
bool getClientOnly() const { return _clientOnly; }
void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; }
virtual void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; }
// if this entity is client-only, which avatar is it associated with?
QUuid getOwningAvatarID() const { return _owningAvatarID; }
void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
virtual void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
virtual bool wantsHandControllerPointerEvents() const { return false; }
virtual bool wantsKeyboardFocus() const { return false; }
@ -477,8 +478,18 @@ public:
void setCauterized(bool value) { _cauterized = value; }
bool getCauterized() const { return _cauterized; }
virtual void postAdd() {}
virtual void preDelete();
virtual void postParentFixup() {}
void addMaterial(graphics::MaterialPointer material, quint16 shapeID);
void removeMaterial(graphics::MaterialPointer material, quint16 shapeID);
std::unordered_map<quint16, graphics::MultiMaterial> getMaterials();
signals:
void requestRenderUpdate();
void addMaterialToRenderItem(graphics::MaterialPointer material, quint16 shapeID);
void removeMaterialFromRenderItem(graphics::MaterialPointer material, quint16 shapeID);
protected:
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
@ -631,6 +642,11 @@ protected:
quint64 _lastUpdatedQueryAACubeTimestamp { 0 };
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
private:
std::unordered_map<quint16, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;
};
#endif // hifi_EntityItem_h

View file

@ -360,7 +360,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_BLEND_FACTOR, blendFactor);
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_PRIORITY, priority);
CHECK_PROPERTY_CHANGE(PROP_PARENT_SHAPE_ID, shapeID);
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_BOUNDS, materialBounds);
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_POS, materialPos);
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_SCALE, materialScale);
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_ROT, materialRot);
// Certifiable Properties
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
@ -664,7 +666,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_BLEND_FACTOR, blendFactor);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_PRIORITY, priority);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_SHAPE_ID, shapeID);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_BOUNDS, materialBounds);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_POS, materialPos);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_SCALE, materialScale);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_ROT, materialRot);
}
if (!skipDefaults && !strictSemantics) {
@ -801,11 +805,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish);
COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints);
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialURL, QString, setMaterialURL);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(materialMode, MaterialMode, setMaterialMode);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(materialMode, MaterialMode);
COPY_PROPERTY_FROM_QSCRIPTVALUE(blendFactor, float, setBlendFactor);
COPY_PROPERTY_FROM_QSCRIPTVALUE(priority, int, setPriority);
COPY_PROPERTY_FROM_QSCRIPTVALUE(shapeID, int, setShapeID);
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialBounds, glmVec4, setMaterialBounds);
COPY_PROPERTY_FROM_QSCRIPTVALUE(priority, quint16, setPriority);
COPY_PROPERTY_FROM_QSCRIPTVALUE(shapeID, quint16, setShapeID);
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialPos, glmVec2, setMaterialPos);
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialScale, glmVec2, setMaterialScale);
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialRot, float, setMaterialRot);
// Certifiable Properties
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
@ -1163,9 +1169,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_URL, MaterialURL, materialURL, QString);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_TYPE, MaterialMode, materialMode, MaterialMode);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_BLEND_FACTOR, BlendFactor, blendFactor, float);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_PRIORITY, Priority, priority, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_PARENT_SHAPE_ID, ShapeID, shapeID, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_BOUNDS, MaterialBounds, materialBounds, glmVec4);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_PRIORITY, Priority, priority, quint16);
ADD_PROPERTY_TO_MAP(PROP_PARENT_SHAPE_ID, ShapeID, shapeID, quint16);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_POS, MaterialPos, materialPos, glmVec2);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_SCALE, MaterialScale, materialScale, glmVec2);
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_ROT, MaterialRot, materialRot, float);
// Certifiable Properties
ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString);
@ -1557,7 +1565,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, properties.getBlendFactor());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, properties.getPriority());
APPEND_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, properties.getShapeID());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BOUNDS, properties.getMaterialBounds());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_POS, properties.getMaterialPos());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_SCALE, properties.getMaterialScale());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_ROT, properties.getMaterialRot());
}
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
@ -1921,9 +1931,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_URL, QString, setMaterialURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_TYPE, MaterialMode, setMaterialMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_BLEND_FACTOR, float, setBlendFactor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_PRIORITY, uint32_t, setPriority);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_SHAPE_ID, uint32_t, setShapeID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_BOUNDS, glmVec4, setMaterialBounds);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_PRIORITY, quint16, setPriority);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_SHAPE_ID, quint16, setShapeID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_POS, glmVec2, setMaterialPos);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_SCALE, glmVec2, setMaterialScale);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_ROT, float, setMaterialRot);
}
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
@ -2104,7 +2116,9 @@ void EntityItemProperties::markAllChanged() {
_blendFactorChanged = true;
_priorityChanged = true;
_shapeIDChanged = true;
_materialBoundsChanged = true;
_materialPosChanged = true;
_materialScaleChanged = true;
_materialRotChanged = true;
// Certifiable Properties
_itemNameChanged = true;
@ -2444,8 +2458,14 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (shapeIDChanged()) {
out += "shapeID";
}
if (materialBoundsChanged()) {
out += "materialBounds";
if (materialPosChanged()) {
out += "materialPos";
}
if (materialScaleChanged()) {
out += "materialScale";
}
if (materialRotChanged()) {
out += "materialRot";
}
// Certifiable Properties

View file

@ -225,9 +225,11 @@ public:
DEFINE_PROPERTY_REF(PROP_MATERIAL_URL, MaterialURL, materialURL, QString, "");
DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_TYPE, MaterialMode, materialMode, MaterialMode, UV);
DEFINE_PROPERTY_REF(PROP_MATERIAL_BLEND_FACTOR, BlendFactor, blendFactor, float, 1.0f);
DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, uint32_t, 0);
DEFINE_PROPERTY_REF(PROP_PARENT_SHAPE_ID, ShapeID, shapeID, uint32_t, 0);
DEFINE_PROPERTY_REF(PROP_MATERIAL_BOUNDS, MaterialBounds, materialBounds, glmVec4, glm::vec4(0, 0, 1, 1));
DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, quint16, 0);
DEFINE_PROPERTY_REF(PROP_PARENT_SHAPE_ID, ShapeID, shapeID, quint16, 0);
DEFINE_PROPERTY_REF(PROP_MATERIAL_POS, MaterialPos, materialPos, glmVec2, glm::vec2(0, 0));
DEFINE_PROPERTY_REF(PROP_MATERIAL_SCALE, MaterialScale, materialScale, glmVec2, glm::vec2(1, 1));
DEFINE_PROPERTY_REF(PROP_MATERIAL_ROT, MaterialRot, materialRot, float, 0);
// Certifiable Properties - related to Proof of Purchase certificates
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);

View file

@ -101,6 +101,7 @@
changedProperties += P; \
}
inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec2& v) { return vec2toScriptValue(e, v); }
inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3toScriptValue(e, v); }
inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScriptValue(v); }
inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); }
@ -183,8 +184,8 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu
properties.setProperty(#P, V); \
}
typedef glm::vec2 glmVec2;
typedef glm::vec3 glmVec3;
typedef glm::vec4 glmVec4;
typedef glm::quat glmQuat;
typedef QVector<glm::vec3> qVectorVec3;
typedef QVector<glm::quat> qVectorQuat;
@ -222,6 +223,23 @@ inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool&
return QByteArray::fromBase64(b64.toUtf8());
}
inline glmVec2 glmVec2_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
isValid = false; /// assume it can't be converted
QScriptValue x = v.property("x");
QScriptValue y = v.property("y");
if (x.isValid() && y.isValid()) {
glm::vec4 newValue(0);
newValue.x = x.toVariant().toFloat();
newValue.y = y.toVariant().toFloat();
isValid = !glm::isnan(newValue.x) &&
!glm::isnan(newValue.y);
if (isValid) {
return newValue;
}
}
return glm::vec2(0);
}
inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
isValid = false; /// assume it can't be converted
QScriptValue x = v.property("x");
@ -242,29 +260,6 @@ inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isVal
return glm::vec3(0);
}
inline glmVec4 glmVec4_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
isValid = false; /// assume it can't be converted
QScriptValue x = v.property("x");
QScriptValue y = v.property("y");
QScriptValue z = v.property("z");
QScriptValue w = v.property("w");
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
glm::vec4 newValue(0);
newValue.x = x.toVariant().toFloat();
newValue.y = y.toVariant().toFloat();
newValue.z = z.toVariant().toFloat();
newValue.w = w.toVariant().toFloat();
isValid = !glm::isnan(newValue.x) &&
!glm::isnan(newValue.y) &&
!glm::isnan(newValue.z) &&
!glm::isnan(newValue.w);
if (isValid) {
return newValue;
}
}
return glm::vec4(0);
}
inline AACube AACube_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
isValid = true;
AACube result;

View file

@ -232,7 +232,9 @@ enum EntityPropertyList {
PROP_MATERIAL_BLEND_FACTOR,
PROP_MATERIAL_PRIORITY,
PROP_PARENT_SHAPE_ID,
PROP_MATERIAL_BOUNDS,
PROP_MATERIAL_POS,
PROP_MATERIAL_SCALE,
PROP_MATERIAL_ROT,
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line

View file

@ -1732,15 +1732,19 @@ void EntityTree::fixupNeedsParentFixups() {
}
});
entity->locationChanged(true);
} else if (getIsServer() && _avatarIDs.contains(entity->getParentID())) {
// this is a child of an avatar, which the entity server will never have
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
if (!_childrenOfAvatars.contains(entity->getParentID())) {
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
entity->postParentFixup();
} else if (_avatarIDs.contains(entity->getParentID())) {
if (getIsServer()) {
// this is a child of an avatar, which the entity server will never have
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
if (!_childrenOfAvatars.contains(entity->getParentID())) {
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
}
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
}
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
doMove = true;
iter.remove(); // and pull it out of the list
entity->postParentFixup();
}
if (queryAACubeSuccess && doMove) {
@ -2378,3 +2382,35 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const {
return entity->getJointNames();
}
std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> EntityTree::_addMaterialToAvatarOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> EntityTree::_removeMaterialFromAvatarOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> EntityTree::_addMaterialToOverlayOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> EntityTree::_removeMaterialFromOverlayOperator = nullptr;
bool EntityTree::addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialPointer material, quint16 shapeID) {
if (_addMaterialToAvatarOperator) {
return _addMaterialToAvatarOperator(avatarID, material, shapeID);
}
return false;
}
bool EntityTree::removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, quint16 shapeID) {
if (_removeMaterialFromAvatarOperator) {
return _removeMaterialFromAvatarOperator(avatarID, material, shapeID);
}
return false;
}
bool EntityTree::addMaterialToOverlay(const QUuid& overlayID, graphics::MaterialPointer material, quint16 shapeID) {
if (_addMaterialToOverlayOperator) {
return _addMaterialToOverlayOperator(overlayID, material, shapeID);
}
return false;
}
bool EntityTree::removeMaterialFromOverlay(const QUuid& overlayID, graphics::MaterialPointer material, quint16 shapeID) {
if (_removeMaterialFromOverlayOperator) {
return _removeMaterialFromOverlayOperator(overlayID, material, shapeID);
}
return false;
}

View file

@ -280,6 +280,16 @@ public:
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
static void setAddMaterialToAvatarOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> addMaterialToAvatarOperator) { _addMaterialToAvatarOperator = addMaterialToAvatarOperator; }
static void setRemoveMaterialFromAvatarOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> removeMaterialFromAvatarOperator) { _removeMaterialFromAvatarOperator = removeMaterialFromAvatarOperator; }
static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialPointer material, quint16 shapeID);
static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, quint16 shapeID);
static void setAddMaterialToOverlayOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> addMaterialToOverlayOperator) { _addMaterialToOverlayOperator = addMaterialToOverlayOperator; }
static void setRemoveMaterialFromOverlayOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> removeMaterialFromOverlayOperator) { _removeMaterialFromOverlayOperator = removeMaterialFromOverlayOperator; }
static bool addMaterialToOverlay(const QUuid& overlayID, graphics::MaterialPointer material, quint16 shapeID);
static bool removeMaterialFromOverlay(const QUuid& overlayID, graphics::MaterialPointer material, quint16 shapeID);
signals:
void deletingEntity(const EntityItemID& entityID);
void deletingEntityPointer(EntityItem* entityID);
@ -387,6 +397,11 @@ private:
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation);
std::shared_ptr<AvatarData> _myAvatar{ nullptr };
static std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> _addMaterialToAvatarOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> _removeMaterialFromAvatarOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> _addMaterialToOverlayOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, quint16)> _removeMaterialFromOverlayOperator;
};
#endif // hifi_EntityTree_h

View file

@ -896,6 +896,7 @@ EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemI
void EntityTreeElement::cleanupEntities() {
withWriteLock([&] {
foreach(EntityItemPointer entity, _entityItems) {
entity->preDelete();
// NOTE: only EntityTreeElement should ever be changing the value of entity->_element
// NOTE: We explicitly don't delete the EntityItem here because since we only
// access it by smart pointers, when we remove it from the _entityItems
@ -907,26 +908,10 @@ void EntityTreeElement::cleanupEntities() {
bumpChangedContent();
}
bool EntityTreeElement::removeEntityWithEntityItemID(const EntityItemID& id) {
bool foundEntity = false;
withWriteLock([&] {
uint16_t numberOfEntities = _entityItems.size();
for (uint16_t i = 0; i < numberOfEntities; i++) {
EntityItemPointer& entity = _entityItems[i];
if (entity->getEntityItemID() == id) {
foundEntity = true;
// NOTE: only EntityTreeElement should ever be changing the value of entity->_element
entity->_element = NULL;
_entityItems.removeAt(i);
bumpChangedContent();
break;
}
}
});
return foundEntity;
}
bool EntityTreeElement::removeEntityItem(EntityItemPointer entity) {
bool EntityTreeElement::removeEntityItem(EntityItemPointer entity, bool deletion) {
if (deletion) {
entity->preDelete();
}
int numEntries = 0;
withWriteLock([&] {
numEntries = _entityItems.removeAll(entity);
@ -955,6 +940,7 @@ void EntityTreeElement::addEntityItem(EntityItemPointer entity) {
});
bumpChangedContent();
entity->_element = getThisPointer();
entity->postAdd();
}
// will average a "common reduced LOD view" from the the child elements...

View file

@ -210,8 +210,7 @@ public:
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
bool removeEntityWithEntityItemID(const EntityItemID& id);
bool removeEntityItem(EntityItemPointer entity);
bool removeEntityItem(EntityItemPointer entity, bool deletion = false);
bool containsEntityBounds(EntityItemPointer entity) const;
bool bestFitEntityBounds(EntityItemPointer entity) const;

View file

@ -16,6 +16,9 @@
EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
Pointer entity(new MaterialEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
entity->setProperties(properties);
// When you reload content, setProperties doesn't have any of the propertiesChanged flags set, so it won't trigger a material add
entity->removeMaterial();
entity->applyMaterial();
return entity;
}
@ -31,7 +34,9 @@ EntityItemProperties MaterialEntityItem::getProperties(EntityPropertyFlags desir
COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendFactor, getBlendFactor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(priority, getPriority);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeID, getShapeID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialBounds, getMaterialBounds);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialPos, getMaterialPos);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialScale, getMaterialScale);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialRot, getMaterialRot);
return properties;
}
@ -43,7 +48,9 @@ bool MaterialEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendFactor, setBlendFactor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(priority, setPriority);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeID, setShapeID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialBounds, setMaterialBounds);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialPos, setMaterialPos);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialScale, setMaterialScale);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialRot, setMaterialRot);
if (somethingChanged) {
bool wantDebug = false;
@ -202,9 +209,11 @@ int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
READ_ENTITY_PROPERTY(PROP_MATERIAL_URL, QString, setMaterialURL);
READ_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, MaterialMode, setMaterialMode);
READ_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, float, setBlendFactor);
READ_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, uint32_t, setPriority);
READ_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, uint32_t, setShapeID);
READ_ENTITY_PROPERTY(PROP_MATERIAL_BOUNDS, glm::vec4, setMaterialBounds);
READ_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, quint16, setPriority);
READ_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, quint16, setShapeID);
READ_ENTITY_PROPERTY(PROP_MATERIAL_POS, glm::vec2, setMaterialPos);
READ_ENTITY_PROPERTY(PROP_MATERIAL_SCALE, glm::vec2, setMaterialScale);
READ_ENTITY_PROPERTY(PROP_MATERIAL_ROT, float, setMaterialRot);
return bytesRead;
}
@ -218,7 +227,9 @@ EntityPropertyFlags MaterialEntityItem::getEntityProperties(EncodeBitstreamParam
requestedProperties += PROP_MATERIAL_BLEND_FACTOR;
requestedProperties += PROP_MATERIAL_PRIORITY;
requestedProperties += PROP_PARENT_SHAPE_ID;
requestedProperties += PROP_MATERIAL_BOUNDS;
requestedProperties += PROP_MATERIAL_POS;
requestedProperties += PROP_MATERIAL_SCALE;
requestedProperties += PROP_MATERIAL_ROT;
return requestedProperties;
}
@ -234,9 +245,11 @@ void MaterialEntityItem::appendSubclassData(OctreePacketData* packetData, Encode
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_URL, getMaterialURL());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, (uint32_t)getMaterialMode());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, getBlendFactor());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, (uint32_t)getPriority());
APPEND_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, (uint32_t)getShapeID());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BOUNDS, getMaterialBounds());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, getPriority());
APPEND_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, getShapeID());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_POS, getMaterialPos());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_SCALE, getMaterialScale());
APPEND_ENTITY_PROPERTY(PROP_MATERIAL_ROT, getMaterialRot());
}
void MaterialEntityItem::debugDump() const {
@ -249,7 +262,9 @@ void MaterialEntityItem::debugDump() const {
qCDebug(entities) << " blend factor:" << _blendFactor;
qCDebug(entities) << " priority:" << _priority;
qCDebug(entities) << " parent shape ID:" << _shapeID;
qCDebug(entities) << " material bounds:" << _materialBounds;
qCDebug(entities) << " material pos:" << _materialPos;
qCDebug(entities) << " material scale:" << _materialRot;
qCDebug(entities) << " material rot:" << _materialScale;
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
@ -269,30 +284,38 @@ std::shared_ptr<NetworkMaterial> MaterialEntityItem::getMaterial() const {
}
}
void MaterialEntityItem::setMaterialURL(const QString& materialURLString) {
if (materialURLString.startsWith("userData")) {
QJsonDocument materialJSON = QJsonDocument::fromJson(getUserData().toUtf8());
_materials.clear();
_materialNames.clear();
if (!materialJSON.isNull()) {
if (materialJSON.isArray()) {
QJsonArray materials = materialJSON.array();
for (auto& material : materials) {
if (!material.isNull() && material.isObject()) {
parseJSONMaterial(material.toObject());
void MaterialEntityItem::setMaterialURL(const QString& materialURLString, bool userDataChanged) {
bool usingUserData = materialURLString.startsWith("userData");
if (_materialURL != materialURLString || (usingUserData && userDataChanged)) {
removeMaterial();
_materialURL = materialURLString;
if (usingUserData) {
QJsonDocument materialJSON = QJsonDocument::fromJson(getUserData().toUtf8());
_materials.clear();
_materialNames.clear();
if (!materialJSON.isNull()) {
if (materialJSON.isArray()) {
QJsonArray materials = materialJSON.array();
for (auto material : materials) {
if (!material.isNull() && material.isObject()) {
parseJSONMaterial(material.toObject());
}
}
} else if (materialJSON.isObject()) {
parseJSONMaterial(materialJSON.object());
}
} else if (materialJSON.isObject()) {
parseJSONMaterial(materialJSON.object());
}
} else {
// get material via network request
}
// TODO: if URL ends with ?string, try to set _currentMaterialName = string
// Since our JSON changed, the current name might not be valid anymore, so we need to update
setCurrentMaterialName(_currentMaterialName);
applyMaterial();
}
_materialURL = materialURLString;
// TODO: if URL ends with ?string, set _currentMaterialName = string
// Since our JSON changed, the current name might not be valid anymore, so we need to update
setCurrentMaterialName(_currentMaterialName);
}
void MaterialEntityItem::setCurrentMaterialName(const QString& currentMaterialName) {
@ -300,14 +323,176 @@ void MaterialEntityItem::setCurrentMaterialName(const QString& currentMaterialNa
if (material != _materials.end()) {
_currentMaterialName = currentMaterialName;
} else if (_materialNames.size() > 0) {
setCurrentMaterialName(_materialNames[0]);
_currentMaterialName = _materialNames[0];
}
}
void MaterialEntityItem::setUserData(const QString& userData) {
EntityItem::setUserData(userData);
if (_materialURL.startsWith("userData")) {
// Trigger material update when user data changes
setMaterialURL(_materialURL);
if (_userData != userData) {
EntityItem::setUserData(userData);
if (_materialURL.startsWith("userData")) {
// Trigger material update when user data changes
setMaterialURL(_materialURL, true);
}
}
}
void MaterialEntityItem::setMaterialPos(const glm::vec2& materialPos) {
if (_materialPos != materialPos) {
removeMaterial();
_materialPos = materialPos;
applyMaterial();
}
}
void MaterialEntityItem::setMaterialScale(const glm::vec2& materialScale) {
if (_materialScale != materialScale) {
removeMaterial();
_materialScale = materialScale;
applyMaterial();
}
}
void MaterialEntityItem::setMaterialRot(const float& materialRot) {
if (_materialRot != materialRot) {
removeMaterial();
_materialRot = materialRot;
applyMaterial();
}
}
void MaterialEntityItem::setBlendFactor(float blendFactor) {
if (_blendFactor != blendFactor) {
removeMaterial();
_blendFactor = blendFactor;
applyMaterial();
}
}
void MaterialEntityItem::setPriority(quint16 priority) {
if (_priority != priority) {
removeMaterial();
_priority = priority;
applyMaterial();
}
}
void MaterialEntityItem::setShapeID(quint16 shapeID) {
if (_shapeID != shapeID) {
removeMaterial();
_shapeID = shapeID;
applyMaterial();
}
}
void MaterialEntityItem::setParentID(const QUuid& parentID) {
if (getParentID() != parentID) {
removeMaterial();
EntityItem::setParentID(parentID);
applyMaterial();
}
}
void MaterialEntityItem::setClientOnly(bool clientOnly) {
if (getClientOnly() != clientOnly) {
removeMaterial();
EntityItem::setClientOnly(clientOnly);
applyMaterial();
}
}
void MaterialEntityItem::setOwningAvatarID(const QUuid& owningAvatarID) {
if (getOwningAvatarID() != owningAvatarID) {
removeMaterial();
EntityItem::setOwningAvatarID(owningAvatarID);
applyMaterial();
}
}
void MaterialEntityItem::removeMaterial() {
graphics::MaterialPointer material = getMaterial();
QUuid parentID = getClientOnly() ? getOwningAvatarID() : getParentID();
if (!material || parentID.isNull()) {
return;
}
// Our parent could be an entity, an avatar, or an overlay
EntityTreePointer tree = getTree();
if (tree) {
EntityItemPointer entity = tree->findEntityByEntityItemID(parentID);
if (entity) {
entity->removeMaterial(material, getShapeID());
return;
}
}
if (EntityTree::removeMaterialFromAvatar(parentID, material, getShapeID())) {
return;
}
if (EntityTree::removeMaterialFromOverlay(parentID, material, getShapeID())) {
return;
}
// if a remove fails, our parent is gone, so we don't need to retry
}
void MaterialEntityItem::applyMaterial() {
_retryApply = false;
graphics::MaterialPointer material = getMaterial();
QUuid parentID = getClientOnly() ? getOwningAvatarID() : getParentID();
if (!material || parentID.isNull()) {
return;
}
Transform textureTransform;
textureTransform.setTranslation(glm::vec3(_materialPos, 0));
textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialRot)));
textureTransform.setScale(glm::vec3(_materialScale, 1));
material->setTextureTransforms(textureTransform);
material->setBlendFactor(getBlendFactor());
material->setPriority(getPriority());
// Our parent could be an entity, an avatar, or an overlay
EntityTreePointer tree = getTree();
if (tree) {
EntityItemPointer entity = tree->findEntityByEntityItemID(parentID);
if (entity) {
entity->addMaterial(material, getShapeID());
return;
}
}
if (EntityTree::addMaterialToAvatar(parentID, material, getShapeID())) {
return;
}
if (EntityTree::addMaterialToOverlay(parentID, material, getShapeID())) {
return;
}
// if we've reached this point, we couldn't find our parent, so we need to try again later
_retryApply = true;
}
void MaterialEntityItem::postAdd() {
removeMaterial();
applyMaterial();
}
void MaterialEntityItem::preDelete() {
EntityItem::preDelete();
removeMaterial();
}
void MaterialEntityItem::postParentFixup() {
removeMaterial();
applyMaterial();
}
void MaterialEntityItem::update(const quint64& now) {
if (_retryApply) {
applyMaterial();
}
EntityItem::update(now);
}

View file

@ -23,6 +23,9 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
void update(const quint64& now) override;
bool needsToCallUpdate() const override { return true; }
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
@ -49,7 +52,7 @@ public:
virtual void setUnscaledDimensions(const glm::vec3& value) override;
QString getMaterialURL() const { return _materialURL; }
void setMaterialURL(const QString& materialURLString);
void setMaterialURL(const QString& materialURLString, bool userDataChanged = false);
QString getCurrentMaterialName() const { return _currentMaterialName; }
void setCurrentMaterialName(const QString& currentMaterialName);
@ -58,33 +61,51 @@ public:
void setMaterialMode(MaterialMode mode) { _materialMode = mode; }
float getBlendFactor() const { return _blendFactor; }
void setBlendFactor(float blendFactor) { _blendFactor = blendFactor; }
void setBlendFactor(float blendFactor);
int getPriority() const { return _priority; }
void setPriority(int priority) { _priority = priority; }
quint16 getPriority() const { return _priority; }
void setPriority(quint16 priority);
int getShapeID() const { return _shapeID; }
void setShapeID(int shapeID) { _shapeID = shapeID; }
quint16 getShapeID() const { return _shapeID; }
void setShapeID(quint16 shapeID);
glm::vec4 getMaterialBounds() const { return _materialBounds; }
void setMaterialBounds(const glm::vec4& materialBounds) { _materialBounds = materialBounds; }
glm::vec2 getMaterialPos() const { return _materialPos; }
void setMaterialPos(const glm::vec2& materialPos);
glm::vec2 getMaterialScale() const { return _materialScale; }
void setMaterialScale(const glm::vec2& materialScale);
float getMaterialRot() const { return _materialRot; }
void setMaterialRot(const float& materialRot);
std::shared_ptr<NetworkMaterial> getMaterial() const;
void setUserData(const QString& userData) override;
void setParentID(const QUuid& parentID) override;
void setClientOnly(bool clientOnly) override;
void setOwningAvatarID(const QUuid& owningAvatarID) override;
void applyMaterial();
void removeMaterial();
void postAdd() override;
void preDelete() override;
void postParentFixup() override;
private:
QString _materialURL;
MaterialMode _materialMode { UV };
float _blendFactor { 1.0f };
int _priority { 0 };
int _shapeID { 0 };
glm::vec4 _materialBounds { 0, 0, 1, 1 };
quint16 _priority { 0 };
quint16 _shapeID { 0 };
glm::vec2 _materialPos { 0, 0 };
glm::vec2 _materialScale { 1, 1 };
float _materialRot { 0 };
QHash<QString, std::shared_ptr<NetworkMaterial>> _materials;
std::vector<QString> _materialNames;
QString _currentMaterialName;
bool _retryApply { false };
void parseJSONMaterial(const QJsonObject& materialJSON);
static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB);

View file

@ -721,4 +721,4 @@ bool ModelEntityItem::isAnimatingSomething() const {
_animationProperties.getRunning() &&
(_animationProperties.getFPS() != 0.0f);
});
}
}

View file

@ -85,6 +85,7 @@ EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, c
ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
_type = EntityTypes::Shape;
_volumeMultiplier *= PI / 6.0f;
_material = std::make_shared<graphics::Material>();
}
EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
@ -184,6 +185,7 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
void ShapeEntityItem::setColor(const rgbColor& value) {
memcpy(_color, value, sizeof(rgbColor));
_material->setAlbedo(glm::vec3(_color[0], _color[1], _color[2]) / 255.0f);
}
xColor ShapeEntityItem::getXColor() const {
@ -204,6 +206,11 @@ void ShapeEntityItem::setColor(const QColor& value) {
setAlpha(value.alpha());
}
void ShapeEntityItem::setAlpha(float alpha) {
_alpha = alpha;
_material->setOpacity(alpha);
}
void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) {
const float MAX_FLAT_DIMENSION = 0.0001f;
if ((_shape == entity::Shape::Circle || _shape == entity::Shape::Quad) && value.y > MAX_FLAT_DIMENSION) {

View file

@ -75,7 +75,7 @@ public:
void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); }
float getAlpha() const { return _alpha; };
void setAlpha(float alpha) { _alpha = alpha; }
void setAlpha(float alpha);
const rgbColor& getColor() const { return _color; }
void setColor(const rgbColor& value);
@ -101,6 +101,8 @@ public:
virtual void computeShapeInfo(ShapeInfo& info) override;
virtual ShapeType getShapeType() const override;
std::shared_ptr<graphics::Material> getMaterial() { return _material; }
protected:
float _alpha { 1 };
@ -111,6 +113,8 @@ protected:
//! prior functionality where new or unsupported shapes are treated as
//! ellipsoids.
ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID };
std::shared_ptr<graphics::Material> _material;
};
#endif // hifi_ShapeEntityItem_h

View file

@ -12,9 +12,13 @@
#include "TextureMap.h"
#include <Transform.h>
using namespace graphics;
using namespace gpu;
int materialPointerMetaID = qRegisterMetaType<MaterialPointer>("graphics::MaterialPointer");
Material::Material() :
_key(0),
_schemaBuffer(),
@ -221,4 +225,15 @@ bool Material::calculateMaterialInfo() const {
_hasCalculatedTextureInfo = allTextures;
}
return _hasCalculatedTextureInfo;
}
void Material::setTextureTransforms(const Transform& transform) {
for (auto &textureMapItem : _textureMaps) {
if (textureMapItem.second) {
textureMapItem.second->setTextureTransform(transform);
}
}
for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) {
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[i] = transform.getMatrix();
}
}

View file

@ -15,11 +15,14 @@
#include <bitset>
#include <map>
#include <queue>
#include <ColorUtils.h>
#include <gpu/Resource.h>
class Transform;
namespace graphics {
class TextureMap;
@ -351,6 +354,14 @@ public:
size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; }
bool hasTextureInfo() const { return _hasCalculatedTextureInfo; }
void setBlendFactor(float blendFactor) { _blendFactor = blendFactor; }
float getBlendFactor() { return _blendFactor; }
void setPriority(quint16 priority) { _priority = priority; }
quint16 getPriority() { return _priority; }
void setTextureTransforms(const Transform& transform);
private:
mutable MaterialKey _key;
mutable UniformBufferView _schemaBuffer;
@ -364,10 +375,35 @@ private:
mutable bool _hasCalculatedTextureInfo { false };
bool calculateMaterialInfo() const;
float _blendFactor { 1.0f };
quint16 _priority { 0 };
};
typedef std::shared_ptr< Material > MaterialPointer;
Q_DECLARE_METATYPE(MaterialPointer)
class MaterialCompare {
public:
bool operator() (MaterialPointer left, MaterialPointer right) {
return left->getPriority() < right->getPriority();
}
};
class MultiMaterial : public std::priority_queue<MaterialPointer, std::vector<MaterialPointer>, MaterialCompare> {
public:
bool remove(const MaterialPointer& value) {
auto it = std::find(c.begin(), c.end(), value);
if (it != c.end()) {
c.erase(it);
std::make_heap(c.begin(), c.end(), comp);
return true;
} else {
return false;
}
}
};
};
#endif

View file

@ -549,7 +549,6 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, im
auto map = std::make_shared<graphics::TextureMap>();
if (texture) {
map->setTextureSource(texture->_textureSource);
emit textureFinished();
}
return map;
@ -728,6 +727,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!occlusionName.isEmpty()) {
auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
// FIXME: we need to handle the occlusion map transform here
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
setTextureMap(MapChannel::OCCLUSION_MAP, map);
}

View file

@ -156,8 +156,7 @@ private:
virtual ~ModelCache() = default;
};
class NetworkMaterial : public QObject, public graphics::Material {
Q_OBJECT
class NetworkMaterial : public graphics::Material {
public:
using MapChannel = graphics::Material::MapChannel;
@ -174,9 +173,6 @@ public:
void setScatteringMap(const QString& url);
void setLightmapMap(const QString& url);
signals:
void textureFinished();
protected:
friend class Geometry;

View file

@ -381,6 +381,17 @@ bool OctreePacketData::appendValue(float value) {
return success;
}
bool OctreePacketData::appendValue(const glm::vec2& value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendValue(const glm::vec3& value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);

View file

@ -164,6 +164,9 @@ public:
/// appends a float value to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(float value);
/// appends a vec2 to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(const glm::vec2& value);
/// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(const glm::vec3& value);
@ -250,8 +253,8 @@ public:
static quint64 getTotalBytesOfColor() { return _totalBytesOfColor; } /// total bytes of color
static int unpackDataFromBytes(const unsigned char* dataBytes, float& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec4& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, bool& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, quint64& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, uint32_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }

View file

@ -47,7 +47,7 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr
MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) {
updateMeshPart(mesh, partIndex);
updateMaterial(material);
addMaterial(material);
}
void MeshPartPayload::updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex) {
@ -67,8 +67,12 @@ void MeshPartPayload::updateTransform(const Transform& transform, const Transfor
_worldBound.transform(_drawTransform);
}
void MeshPartPayload::updateMaterial(graphics::MaterialPointer drawMaterial) {
_drawMaterial = drawMaterial;
void MeshPartPayload::addMaterial(graphics::MaterialPointer material) {
_drawMaterials.push(material);
}
void MeshPartPayload::removeMaterial(graphics::MaterialPointer material) {
_drawMaterials.remove(material);
}
void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits) {
@ -85,8 +89,8 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits)
builder.withLayered();
}
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
if (_drawMaterials.top()) {
auto matKey = _drawMaterials.top()->getKey();
if (matKey.isTranslucent()) {
builder.withTransparent();
}
@ -105,8 +109,8 @@ Item::Bound MeshPartPayload::getBound() const {
ShapeKey MeshPartPayload::getShapeKey() const {
graphics::MaterialKey drawMaterialKey;
if (_drawMaterial) {
drawMaterialKey = _drawMaterial->getKey();
if (_drawMaterials.top()) {
drawMaterialKey = _drawMaterials.top()->getKey();
}
ShapeKey::Builder builder;
@ -160,7 +164,7 @@ void MeshPartPayload::render(RenderArgs* args) {
bindMesh(batch);
// apply material properties
args->_shapePipeline->bindMaterial(_drawMaterial, batch, args->_enableTexturing);
args->_shapePipeline->bindMaterial(_drawMaterials.top(), batch, args->_enableTexturing);
args->_details._materialSwitches++;
// Draw!
@ -252,7 +256,7 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) {
auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID);
if (networkMaterial) {
_drawMaterial = networkMaterial;
addMaterial(networkMaterial);
}
}
@ -298,8 +302,8 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tag
builder.withDeformed();
}
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
if (_drawMaterials.top()) {
auto matKey = _drawMaterials.top()->getKey();
if (matKey.isTranslucent()) {
builder.withTransparent();
}
@ -329,8 +333,8 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe
}
graphics::MaterialKey drawMaterialKey;
if (_drawMaterial) {
drawMaterialKey = _drawMaterial->getKey();
if (_drawMaterials.top()) {
drawMaterialKey = _drawMaterials.top()->getKey();
}
bool isTranslucent = drawMaterialKey.isTranslucent();
@ -411,7 +415,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
bindMesh(batch);
// apply material properties
args->_shapePipeline->bindMaterial(_drawMaterial, batch, args->_enableTexturing);
args->_shapePipeline->bindMaterial(_drawMaterials.top(), batch, args->_enableTexturing);
args->_details._materialSwitches++;
// Draw!

View file

@ -39,8 +39,6 @@ public:
virtual void notifyLocationChanged() {}
void updateTransform(const Transform& transform, const Transform& offsetTransform);
virtual void updateMaterial(graphics::MaterialPointer drawMaterial);
// Render Item interface
virtual render::ItemKey getKey() const;
virtual render::Item::Bound getBound() const;
@ -63,13 +61,16 @@ public:
mutable graphics::Box _worldBound;
std::shared_ptr<const graphics::Mesh> _drawMesh;
std::shared_ptr<graphics::Material> _drawMaterial;
graphics::MultiMaterial _drawMaterials;
graphics::Mesh::Part _drawPart;
size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; }
size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; }
int getMaterialTextureCount() { return _drawMaterial ? _drawMaterial->getTextureCount() : 0; }
bool hasTextureInfo() const { return _drawMaterial ? _drawMaterial->hasTextureInfo() : false; }
size_t getMaterialTextureSize() { return _drawMaterials.top() ? _drawMaterials.top()->getTextureSize() : 0; }
int getMaterialTextureCount() { return _drawMaterials.top() ? _drawMaterials.top()->getTextureCount() : 0; }
bool hasTextureInfo() const { return _drawMaterials.top() ? _drawMaterials.top()->hasTextureInfo() : false; }
void addMaterial(graphics::MaterialPointer material);
void removeMaterial(graphics::MaterialPointer material);
protected:
render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() };

View file

@ -1524,6 +1524,50 @@ bool Model::isRenderable() const {
return !_meshStates.empty() || (isLoaded() && _renderGeometry->getMeshes().empty());
}
void Model::addMaterial(graphics::MaterialPointer material, quint16 shapeID) {
if (shapeID < _modelMeshRenderItemIDs.size()) {
render::Transaction transaction;
auto itemID = _modelMeshRenderItemIDs[shapeID];
bool visible = isVisible();
uint8_t viewTagBits = getViewTagBits();
bool layeredInFront = isLayeredInFront();
bool layeredInHUD = isLayeredInHUD();
bool wireframe = isWireframe();
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits,
invalidatePayloadShapeKey, wireframe](ModelMeshPartPayload& data) {
data.addMaterial(material);
// if the material changed, we might need to update our item key or shape key
data.updateKey(visible, layeredInFront || layeredInHUD, viewTagBits);
data.setShapeKey(invalidatePayloadShapeKey, wireframe);
});
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
}
}
void Model::removeMaterial(graphics::MaterialPointer material, quint16 shapeID) {
if (shapeID < _modelMeshRenderItemIDs.size()) {
render::Transaction transaction;
auto itemID = _modelMeshRenderItemIDs[shapeID];
bool visible = isVisible();
uint8_t viewTagBits = getViewTagBits();
bool layeredInFront = isLayeredInFront();
bool layeredInHUD = isLayeredInHUD();
bool wireframe = isWireframe();
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits,
invalidatePayloadShapeKey, wireframe](ModelMeshPartPayload& data) {
data.removeMaterial(material);
// if the material changed, we might need to update our item key or shape key
data.updateKey(visible, layeredInFront || layeredInHUD, viewTagBits);
data.setShapeKey(invalidatePayloadShapeKey, wireframe);
});
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
}
}
class CollisionRenderGeometry : public Geometry {
public:
CollisionRenderGeometry(graphics::MeshPointer mesh) {

View file

@ -318,6 +318,9 @@ public:
void scaleToFit();
void addMaterial(graphics::MaterialPointer material, quint16 shapeID);
void removeMaterial(graphics::MaterialPointer material, quint16 shapeID);
public slots:
void loadURLFinished(bool success);

View file

@ -342,7 +342,7 @@ var toolBar = (function () {
var buttonHandlers = {}; // only used to tablet mode
function addButton(name, image, handler) {
function addButton(name, handler) {
buttonHandlers[name] = handler;
}
@ -399,7 +399,7 @@ var toolBar = (function () {
function handleNewMaterialDialogResult(result) {
if (result) {
var json = result.textInput;
var materialURL = result.textInput;
var materialMode;
switch (result.comboBox) {
case MATERIAL_MODE_PROJECTED:
@ -409,11 +409,13 @@ var toolBar = (function () {
shapeType = "uv";
}
if (json) {
var DEFAULT_LAYERED_MATERIAL_PRIORITY = 1;
if (materialURL) {
createNewEntity({
type: "Material",
materialURL: json,
materialMode: materialMode
materialURL: materialURL,
materialMode: materialMode,
priority: DEFAULT_LAYERED_MATERIAL_PRIORITY
});
}
}
@ -475,32 +477,22 @@ var toolBar = (function () {
that.toggle();
});
addButton("importEntitiesButton", "assets-01.svg", function() {
addButton("importEntitiesButton", function() {
Window.browseChanged.connect(onFileOpenChanged);
Window.browseAsync("Select Model to Import", "", "*.json");
});
addButton("openAssetBrowserButton", "assets-01.svg", function() {
addButton("openAssetBrowserButton", function() {
Window.showAssetServer();
});
addButton("newModelButton", "model-01.svg", function () {
var SHAPE_TYPES = [];
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
addButton("newModelButton", function () {
// tablet version of new-model dialog
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.pushOntoStack("hifi/tablet/NewModelDialog.qml");
});
addButton("newCubeButton", "cube-01.svg", function () {
addButton("newCubeButton", function () {
createNewEntity({
type: "Box",
dimensions: DEFAULT_DIMENSIONS,
@ -512,7 +504,7 @@ var toolBar = (function () {
});
});
addButton("newSphereButton", "sphere-01.svg", function () {
addButton("newSphereButton", function () {
createNewEntity({
type: "Sphere",
dimensions: DEFAULT_DIMENSIONS,
@ -524,7 +516,7 @@ var toolBar = (function () {
});
});
addButton("newLightButton", "light-01.svg", function () {
addButton("newLightButton", function () {
createNewEntity({
type: "Light",
dimensions: DEFAULT_LIGHT_DIMENSIONS,
@ -543,7 +535,7 @@ var toolBar = (function () {
});
});
addButton("newTextButton", "text-01.svg", function () {
addButton("newTextButton", function () {
createNewEntity({
type: "Text",
dimensions: {
@ -566,7 +558,7 @@ var toolBar = (function () {
});
});
addButton("newWebButton", "web-01.svg", function () {
addButton("newWebButton", function () {
createNewEntity({
type: "Web",
dimensions: {
@ -578,7 +570,7 @@ var toolBar = (function () {
});
});
addButton("newZoneButton", "zone-01.svg", function () {
addButton("newZoneButton", function () {
createNewEntity({
type: "Zone",
dimensions: {
@ -589,7 +581,7 @@ var toolBar = (function () {
});
});
addButton("newParticleButton", "particle-01.svg", function () {
addButton("newParticleButton", function () {
createNewEntity({
type: "ParticleEffect",
isEmitting: true,
@ -642,15 +634,10 @@ var toolBar = (function () {
});
});
addButton("newMaterialButton", "model-01.svg", function () {
var MATERIAL_MODES = [];
MATERIAL_MODES[MATERIAL_MODE_UV] = "UV space material";
MATERIAL_MODES[MATERIAL_MODE_PROJECTED] = "3D projected material";
var MATERIAL_MODE_DEFAULT = MATERIAL_MODE_UV;
addButton("newMaterialButton", function () {
// tablet version of new material dialog
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.pushOntoStack("NewMaterialDialog.qml");
tablet.pushOntoStack("hifi/tablet/NewMaterialDialog.qml");
});
that.setActive(false);

View file

@ -645,14 +645,14 @@ hr {
margin-left: 10px;
}
.text label, .url label, .number label, .textarea label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label {
.text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label,.pyr label, .dropdown label, .gen label {
float: left;
margin-left: 1px;
margin-bottom: 3px;
margin-top: -2px;
}
.text legend, .url legend, .number legend, .textarea legend, .rgb legend, .xyz legend, .pyr legend, .dropdown legend, .gen legend {
.text legend, .url legend, .number legend, .textarea legend, .xy legend, .wh legend, .rgb legend, .xyz legend, .pyr legend, .dropdown legend, .gen legend {
float: left;
margin-left: 1px;
margin-bottom: 3px;
@ -667,7 +667,7 @@ hr {
clear: both;
float: left;
}
.xyz > div, .pyr > div, .gen > div {
.xy > div, .wh > div, .xyz > div, .pyr > div, .gen > div {
clear: both;
}
@ -841,6 +841,12 @@ div.refresh input[type="button"] {
margin-right: -6px;
}
.xy .tuple input {
padding-left: 25px;
}
.wh .tuple input {
padding-left: 45px;
}
.rgb .tuple input {
padding-left: 65px;
}

View file

@ -787,7 +787,7 @@
<div class="material-group material-section property number">
<label>Priority </label>
<input type="number" id="property-priority">
<input type="number" id="property-priority" min="0">
</div>
<div class="material-group material-section property number">
@ -795,13 +795,26 @@
<input type="number" id="property-shape-id" min="0">
</div>
<div class="material-group material-section property tuple">
<label>Material Bounds </label>
<div><input type="number" class="x" id="property-material-bounds-x" min="0" max="1" step="0.1"><label for="property-material-bounds-x">Left:</label></div>
<div><input type="number" class="y" id="property-material-bounds-y" min="0" max="1" step="0.1"><label for="property-material-bounds-y">Top:</label></div>
<div><input type="number" class="z" id="property-material-bounds-z" min="0" max="1" step="0.1"><label for="property-material-bounds-z">Right:</label></div>
<div><input type="number" class="w" id="property-material-bounds-w" min="0" max="1" step="0.1"><label for="property-material-bounds-w">Bottom:</label></div>
</div>
<fieldset>
<div class="material-group material-section property xy fstuple">
<label>Material Position </label>
<div class="tuple">
<div><input type="number" class="x" id="property-material-pos-x" min="0" max="1" step="0.1"><label for="property-material-pos-x">X:</label></div>
<div><input type="number" class="y" id="property-material-pos-y" min="0" max="1" step="0.1"><label for="property-material-pos-y">Y:</label></div>
</div>
<div class="material-group material-section property wh fstuple">
<label>Material Scale </label>
<div class="tuple">
<div><input type="number" class="x" id="property-material-scale-x" min="0" step="0.1"><label for="property-material-scale-x">Width:</label></div>
<div><input type="number" class="y" id="property-material-scale-y" min="0" step="0.1"><label for="property-material-scale-y">Height:</label></div>
</div>
<div class="material-group material-section property number">
<label>Material Rotation <span class="unit">deg</span></label>
<input type="number" id="property-material-rot" step="0.1">
</div>
</fieldset>
</fieldset>
</fieldset>

View file

@ -25,7 +25,7 @@ var ICON_FOR_TYPE = {
PolyVox: "&#xe005;",
Multiple: "&#xe000;",
PolyLine: "&#xe01b;",
Material: "&#xe008;"
Material: "&#xe00b;"
};
var EDITOR_TIMEOUT_DURATION = 1500;
@ -167,6 +167,17 @@ function createEmitGroupTextPropertyUpdateFunction(group, propertyName) {
};
}
function createEmitVec2PropertyUpdateFunction(property, elX, elY) {
return function () {
var properties = {};
properties[property] = {
x: elX.value,
y: elY.value
};
updateProperties(properties);
};
}
function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
return function() {
var properties = {};
@ -192,19 +203,6 @@ function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, el
};
}
function createEmitVec4PropertyUpdateFunction(property, elX, elY, elZ, elW) {
return function () {
var properties = {};
properties[property] = {
x: elX.value,
y: elY.value,
z: elZ.value,
w: elW.value
};
updateProperties(properties);
};
}
function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
return function() {
var properties = {};
@ -638,10 +636,11 @@ function loaded() {
var elBlendFactor = document.getElementById("property-blend-factor");
var elPriority = document.getElementById("property-priority");
var elShapeID = document.getElementById("property-shape-id");
var elMaterialBoundsX = document.getElementById("property-material-bounds-x");
var elMaterialBoundsY = document.getElementById("property-material-bounds-y");
var elMaterialBoundsZ = document.getElementById("property-material-bounds-z");
var elMaterialBoundsW = document.getElementById("property-material-bounds-w");
var elMaterialPosX = document.getElementById("property-material-pos-x");
var elMaterialPosY = document.getElementById("property-material-pos-y");
var elMaterialScaleX = document.getElementById("property-material-scale-x");
var elMaterialScaleY = document.getElementById("property-material-scale-y");
var elMaterialRot = document.getElementById("property-material-rot");
var elWebSourceURL = document.getElementById("property-web-source-url");
var elWebDPI = document.getElementById("property-web-dpi");
@ -1134,10 +1133,11 @@ function loaded() {
elBlendFactor.value = properties.blendFactor.toFixed(2);
elPriority.value = properties.priority;
elShapeID.value = properties.shapeID;
elMaterialBoundsX.value = properties.materialBounds.x.toFixed(2);
elMaterialBoundsY.value = properties.materialBounds.y.toFixed(2);
elMaterialBoundsZ.value = properties.materialBounds.z.toFixed(2);
//elMaterialBoundsW.value = properties.materialBounds.w.toFixed(2);
elMaterialPosX.value = properties.materialPos.x.toFixed(4);
elMaterialPosY.value = properties.materialPos.y.toFixed(4);
elMaterialScaleX.value = properties.materialScale.x.toFixed(4);
elMaterialScaleY.value = properties.materialScale.y.toFixed(4);
elMaterialRot.value = properties.materialRot.toFixed(2);
}
if (properties.locked) {
@ -1411,15 +1411,16 @@ function loaded() {
elMaterialURL.addEventListener('change', createEmitTextPropertyUpdateFunction('materialURL'));
elMaterialMode.addEventListener('change', createEmitTextPropertyUpdateFunction('materialMode'));
elBlendFactor.addEventListener('change', createEmitNumberPropertyUpdateFunction('blendFactor', 2));
elPriority.addEventListener('change', createEmitNumberPropertyUpdateFunction('priority'));
elShapeID.addEventListener('change', createEmitNumberPropertyUpdateFunction('shapeID'));
elPriority.addEventListener('change', createEmitNumberPropertyUpdateFunction('priority', 0));
elShapeID.addEventListener('change', createEmitNumberPropertyUpdateFunction('shapeID', 0));
var materialBoundsChangeFunction = createEmitVec4PropertyUpdateFunction('materialBounds',
elMaterialBoundsX, elMaterialBoundsY, elMaterialBoundsZ, elMaterialBoundsW);
elMaterialBoundsX.addEventListener('change', materialBoundsChangeFunction);
elMaterialBoundsY.addEventListener('change', materialBoundsChangeFunction);
elMaterialBoundsZ.addEventListener('change', materialBoundsChangeFunction);
elMaterialBoundsW.addEventListener('change', materialBoundsChangeFunction);
var materialPosChangeFunction = createEmitVec2PropertyUpdateFunction('materialPos', elMaterialPosX, elMaterialPosY);
elMaterialPosX.addEventListener('change', materialPosChangeFunction);
elMaterialPosY.addEventListener('change', materialPosChangeFunction);
var materialScaleChangeFunction = createEmitVec2PropertyUpdateFunction('materialScale', elMaterialScaleX, elMaterialScaleY);
elMaterialScaleX.addEventListener('change', materialScaleChangeFunction);
elMaterialScaleY.addEventListener('change', materialScaleChangeFunction);
elMaterialRot.addEventListener('change', createEmitNumberPropertyUpdateFunction('materialRot', 2));
elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera'));

138
tools/jsdoc/package-lock.json generated Normal file
View file

@ -0,0 +1,138 @@
{
"name": "hifiJSDoc",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"babylon": {
"version": "7.0.0-beta.19",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz",
"integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A=="
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"catharsis": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz",
"integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
"requires": {
"underscore-contrib": "0.3.0"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"js2xmlparser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz",
"integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=",
"requires": {
"xmlcreate": "1.0.2"
}
},
"jsdoc": {
"version": "3.5.5",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz",
"integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==",
"requires": {
"babylon": "7.0.0-beta.19",
"bluebird": "3.5.1",
"catharsis": "0.8.9",
"escape-string-regexp": "1.0.5",
"js2xmlparser": "3.0.0",
"klaw": "2.0.0",
"marked": "0.3.12",
"mkdirp": "0.5.1",
"requizzle": "0.2.1",
"strip-json-comments": "2.0.1",
"taffydb": "2.6.2",
"underscore": "1.8.3"
}
},
"klaw": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz",
"integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=",
"requires": {
"graceful-fs": "4.1.11"
}
},
"marked": {
"version": "0.3.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz",
"integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA=="
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"requizzle": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz",
"integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=",
"requires": {
"underscore": "1.6.0"
},
"dependencies": {
"underscore": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
}
}
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"taffydb": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
"integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg="
},
"underscore": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
},
"underscore-contrib": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz",
"integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=",
"requires": {
"underscore": "1.6.0"
},
"dependencies": {
"underscore": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
}
}
},
"xmlcreate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz",
"integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8="
}
}
}