diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index e94325f399..65dd871fb2 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -133,6 +133,17 @@ TabView { editTabView.currentIndex = 4 } } + + NewEntityButton { + icon: "icons/create-icons/94-model-01.svg" + text: "MATERIAL" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newMaterialButton" } + }); + editTabView.currentIndex = 2 + } + } } HifiControls.Button { diff --git a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml new file mode 100644 index 0000000000..6d3477187b --- /dev/null +++ b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml @@ -0,0 +1,174 @@ +// +// NewMaterialDialog.qml +// qml/hifi +// +// Created by Sam Gondelman on 1/17/18 +// Copyright 2018 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs + +import "../../styles-uit" +import "../../controls-uit" +import "../dialogs" + +Rectangle { + id: newMaterialDialog + // width: parent.width + // height: parent.height + HifiConstants { id: hifi } + color: hifi.colors.baseGray; + signal sendToScript(var message); + property bool keyboardEnabled: false + property bool punctuationMode: false + property bool keyboardRasied: false + + function errorMessageBox(message) { + return desktop.messageBox({ + icon: hifi.icons.warning, + defaultButton: OriginalDialogs.StandardButton.Ok, + title: "Error", + text: message + }); + } + + Item { + id: column1 + anchors.rightMargin: 10 + anchors.leftMargin: 10 + anchors.bottomMargin: 10 + anchors.topMargin: 10 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: keyboard.top + + Text { + id: text1 + text: qsTr("Material URL") + color: "#ffffff" + font.pixelSize: 12 + } + + TextInput { + id: materialURL + height: 20 + text: qsTr("") + color: "white" + anchors.top: text1.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + font.pixelSize: 12 + + onAccepted: { + newMaterialDialog.keyboardEnabled = false; + } + + MouseArea { + anchors.fill: parent + onClicked: { + newMaterialDialog.keyboardEnabled = HMD.active + parent.focus = true; + parent.forceActiveFocus(); + materialURL.cursorPosition = materialURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters); + } + } + } + + Rectangle { + id: textInputBox + color: "white" + anchors.fill: materialURL + opacity: 0.1 + } + + Row { + id: row1 + height: 400 + spacing: 30 + anchors.top: materialURL.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + + Column { + id: column3 + height: 400 + spacing: 10 + + Text { + id: text3 + text: qsTr("Material Mode") + color: "#ffffff" + font.pixelSize: 12 + } + + ComboBox { + id: materialMode + property var materialArray: ["UV space material", + "3D projected material"] + + width: 200 + z: 100 + transformOrigin: Item.Center + model: materialArray + } + + Row { + id: row3 + width: 200 + height: 400 + spacing: 5 + + anchors.horizontalCenter: column3.horizontalCenter + anchors.horizontalCenterOffset: -20 + + Button { + id: button1 + text: qsTr("Add") + z: -1 + onClicked: { + newMaterialDialog.sendToScript({ + method: "newMaterialDialogAdd", + params: { + textInput: materialURL.text, + comboBox: materialMode.currentIndex + } + }); + } + } + + Button { + id: button2 + z: -1 + text: qsTr("Cancel") + onClicked: { + newMaterialDialog.sendToScript({method: "newMaterialDialogCancel"}) + } + } + } + } + } + } + + Keyboard { + id: keyboard + raised: parent.keyboardEnabled + numeric: parent.punctuationMode + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + } +} diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index fb9aba636b..cbd2df86ad 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -25,6 +25,7 @@ #include "RenderableTextEntityItem.h" #include "RenderableWebEntityItem.h" #include "RenderableZoneEntityItem.h" +#include "RenderableMaterialEntityItem.h" using namespace render; @@ -247,6 +248,10 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, result = make_renderer(entity); break; + case Type::Material: + result = make_renderer(entity); + break; + default: break; } @@ -388,4 +393,4 @@ void EntityRenderer::onAddToScene(const EntityItemPointer& entity) { void EntityRenderer::onRemoveFromScene(const EntityItemPointer& entity) { entity->deregisterChangeHandler(_changeHandlerId); QObject::disconnect(this, &EntityRenderer::requestRenderUpdate, this, nullptr); -} +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 8eb82e2c6e..e54ae2f6b4 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -59,7 +59,6 @@ protected: virtual void onAddToScene(const EntityItemPointer& entity); virtual void onRemoveFromScene(const EntityItemPointer& entity); -protected: EntityRenderer(const EntityItemPointer& entity); ~EntityRenderer(); diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp new file mode 100644 index 0000000000..83754c1c16 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -0,0 +1,238 @@ +// +// Created by Sam Gondelman on 1/18/2018 +// Copyright 2018 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 "RenderableMaterialEntityItem.h" + +using namespace render; +using namespace render::entities; + +bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { + if (entity->getMaterial() != _drawMaterial) { + return true; + } + return false; +} + +void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + withWriteLock([&] { + _drawMaterial = entity->getMaterial(); + _renderTransform = getModelTransform(); + const float MATERIAL_ENTITY_SCALE = 0.5f; + _renderTransform.postScale(MATERIAL_ENTITY_SCALE); + _renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS); + }); +} + +ItemKey MaterialEntityRenderer::getKey() { + ItemKey::Builder builder; + builder.withTypeShape(); + + if (!_visible) { + builder.withInvisible(); + } + + if (_drawMaterial) { + auto matKey = _drawMaterial->getKey(); + if (matKey.isTranslucent()) { + builder.withTransparent(); + } + } + + return builder.build(); +} + +ShapeKey MaterialEntityRenderer::getShapeKey() { + graphics::MaterialKey drawMaterialKey; + if (_drawMaterial) { + drawMaterialKey = _drawMaterial->getKey(); + } + + bool isTranslucent = drawMaterialKey.isTranslucent(); + bool hasTangents = drawMaterialKey.isNormalMap(); + bool hasSpecular = drawMaterialKey.isMetallicMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + bool isUnlit = drawMaterialKey.isUnlit(); + + ShapeKey::Builder builder; + builder.withMaterial(); + + if (isTranslucent) { + builder.withTranslucent(); + } + if (hasTangents) { + builder.withTangents(); + } + if (hasSpecular) { + builder.withSpecular(); + } + if (hasLightmap) { + builder.withLightmap(); + } + if (isUnlit) { + builder.withUnlit(); + } + + return builder.build(); +} + +glm::vec3 MaterialEntityRenderer::getVertexPos(float phi, float theta) { + return glm::vec3(glm::sin(theta) * glm::cos(phi), glm::cos(theta), glm::sin(theta) * glm::sin(phi)); +} + +glm::vec3 MaterialEntityRenderer::getTangent(float phi, float theta) { + return glm::vec3(-glm::cos(theta) * glm::cos(phi), 0, -glm::cos(theta) * glm::sin(phi)); +} + +void MaterialEntityRenderer::addVertex(std::vector& buffer, const glm::vec3& pos, const glm::vec3& tan, const glm::vec2 uv) { + buffer.push_back(pos.x); buffer.push_back(pos.y); buffer.push_back(pos.z); + buffer.push_back(tan.x); buffer.push_back(tan.y); buffer.push_back(tan.z); + buffer.push_back(uv.x); buffer.push_back(uv.y); +} + +void MaterialEntityRenderer::addTriangleFan(std::vector& buffer, int stack, int step) { + float v1 = ((float)stack) / STACKS; + float theta1 = v1 * M_PI; + glm::vec3 tip = getVertexPos(0, theta1); + float v2 = ((float)(stack + step)) / STACKS; + float theta2 = v2 * M_PI; + for (int i = 0; i < SLICES; i++) { + float u1 = ((float)i) / SLICES; + float u2 = ((float)(i + step)) / SLICES; + float phi1 = u1 * M_PI_TIMES_2; + float phi2 = u2 * M_PI_TIMES_2; + /* (flipped for negative step) + p1 + / \ + / \ + / \ + p3 ------ p2 + */ + + glm::vec3 pos2 = getVertexPos(phi2, theta2); + glm::vec3 pos3 = getVertexPos(phi1, theta2); + + glm::vec3 tan1 = getTangent(0, theta1); + glm::vec3 tan2 = getTangent(phi2, theta2); + glm::vec3 tan3 = getTangent(phi1, theta2); + + glm::vec2 uv1 = glm::vec2((u1 + u2) / 2.0f, v1); + glm::vec2 uv2 = glm::vec2(u2, v2); + glm::vec2 uv3 = glm::vec2(u1, v2); + + addVertex(buffer, tip, tan1, uv1); + addVertex(buffer, pos2, tan2, uv2); + addVertex(buffer, pos3, tan3, uv3); + + _numVertices += 3; + } +} + +int MaterialEntityRenderer::_numVertices = 0; +std::shared_ptr MaterialEntityRenderer::_streamFormat = nullptr; +std::shared_ptr MaterialEntityRenderer::_stream = nullptr; +std::shared_ptr MaterialEntityRenderer::_verticesBuffer = nullptr; + +void MaterialEntityRenderer::generateMesh() { + _streamFormat = std::make_shared(); + _stream = std::make_shared(); + _verticesBuffer = std::make_shared(); + + const int NUM_POS_COORDS = 3; + const int NUM_TANGENT_COORDS = 3; + const int VERTEX_TANGENT_OFFSET = NUM_POS_COORDS * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = VERTEX_TANGENT_OFFSET + NUM_TANGENT_COORDS * sizeof(float); + + _streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + _streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + _streamFormat->setAttribute(gpu::Stream::TANGENT, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_TANGENT_OFFSET); + _streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); + + _stream->addBuffer(_verticesBuffer, 0, _streamFormat->getChannels().at(0)._stride); + + std::vector vertexBuffer; + + // Top + addTriangleFan(vertexBuffer, 0, 1); + + // Middle section + for (int j = 1; j < STACKS - 1; j++) { + float v1 = ((float)j) / STACKS; + float v2 = ((float)(j + 1)) / STACKS; + float theta1 = v1 * M_PI; + float theta2 = v2 * M_PI; + for (int i = 0; i < SLICES; i++) { + float u1 = ((float)i) / SLICES; + float u2 = ((float)(i + 1)) / SLICES; + float phi1 = u1 * M_PI_TIMES_2; + float phi2 = u2 * M_PI_TIMES_2; + + /* + p2 ---- p3 + | / | + | / | + | / | + p1 ---- p4 + */ + + glm::vec3 pos1 = getVertexPos(phi1, theta2); + glm::vec3 pos2 = getVertexPos(phi1, theta1); + glm::vec3 pos3 = getVertexPos(phi2, theta1); + glm::vec3 pos4 = getVertexPos(phi2, theta2); + + glm::vec3 tan1 = getTangent(phi1, theta2); + glm::vec3 tan2 = getTangent(phi1, theta1); + glm::vec3 tan3 = getTangent(phi2, theta1); + glm::vec3 tan4 = getTangent(phi2, theta2); + + glm::vec2 uv1 = glm::vec2(u1, v2); + glm::vec2 uv2 = glm::vec2(u1, v1); + glm::vec2 uv3 = glm::vec2(u2, v1); + glm::vec2 uv4 = glm::vec2(u2, v2); + + addVertex(vertexBuffer, pos1, tan1, uv1); + addVertex(vertexBuffer, pos2, tan2, uv2); + addVertex(vertexBuffer, pos3, tan3, uv3); + + addVertex(vertexBuffer, pos3, tan3, uv3); + addVertex(vertexBuffer, pos4, tan4, uv4); + addVertex(vertexBuffer, pos1, tan1, uv1); + + _numVertices += 6; + } + } + + // Bottom + addTriangleFan(vertexBuffer, STACKS, -1); + + _verticesBuffer->append(vertexBuffer.size() * sizeof(float), (gpu::Byte*) vertexBuffer.data()); +} + +void MaterialEntityRenderer::doRender(RenderArgs* args) { + PerformanceTimer perfTimer("RenderableMaterialEntityItem::render"); + Q_ASSERT(args->_batch); + + gpu::Batch& batch = *args->_batch; + + batch.setModelTransform(_renderTransform); + + // bind the material + args->_shapePipeline->bindMaterial(_drawMaterial, batch, args->_enableTexturing); + args->_details._materialSwitches++; + + // Draw! + if (_numVertices == 0) { + generateMesh(); + } + + batch.setInputFormat(_streamFormat); + batch.setInputStream(0, *_stream); + batch.draw(gpu::TRIANGLES, _numVertices, 0); + + const int NUM_VERTICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _numVertices / NUM_VERTICES_PER_TRIANGLE; +} diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h new file mode 100644 index 0000000000..166ad762cc --- /dev/null +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -0,0 +1,54 @@ +// +// Created by Sam Gondelman on 1/18/2018 +// Copyright 2018 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_RenderableMaterialEntityItem_h +#define hifi_RenderableMaterialEntityItem_h + +#include "RenderableEntityItem.h" + +#include + +class NetworkMaterial; + +namespace render { namespace entities { + +class MaterialEntityRenderer : public TypedEntityRenderer { + using Parent = TypedEntityRenderer; + using Pointer = std::shared_ptr; +public: + MaterialEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {} + +private: + virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doRender(RenderArgs* args) override; + + ItemKey getKey() override; + ShapeKey getShapeKey() override; + + Transform _renderTransform; + + std::shared_ptr _drawMaterial; + + static int _numVertices; + static std::shared_ptr _streamFormat; + static std::shared_ptr _stream; + static std::shared_ptr _verticesBuffer; + + void generateMesh(); + void addTriangleFan(std::vector& buffer, int stack, int step); + static glm::vec3 getVertexPos(float phi, float theta); + static glm::vec3 getTangent(float phi, float theta); + static void addVertex(std::vector& 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; +}; + +} } +#endif // hifi_RenderableMaterialEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f8f8859d73..77300be399 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -115,6 +115,17 @@ void buildStringToShapeTypeLookup() { addShapeType(SHAPE_TYPE_STATIC_MESH); } +QHash stringToMaterialModeLookup; + +void addMaterialMode(MaterialMode mode) { + stringToMaterialModeLookup[MaterialModeHelpers::getNameForMaterialMode(mode)] = mode; +} + +void buildStringToMaterialModeLookup() { + addMaterialMode(UV); + addMaterialMode(PROJECTED); +} + QString getCollisionGroupAsString(uint8_t group) { switch (group) { case USER_COLLISION_GROUP_DYNAMIC: @@ -259,6 +270,21 @@ void EntityItemProperties::setSkyboxModeFromString(const QString& skyboxMode) { } } +QString EntityItemProperties::getMaterialModeAsString() const { + return MaterialModeHelpers::getNameForMaterialMode(_materialMode); +} + +void EntityItemProperties::setMaterialModeFromString(const QString& materialMode) { + if (stringToMaterialModeLookup.empty()) { + buildStringToMaterialModeLookup(); + } + auto materialModeItr = stringToMaterialModeLookup.find(materialMode.toLower()); + if (materialModeItr != stringToMaterialModeLookup.end()) { + _materialMode = materialModeItr.value(); + _materialModeChanged = true; + } +} + EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; @@ -329,7 +355,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RADIUS_SPREAD, radiusSpread); CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); - CHECK_PROPERTY_CHANGE(PROP_MATERIAL_URL, materialURL); CHECK_PROPERTY_CHANGE(PROP_MATERIAL_TYPE, materialMode); CHECK_PROPERTY_CHANGE(PROP_MATERIAL_BLEND_FACTOR, blendFactor); @@ -635,7 +660,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Materials if (_type == EntityTypes::Material) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_URL, materialURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_TYPE, materialMode); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_MATERIAL_TYPE, materialMode, getMaterialModeAsString()); 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); @@ -776,7 +801,7 @@ 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_QSCRIPTVALUE(materialMode, MaterialMode, setMaterialMode); + COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(materialMode, MaterialMode, setMaterialMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(blendFactor, float, setBlendFactor); COPY_PROPERTY_FROM_QSCRIPTVALUE(priority, int, setPriority); COPY_PROPERTY_FROM_QSCRIPTVALUE(shapeID, int, setShapeID); @@ -1135,6 +1160,13 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float); + 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); + // Certifiable Properties ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString); ADD_PROPERTY_TO_MAP(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString); @@ -2067,6 +2099,13 @@ void EntityItemProperties::markAllChanged() { //_alphaStartChanged = true; //_alphaFinishChanged = true; + _materialURLChanged = true; + _materialModeChanged = true; + _blendFactorChanged = true; + _priorityChanged = true; + _shapeIDChanged = true; + _materialBoundsChanged = true; + // Certifiable Properties _itemNameChanged = true; _itemDescriptionChanged = true; @@ -2390,6 +2429,24 @@ QList EntityItemProperties::listChangedProperties() { if (radiusFinishChanged()) { out += "radiusFinish"; } + if (materialURLChanged()) { + out += "materialURL"; + } + if (materialModeChanged()) { + out += "materialMode"; + } + if (blendFactorChanged()) { + out += "blendFactor"; + } + if (priorityChanged()) { + out += "priority"; + } + if (shapeIDChanged()) { + out += "shapeID"; + } + if (materialBoundsChanged()) { + out += "materialBounds"; + } // Certifiable Properties if (itemNameChanged()) { diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index c8c8a658ce..557a8476fa 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -242,6 +242,29 @@ 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; diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp index 19daa11490..bbcc691879 100644 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -10,6 +10,9 @@ #include "EntityItemProperties.h" +#include "QJsonDocument" +#include "QJsonArray" + EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { Pointer entity(new MaterialEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); @@ -34,7 +37,7 @@ EntityItemProperties MaterialEntityItem::getProperties(EntityPropertyFlags desir bool MaterialEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - + SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialURL, setMaterialURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(materialMode, setMaterialMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendFactor, setBlendFactor); @@ -55,6 +58,139 @@ bool MaterialEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } +bool MaterialEntityItem::parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB) { + if (array.isArray()) { + QJsonArray colorArray = array.toArray(); + if (colorArray.size() >= 3 && colorArray[0].isDouble() && colorArray[1].isDouble() && colorArray[2].isDouble()) { + isSRGB = true; + if (colorArray.size() >= 4) { + if (colorArray[3].isBool()) { + isSRGB = colorArray[3].toBool(); + } + } + color = glm::vec3(colorArray[0].toDouble(), colorArray[1].toDouble(), colorArray[2].toDouble()); + return true; + } + } + return false; +} + +void MaterialEntityItem::parseJSONMaterial(const QJsonObject& materialJSON) { + QString name = ""; + std::shared_ptr material = std::make_shared(); + for (auto& key : materialJSON.keys()) { + if (key == "name") { + auto nameJSON = materialJSON.value(key); + if (nameJSON.isString()) { + name = nameJSON.toString(); + } + } else if (key == "emissive") { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); + if (valid) { + material->setEmissive(color, isSRGB); + } + } else if (key == "opacity") { + auto value = materialJSON.value(key); + if (value.isDouble()) { + material->setOpacity(value.toDouble()); + } + } else if (key == "albedo") { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); + if (valid) { + material->setAlbedo(color, isSRGB); + } + } else if (key == "roughness") { + auto value = materialJSON.value(key); + if (value.isDouble()) { + material->setRoughness(value.toDouble()); + } + } else if (key == "fresnel") { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); + if (valid) { + material->setFresnel(color, isSRGB); + } + } else if (key == "metallic") { + auto value = materialJSON.value(key); + if (value.isDouble()) { + material->setMetallic(value.toDouble()); + } + } else if (key == "scattering") { + auto value = materialJSON.value(key); + if (value.isDouble()) { + material->setScattering(value.toDouble()); + } + } else if (key == "emissiveMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setEmissiveMap(value.toString()); + } + } else if (key == "albedoMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + bool useAlphaChannel = false; + auto opacityMap = materialJSON.find("opacityMap"); + if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == value.toString()) { + useAlphaChannel = true; + } + material->setAlbedoMap(value.toString(), useAlphaChannel); + } + } else if (key == "roughnessMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setRoughnessMap(value.toString(), false); + } + } else if (key == "glossMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setRoughnessMap(value.toString(), true); + } + } else if (key == "metallicMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setMetallicMap(value.toString(), false); + } + } else if (key == "specularMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setMetallicMap(value.toString(), true); + } + } else if (key == "normalMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setNormalMap(value.toString(), false); + } + } else if (key == "bumpMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setNormalMap(value.toString(), true); + } + } else if (key == "occlusionMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setOcclusionMap(value.toString()); + } + } else if (key == "scatteringMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setScatteringMap(value.toString()); + } + } else if (key == "lightMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + material->setLightmapMap(value.toString()); + } + } + } + _materials[name] = material; + _materialNames.push_back(name); +} + int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, @@ -66,8 +202,8 @@ 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, int, setPriority); - READ_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, int, setShapeID); + 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); return bytesRead; @@ -96,25 +232,82 @@ void MaterialEntityItem::appendSubclassData(OctreePacketData* packetData, Encode bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_MATERIAL_URL, getMaterialURL()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, getMaterialMode()); + APPEND_ENTITY_PROPERTY(PROP_MATERIAL_TYPE, (uint32_t)getMaterialMode()); APPEND_ENTITY_PROPERTY(PROP_MATERIAL_BLEND_FACTOR, getBlendFactor()); - APPEND_ENTITY_PROPERTY(PROP_MATERIAL_PRIORITY, getPriority()); - APPEND_ENTITY_PROPERTY(PROP_PARENT_SHAPE_ID, getShapeID()); + 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()); } void MaterialEntityItem::debugDump() const { quint64 now = usecTimestampNow(); - qCDebug(entities) << "MATERIAL EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; - qCDebug(entities) << " url:" << _materialURL; - qCDebug(entities) << " material type:" << _materialMode; - qCDebug(entities) << " blend factor:" << _blendFactor; - qCDebug(entities) << " priority:" << _priority; - qCDebug(entities) << " parent shape ID:" << _shapeID; - qCDebug(entities) << " material bounds:" << _materialBounds; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; + qCDebug(entities) << " MATERIAL EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " material json:" << _materialURL; + qCDebug(entities) << " current material name:" << _currentMaterialName; + qCDebug(entities) << " material type:" << _materialMode; + qCDebug(entities) << " blend factor:" << _blendFactor; + qCDebug(entities) << " priority:" << _priority; + qCDebug(entities) << " parent shape ID:" << _shapeID; + qCDebug(entities) << " material bounds:" << _materialBounds; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "MATERIAL EntityItem Ptr:" << this; } + +void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) { + EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS); +} + +std::shared_ptr MaterialEntityItem::getMaterial() const { + auto material = _materials.find(_currentMaterialName); + if (material != _materials.end()) { + return material.value(); + } else { + return nullptr; + } +} + +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()); + } + } + } else if (materialJSON.isObject()) { + parseJSONMaterial(materialJSON.object()); + } + } + } + _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) { + auto material = _materials.find(currentMaterialName); + if (material != _materials.end()) { + _currentMaterialName = currentMaterialName; + } else if (_materialNames.size() > 0) { + setCurrentMaterialName(_materialNames[0]); + } +} + +void MaterialEntityItem::setUserData(const QString& userData) { + EntityItem::setUserData(userData); + if (_materialURL.startsWith("userData")) { + // Trigger material update when user data changes + setMaterialURL(_materialURL); + } +} \ No newline at end of file diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h index 094c94f24c..80fe226b68 100644 --- a/libraries/entities/src/MaterialEntityItem.h +++ b/libraries/entities/src/MaterialEntityItem.h @@ -46,25 +46,34 @@ public: void debugDump() const override; - const QString& getMaterialURL() { return _materialURL; } - void setMaterialURL(const QString& materialURL) { _materialURL = materialURL; } + virtual void setUnscaledDimensions(const glm::vec3& value) override; - MaterialMode getMaterialType() { return _materialMode; } - void setMaterialMode(MaterialMode mode); + QString getMaterialURL() const { return _materialURL; } + void setMaterialURL(const QString& materialURLString); - float getBlendFactor() { return _blendFactor; } + QString getCurrentMaterialName() const { return _currentMaterialName; } + void setCurrentMaterialName(const QString& currentMaterialName); + + MaterialMode getMaterialMode() const { return _materialMode; } + void setMaterialMode(MaterialMode mode) { _materialMode = mode; } + + float getBlendFactor() const { return _blendFactor; } void setBlendFactor(float blendFactor) { _blendFactor = blendFactor; } - int getPriority() { return _priority; } + int getPriority() const { return _priority; } void setPriority(int priority) { _priority = priority; } - int getShapeID() { return _shapeID; } + int getShapeID() const { return _shapeID; } void setShapeID(int shapeID) { _shapeID = shapeID; } - const glm::vec4& getMaterialBounds() { return _materialBounds; } + glm::vec4 getMaterialBounds() const { return _materialBounds; } void setMaterialBounds(const glm::vec4& materialBounds) { _materialBounds = materialBounds; } -protected: + std::shared_ptr getMaterial() const; + + void setUserData(const QString& userData) override; + +private: QString _materialURL; MaterialMode _materialMode { UV }; float _blendFactor { 1.0f }; @@ -72,7 +81,12 @@ protected: int _shapeID { 0 }; glm::vec4 _materialBounds { 0, 0, 1, 1 }; - //NetworkMaterial _material; + QHash> _materials; + std::vector _materialNames; + QString _currentMaterialName; + + void parseJSONMaterial(const QJsonObject& materialJSON); + static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB); }; diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index ea5bd331c9..6cc6b8472f 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -221,4 +221,4 @@ bool Material::calculateMaterialInfo() const { _hasCalculatedTextureInfo = allTextures; } return _hasCalculatedTextureInfo; -} +} \ No newline at end of file diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 07f7283bfa..1fe3648838 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -425,7 +425,7 @@ bool Geometry::areTexturesLoaded() const { return true; } -const std::shared_ptr Geometry::getShapeMaterial(int partID) const { +const std::shared_ptr Geometry::getShapeMaterial(int partID) const { if ((partID >= 0) && (partID < (int)_meshParts->size())) { int materialID = _meshParts->at(partID)->materialID; if ((materialID >= 0) && (materialID < (int)_materials.size())) { @@ -491,6 +491,15 @@ void GeometryResourceWatcher::resourceRefreshed() { // _instance.reset(); } +NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) : + Material(m), + _textures(m._textures), + _albedoTransform(m._albedoTransform), + _lightmapParams(m._lightmapParams), + _lightmapTransform(m._lightmapTransform), + _isOriginal(m._isOriginal) +{} + const QString NetworkMaterial::NO_TEXTURE = QString(); const QString& NetworkMaterial::getTextureName(MapChannel channel) { @@ -532,19 +541,85 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl } graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) { - const auto texture = DependencyManager::get()->getTexture(url, type); - _textures[channel].texture = texture; + auto textureCache = DependencyManager::get(); + if (textureCache) { + auto texture = textureCache->getTexture(url, type); + _textures[channel].texture = texture; - auto map = std::make_shared(); - map->setTextureSource(texture->_textureSource); + auto map = std::make_shared(); + if (texture) { + map->setTextureSource(texture->_textureSource); + emit textureFinished(); + } - return map; + return map; + } + return nullptr; +} + +void NetworkMaterial::setAlbedoMap(const QString& url, bool useAlphaChannel) { + auto map = fetchTextureMap(QUrl(url), image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); + if (map) { + map->setUseAlphaChannel(useAlphaChannel); + setTextureMap(MapChannel::ALBEDO_MAP, map); + } +} + +void NetworkMaterial::setNormalMap(const QString& url, bool isBumpmap) { + auto map = fetchTextureMap(QUrl(url), isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); + if (map) { + setTextureMap(MapChannel::NORMAL_MAP, map); + } +} + +void NetworkMaterial::setRoughnessMap(const QString& url, bool isGloss) { + auto map = fetchTextureMap(QUrl(url), isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); + if (map) { + setTextureMap(MapChannel::ROUGHNESS_MAP, map); + } +} + +void NetworkMaterial::setMetallicMap(const QString& url, bool isSpecular) { + auto map = fetchTextureMap(QUrl(url), isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); + if (map) { + setTextureMap(MapChannel::METALLIC_MAP, map); + } +} + +void NetworkMaterial::setOcclusionMap(const QString& url) { + auto map = fetchTextureMap(QUrl(url), image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); + if (map) { + setTextureMap(MapChannel::OCCLUSION_MAP, map); + } +} + +void NetworkMaterial::setEmissiveMap(const QString& url) { + auto map = fetchTextureMap(QUrl(url), image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + if (map) { + setTextureMap(MapChannel::EMISSIVE_MAP, map); + } +} + +void NetworkMaterial::setScatteringMap(const QString& url) { + auto map = fetchTextureMap(QUrl(url), image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); + if (map) { + setTextureMap(MapChannel::SCATTERING_MAP, map); + } +} + +void NetworkMaterial::setLightmapMap(const QString& url) { + auto map = fetchTextureMap(QUrl(url), image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); + if (map) { + //map->setTextureTransform(_lightmapTransform); + //map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); + setTextureMap(MapChannel::LIGHTMAP_MAP, map); + } } NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : - graphics::Material(*material._material) + graphics::Material(*material._material), + _textures(MapChannel::NUM_MAP_CHANNELS) { - _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); _albedoTransform = material.albedoTexture.transform; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index f650b3f2eb..575f94f9bf 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -48,7 +48,7 @@ public: const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; } const GeometryMeshes& getMeshes() const { return *_meshes; } - const std::shared_ptr getShapeMaterial(int shapeID) const; + const std::shared_ptr getShapeMaterial(int shapeID) const; const QVariantMap getTextures() const; void setTextures(const QVariantMap& textureMap); @@ -131,7 +131,6 @@ private: Geometry::Pointer& _geometryRef; }; - /// Stores cached model geometries. class ModelCache : public ResourceCache, public Dependency { Q_OBJECT @@ -157,11 +156,26 @@ private: virtual ~ModelCache() = default; }; -class NetworkMaterial : public graphics::Material { +class NetworkMaterial : public QObject, public graphics::Material { + Q_OBJECT public: using MapChannel = graphics::Material::MapChannel; + NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {} NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); + NetworkMaterial(const NetworkMaterial& material); + + void setAlbedoMap(const QString& url, bool useAlphaChannel); + void setNormalMap(const QString& url, bool isBumpmap); + void setRoughnessMap(const QString& url, bool isGloss); + void setMetallicMap(const QString& url, bool isSpecular); + void setOcclusionMap(const QString& url); + void setEmissiveMap(const QString& url); + void setScatteringMap(const QString& url); + void setLightmapMap(const QString& url); + +signals: + void textureFinished(); protected: friend class Geometry; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8153dba3a5..3402758ef1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -275,6 +275,8 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh return QSharedPointer(texture, &Resource::deleter); } +int networkTexturePointerMetaTypeId = qRegisterMetaType>(); + NetworkTexture::NetworkTexture(const QUrl& url) : Resource(url), _type(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 742d003d02..6a88eb641a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -163,7 +163,6 @@ public: NetworkTexturePointer getTexture(const QUrl& url, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE, const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); - gpu::TexturePointer getTextureByHash(const std::string& hash); gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index a3ac995bcf..7ba7cca96d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -340,14 +340,6 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& return resource; } - if (QThread::currentThread() != thread()) { - qCDebug(networking) << "Fetching asynchronously:" << url; - QMetaObject::invokeMethod(this, "getResource", - Q_ARG(QUrl, url), Q_ARG(QUrl, fallback)); - // Cannot use extra parameter as it might be freed before the invocation - return QSharedPointer(); - } - if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { return getResource(fallback, QUrl()); } @@ -358,6 +350,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& extra); resource->setSelf(resource); resource->setCache(this); + resource->moveToThread(qApp->thread()); connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); { QWriteLocker locker(&_resourcesLock); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 711aeb2cc2..98ad1ab323 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::SoftEntities); + return static_cast(EntityVersion::MaterialEntities); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::RemovedJurisdictions); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 458666571c..e13809810c 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -206,7 +206,8 @@ enum class EntityVersion : PacketVersion { OwnershipChallengeFix, ZoneLightInheritModes = 82, ZoneStageRemoved, - SoftEntities + SoftEntities, + MaterialEntities }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index ef4e98798f..7f8eb49101 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -33,6 +33,8 @@ #include #include +#include "MaterialMode.h" + #include "OctreeConstants.h" #include "OctreeElement.h" @@ -249,6 +251,7 @@ public: 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::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); } @@ -257,6 +260,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, MaterialMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result); static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& result); diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 3d213840dd..41a5bf5faf 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -38,7 +38,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform _cauterizedTransform = renderTransform; } -void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { +void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization; if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { @@ -46,7 +46,7 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S } batch.setModelTransform(_cauterizedTransform); } else { - ModelMeshPartPayload::bindTransform(batch, locations, renderMode); + ModelMeshPartPayload::bindTransform(batch, renderMode); } } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 2337632047..3c0f90fcb5 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -25,7 +25,7 @@ public: void updateTransformForCauterizedMesh(const Transform& renderTransform); - void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; + void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override; void setEnableCauterization(bool enableCauterization) { _enableCauterization = enableCauterization; } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 1fd6ca2980..0d76b2c093 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1318,7 +1318,6 @@ void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, in renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id); } - void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c506887fc4..dd5809093e 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -125,126 +125,7 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputStream(0, _drawMesh->getVertexStream()); } -void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool enableTextures) const { - if (!_drawMaterial) { - return; - } - - auto textureCache = DependencyManager::get(); - - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::MATERIAL, _drawMaterial->getSchemaBuffer()); - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::TEXMAPARRAY, _drawMaterial->getTexMapArrayBuffer()); - - const auto& materialKey = _drawMaterial->getKey(); - const auto& textureMaps = _drawMaterial->getTextureMaps(); - - int numUnlit = 0; - if (materialKey.isUnlit()) { - numUnlit++; - } - - if (!enableTextures) { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); - return; - } - - // Albedo - if (materialKey.isAlbedoMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView()); - } else { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getGrayTexture()); - } - } - - // Roughness map - if (materialKey.isRoughnessMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); - } - } - - // Normal map - if (materialKey.isNormalMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); - } - } - - // Metallic map - if (materialKey.isMetallicMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); - } - } - - // Occlusion map - if (materialKey.isOcclusionMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); - } - } - - // Scattering map - if (materialKey.isScatteringMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); - } - } - - // Emissive / Lightmap - if (materialKey.isLightmapMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); - - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture()); - } - } else if (materialKey.isEmissiveMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); - - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); - } - } -} - -void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { +void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { batch.setModelTransform(_drawTransform); } @@ -252,23 +133,21 @@ void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::Loca void MeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("MeshPartPayload::render"); + if (!args) { + return; + } + gpu::Batch& batch = *(args->_batch); - auto locations = args->_shapePipeline->locations; - assert(locations); - // Bind the model transform and the skinCLusterMatrices if needed - bindTransform(batch, locations, args->_renderMode); + bindTransform(batch, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); // apply material properties - bindMaterial(batch, locations, args->_enableTexturing); - - if (args) { - args->_details._materialSwitches++; - } + args->_shapePipeline->bindMaterial(_drawMaterial, batch, args->_enableTexturing); + args->_details._materialSwitches++; // Draw! { @@ -276,10 +155,8 @@ void MeshPartPayload::render(RenderArgs* args) { drawCall(batch); } - if (args) { - const int INDICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; - } + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } namespace render { @@ -500,7 +377,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { } } -void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { if (_clusterBuffer) { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer); } @@ -515,17 +392,14 @@ void ModelMeshPartPayload::render(RenderArgs* args) { } gpu::Batch& batch = *(args->_batch); - auto locations = args->_shapePipeline->locations; - assert(locations); - bindTransform(batch, locations, args->_renderMode); + bindTransform(batch, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); // apply material properties - bindMaterial(batch, locations, args->_enableTexturing); - + args->_shapePipeline->bindMaterial(_drawMaterial, batch, args->_enableTexturing); args->_details._materialSwitches++; // Draw! diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 8160b9f009..7ec02ae7f0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -17,7 +17,6 @@ #include #include -#include #include @@ -49,8 +48,7 @@ public: // ModelMeshPartPayload functions to perform render void drawCall(gpu::Batch& batch) const; virtual void bindMesh(gpu::Batch& batch); - virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool enableTextures) const; - virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; + virtual void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const; // Payload resource cached values Transform _drawTransform; @@ -63,7 +61,7 @@ public: mutable graphics::Box _worldBound; std::shared_ptr _drawMesh; - std::shared_ptr _drawMaterial; + std::shared_ptr _drawMaterial; graphics::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } @@ -109,7 +107,7 @@ public: // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) override; - void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; + void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override; void computeAdjustedLocalBound(const std::vector& clusterTransforms); diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 1d88c3e5f5..4f8673cde6 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -2,7 +2,9 @@ set(TARGET_NAME render) AUTOSCRIBE_SHADER_LIB(gpu graphics) setup_hifi_library() -# render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared ktx gpu graphics octree) +# render needs octree only for getAccuracyAngle(float, int), and model-networking for TextureCache +link_hifi_libraries(shared ktx gpu graphics octree model-networking) +include_hifi_library_headers(networking) +include_hifi_library_headers(image) target_nsight() diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 4254280fa1..db4ff604ca 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -15,6 +15,8 @@ #include +#include + using namespace render; ShapePipeline::CustomFactoryMap ShapePipeline::_globalCustomFactoryMap; @@ -157,3 +159,122 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke return shapePipeline; } + +void ShapePipeline::bindMaterial(graphics::MaterialPointer material, gpu::Batch& batch, bool enableTextures) const { + if (!material) { + return; + } + + auto textureCache = DependencyManager::get(); + + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::MATERIAL, material->getSchemaBuffer()); + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::TEXMAPARRAY, material->getTexMapArrayBuffer()); + + const auto& materialKey = material->getKey(); + const auto& textureMaps = material->getTextureMaps(); + + int numUnlit = 0; + if (materialKey.isUnlit()) { + numUnlit++; + } + + if (!enableTextures) { + batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture()); + batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); + batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); + batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); + batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); + batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); + batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); + return; + } + + // Albedo + if (materialKey.isAlbedoMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView()); + } else { + batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getGrayTexture()); + } + } + + // Roughness map + if (materialKey.isRoughnessMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, itr->second->getTextureView()); + + // texcoord are assumed to be the same has albedo + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); + } + } + + // Normal map + if (materialKey.isNormalMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, itr->second->getTextureView()); + + // texcoord are assumed to be the same has albedo + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); + } + } + + // Metallic map + if (materialKey.isMetallicMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, itr->second->getTextureView()); + + // texcoord are assumed to be the same has albedo + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); + } + } + + // Occlusion map + if (materialKey.isOcclusionMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, itr->second->getTextureView()); + + // texcoord are assumed to be the same has albedo + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); + } + } + + // Scattering map + if (materialKey.isScatteringMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, itr->second->getTextureView()); + + // texcoord are assumed to be the same has albedo + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); + } + } + + // Emissive / Lightmap + if (materialKey.isLightmapMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); + + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture()); + } + } else if (materialKey.isEmissiveMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + + if (itr != textureMaps.end() && itr->second->isDefined()) { + batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); + } else { + batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); + } + } +} diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index be77e2f95e..3e358e9ac2 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -18,6 +18,8 @@ #include "Args.h" +#include + namespace render { class Item; class ShapePlumber; @@ -294,6 +296,8 @@ public: void prepareShapeItem(Args* args, const ShapeKey& key, const Item& shape); + void bindMaterial(graphics::MaterialPointer material, gpu::Batch& batch, bool enableTextures) const; + protected: friend class ShapePlumber; diff --git a/libraries/shared/src/MaterialMode.cpp b/libraries/shared/src/MaterialMode.cpp new file mode 100644 index 0000000000..cb656a7bf7 --- /dev/null +++ b/libraries/shared/src/MaterialMode.cpp @@ -0,0 +1,24 @@ +// +// Created by Sam Gondelman on 1/17/2018 +// Copyright 2018 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 "MaterialMode.h" + +const char* materialModeNames[] = { + "uv", + "projected" +}; + +static const size_t MATERIAL_MODE_NAMES = (sizeof(materialModeNames) / sizeof((materialModeNames)[0])); + +QString MaterialModeHelpers::getNameForMaterialMode(MaterialMode mode) { + if (((int)mode <= 0) || ((int)mode >= (int)MATERIAL_MODE_NAMES)) { + mode = (MaterialMode)0; + } + + return materialModeNames[(int)mode]; +} \ No newline at end of file diff --git a/libraries/shared/src/MaterialMode.h b/libraries/shared/src/MaterialMode.h index 05c6d295a5..a933693f75 100644 --- a/libraries/shared/src/MaterialMode.h +++ b/libraries/shared/src/MaterialMode.h @@ -9,10 +9,17 @@ #ifndef hifi_MaterialMode_h #define hifi_MaterialMode_h +#include "QString" + enum MaterialMode { UV = 0, PROJECTED }; +class MaterialModeHelpers { +public: + static QString getNameForMaterialMode(MaterialMode mode); +}; + #endif // hifi_MaterialMode_h diff --git a/scripts/developer/tests/toolbarTest.js b/scripts/developer/tests/toolbarTest.js index e21fbd8e19..dfc97ebe39 100644 --- a/scripts/developer/tests/toolbarTest.js +++ b/scripts/developer/tests/toolbarTest.js @@ -11,7 +11,8 @@ var toolBar = (function() { newTextButton, newWebButton, newZoneButton, - newParticleButton + newParticleButton, + newMaterialButton var toolIconUrl = Script.resolvePath("../../system/assets/images/tools/"); @@ -89,6 +90,13 @@ var toolBar = (function() { visible: false }); + newMaterialButton = toolBar.addButton({ + objectName: "newMaterialButton", + imageURL: toolIconUrl + "model-01.svg", + alpha: 0.9, + visible: false + }); + that.setActive(false); newModelButton.clicked(); } @@ -109,8 +117,8 @@ var toolBar = (function() { newTextButton.writeProperty('visible', doShow); newWebButton.writeProperty('visible', doShow); newZoneButton.writeProperty('visible', doShow); - newModelButton.writeProperty('visible', doShow); newParticleButton.writeProperty('visible', doShow); + newMaterialButton.writeProperty('visible', doShow); }; initialize(); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 87cd3e0faf..928d8ba1fa 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -251,7 +251,7 @@ var toolBar = (function () { // Align entity with Avatar orientation. properties.rotation = MyAvatar.orientation; - var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"]; + var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web", "Material"]; if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box prior to creating it. @@ -354,6 +354,9 @@ var toolBar = (function () { var SHAPE_TYPE_SPHERE = 5; var DYNAMIC_DEFAULT = false; + var MATERIAL_MODE_UV = 0; + var MATERIAL_MODE_PROJECTED = 1; + function handleNewModelDialogResult(result) { if (result) { var url = result.textInput; @@ -394,6 +397,28 @@ var toolBar = (function () { } } + function handleNewMaterialDialogResult(result) { + if (result) { + var json = result.textInput; + var materialMode; + switch (result.comboBox) { + case MATERIAL_MODE_PROJECTED: + materialMode = "projected"; + break; + default: + shapeType = "uv"; + } + + if (json) { + createNewEntity({ + type: "Material", + materialURL: json, + materialMode: materialMode + }); + } + } + } + function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.popFromStack(); @@ -404,6 +429,9 @@ var toolBar = (function () { case "newEntityButtonClicked": buttonHandlers[message.params.buttonName](); break; + case "newMaterialDialogAdd": + handleNewMaterialDialogResult(message.params); + break; } } @@ -614,6 +642,17 @@ 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; + + // tablet version of new material dialog + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + tablet.pushOntoStack("NewMaterialDialog.qml"); + }); + that.setActive(false); } @@ -1983,7 +2022,7 @@ var PropertiesTool = function (opts) { ); } Entities.editEntity(selectionManager.selections[0], data.properties); - if (data.properties.name !== undefined || data.properties.modelURL !== undefined || + if (data.properties.name !== undefined || data.properties.modelURL !== undefined || data.properties.materialURL !== undefined || data.properties.visible !== undefined || data.properties.locked !== undefined) { entityListTool.sendUpdate(); } @@ -1994,7 +2033,7 @@ var PropertiesTool = function (opts) { parentSelectedEntities(); } else if (data.type === 'unparent') { unparentSelectedEntities(); - } else if (data.type === 'saveUserData'){ + } else if (data.type === 'saveUserData') { //the event bridge and json parsing handle our avatar id string differently. var actualID = data.id.split('"')[1]; Entities.editEntity(actualID, data.properties); diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 736d42d593..96084f355f 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -448,7 +448,7 @@ input[type=checkbox]:checked + label:hover { border: 1.5pt solid black; } -.shape-section, .light-section, .model-section, .web-section, .hyperlink-section, .text-section, .zone-section { +.shape-section, .light-section, .model-section, .web-section, .hyperlink-section, .text-section, .zone-section, .material-section { display: table; } @@ -570,6 +570,7 @@ hr { .physical-group[collapsed="true"] ~ .physical-group, .behavior-group[collapsed="true"] ~ .behavior-group, .model-group[collapsed="true"] ~ .model-group, +.material-group[collapsed="true"] ~ .material-group, .light-group[collapsed="true"] ~ .light-group { display: none !important; } @@ -1386,7 +1387,7 @@ input#reset-to-natural-dimensions { } -#static-userdata{ +#static-userdata { display: none; z-index: 99; position: absolute; @@ -1397,7 +1398,7 @@ input#reset-to-natural-dimensions { background-color: #2e2e2e; } -#userdata-saved{ +#userdata-saved { margin-top:5px; font-size:16px; display:none; @@ -1454,6 +1455,9 @@ input#reset-to-natural-dimensions { order: 6; } +#properties-list.ShapeMenu #material, +#properties-list.BoxMenu #material, +#properties-list.SphereMenu #material, #properties-list.ShapeMenu #light, #properties-list.BoxMenu #light, #properties-list.SphereMenu #light, @@ -1490,6 +1494,7 @@ input#reset-to-natural-dimensions { } /* items to hide */ +#properties-list.ParticleEffectMenu #material, #properties-list.ParticleEffectMenu #base-color-section, #properties-list.ParticleEffectMenu #hyperlink, #properties-list.ParticleEffectMenu #light, @@ -1524,6 +1529,7 @@ input#reset-to-natural-dimensions { order: 7; } /* sections to hide */ +#properties-list.LightMenu #material, #properties-list.LightMenu #model, #properties-list.LightMenu #zone, #properties-list.LightMenu #text, @@ -1560,6 +1566,7 @@ input#reset-to-natural-dimensions { order: 7; } /* sections to hide */ +#properties-list.ModelMenu #material, #properties-list.ModelMenu #light, #properties-list.ModelMenu #zone, #properties-list.ModelMenu #text, @@ -1596,6 +1603,7 @@ input#reset-to-natural-dimensions { order: 7; } /* sections to hide */ +#properties-list.ZoneMenu #material, #properties-list.ZoneMenu #light, #properties-list.ZoneMenu #model, #properties-list.ZoneMenu #text, @@ -1632,6 +1640,7 @@ input#reset-to-natural-dimensions { order: 7; } /* sections to hide */ +#properties-list.WebMenu #material, #properties-list.WebMenu #light, #properties-list.WebMenu #model, #properties-list.WebMenu #zone, @@ -1669,6 +1678,7 @@ input#reset-to-natural-dimensions { order: 7; } /* sections to hide */ +#properties-list.TextMenu #material, #properties-list.TextMenu #light, #properties-list.TextMenu #model, #properties-list.TextMenu #zone, @@ -1681,6 +1691,39 @@ input#reset-to-natural-dimensions { display: none } +/* ----- Order of Menu items for Material ----- */ +#properties-list.MaterialMenu #general { + order: 1; +} +#properties-list.MaterialMenu #material { + order: 2; +} +#properties-list.MaterialMenu #spatial { + order: 3; +} +#properties-list.MaterialMenu #hyperlink { + order: 4; +} +#properties-list.MaterialMenu #behavior { + order: 5; +} + +/* sections to hide */ +#properties-list.MaterialMenu #physical, +#properties-list.MaterialMenu #collision-info, +#properties-list.MaterialMenu #model, +#properties-list.MaterialMenu #light, +#properties-list.MaterialMenu #zone, +#properties-list.MaterialMenu #text, +#properties-list.MaterialMenu #web { + display: none; +} +/* items to hide */ +#properties-list.MaterialMenu #shape-list, +#properties-list.MaterialMenu #base-color-section { + display: none +} + /* Currently always hidden */ #properties-list #polyvox { diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index b93974ee77..93d22180c9 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -761,6 +761,51 @@ +
+ + MaterialM + +
+
+ + +
+
+
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+
+ +
+
+ diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 72092b66ca..af9d106149 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -24,7 +24,8 @@ var ICON_FOR_TYPE = { Zone: "o", PolyVox: "", Multiple: "", - PolyLine: "" + PolyLine: "", + Material: "" }; var EDITOR_TIMEOUT_DURATION = 1500; @@ -77,7 +78,6 @@ function disableProperties() { if ($('#userdata-editor').css('display') === "block" && elLocked.checked === true) { showStaticUserData(); } - } function showElements(els, show) { @@ -192,6 +192,19 @@ 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 = {}; @@ -473,7 +486,6 @@ function bindAllNonJSONEditorElements() { } else { if ($('#userdata-editor').css('height') !== "0px") { saveJSONUserData(true); - } } }); @@ -621,6 +633,16 @@ function loaded() { var elModelTextures = document.getElementById("property-model-textures"); var elModelOriginalTextures = document.getElementById("property-model-original-textures"); + var elMaterialURL = document.getElementById("property-material-url"); + var elMaterialMode = document.getElementById("property-material-mode"); + 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 elWebSourceURL = document.getElementById("property-web-source-url"); var elWebDPI = document.getElementById("property-web-dpi"); @@ -1105,6 +1127,17 @@ function loaded() { elXTextureURL.value = properties.xTextureURL; elYTextureURL.value = properties.yTextureURL; elZTextureURL.value = properties.zTextureURL; + } else if (properties.type === "Material") { + elMaterialURL.value = properties.materialURL; + elMaterialMode.value = properties.materialMode; + setDropdownText(elMaterialMode); + 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); } if (properties.locked) { @@ -1375,6 +1408,19 @@ function loaded() { elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); + 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')); + + var materialBoundsChangeFunction = createEmitVec4PropertyUpdateFunction('materialBounds', + elMaterialBoundsX, elMaterialBoundsY, elMaterialBoundsZ, elMaterialBoundsW); + elMaterialBoundsX.addEventListener('change', materialBoundsChangeFunction); + elMaterialBoundsY.addEventListener('change', materialBoundsChangeFunction); + elMaterialBoundsZ.addEventListener('change', materialBoundsChangeFunction); + elMaterialBoundsW.addEventListener('change', materialBoundsChangeFunction); + elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text')); elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera')); elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight')); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 9d9689000e..d53766ab4e 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -77,18 +77,24 @@ EntityListTool = function(opts) { var properties = Entities.getEntityProperties(id); if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) { + var url = ""; + if (properties.type == "Model") { + url = properties.modelURL; + } else if (properties.type == "Material") { + url = properties.materialURL; + } entities.push({ id: id, name: properties.name, type: properties.type, - url: properties.type == "Model" ? properties.modelURL : "", + url: url, locked: properties.locked, visible: properties.visible, verticesCount: valueIfDefined(properties.renderInfo.verticesCount), texturesCount: valueIfDefined(properties.renderInfo.texturesCount), texturesSize: valueIfDefined(properties.renderInfo.texturesSize), hasTransparent: valueIfDefined(properties.renderInfo.hasTransparent), - isBaked: properties.type == "Model" ? properties.modelURL.toLowerCase().endsWith(".baked.fbx") : false, + isBaked: properties.type == "Model" ? url.toLowerCase().endsWith(".baked.fbx") : false, drawCalls: valueIfDefined(properties.renderInfo.drawCalls), hasScript: properties.script !== "" });